[
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n  \"image\": \"mcr.microsoft.com/devcontainers/java:21-bookworm\",\n  \"features\": {\n      \"ghcr.io/devcontainers/features/java:1\": {\n          \"version\": \"17\"\n      }\n  },\n  \"customizations\": {\n      \"vscode\": {\n          \"settings\": {\n              \"java.server.launchMode\": \"Standard\"\n          },\n          \"extensions\": [\n              \"vscjava.vscode-java-pack\",\n              \"vscjava.vscode-gradle\"\n          ]\n      }\n  }\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_size = 2\nij_continuation_indent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.{kt, kts}]\nij_kotlin_imports_layout = *\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n\n*.bat text eol=crlf\n*.jar binary"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "Contributing\n============\n\nIf you would like to contribute code to OkHttp you can do so through GitHub by\nforking the repository and sending a pull request.\n\nWhen submitting code, please make every effort to follow existing conventions\nand style in order to keep the code as readable as possible. Please also make\nsure your code compiles by running `./gradlew check`. Checkstyle failures\nduring compilation indicate errors in your style and can be viewed in the\n`checkstyle-result.xml` file.\n\nSome general advice\n\n- Don’t change public API lightly, avoid if possible, and include your reasoning in the PR if essential.  It causes pain for developers who use OkHttp and sometimes runtime errors.\n- Favour a working external library if appropriate.  There are many examples of OkHttp libraries that can sit on top or hook in via existing APIs.\n- Get working code on a personal branch with tests before you submit a PR.\n- OkHttp is a small and light dependency.  Don't introduce new dependencies or major new functionality.\n- OkHttp targets the intersection of RFC correct *and* widely implemented.  Incorrect implementations that are very widely implemented e.g. a bug in Apache, Nginx, Google, Firefox should also be handled.\n\nBefore your code can be accepted into the project you must also sign the\n[Individual Contributor License Agreement (CLA)][1].\n\n\n [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: A reproducible problem\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\nGood bug reports include a failing test! Writing a test helps you to isolate and describe the problem, and it helps us to fix it fast. Bug reports without a failing test or reproduction steps are likely to be closed.\n\nHere’s an example test to get you started.\nhttps://gist.github.com/swankjesse/981fcae102f513eb13ed\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\nStart by telling us what problem you’re trying to solve. Often a solution already exists!\n\nDon’t send pull requests to implement new features without first getting our support. Sometimes we leave features out on purpose to keep the project small.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: Question\nabout: Use Stack Overflow instead\ntitle: \"\\U0001F649\"\nlabels: ''\nassignees: ''\n\n---\n\n🛑 𝙎𝙏𝙊𝙋\n\nThis issue tracker is not the place for questions!\n\nIf you want to ask how to do something, or to understand why something isn't working the way you expect it to, use Stack Overflow. https://stackoverflow.com/questions/tagged/okhttp\n\nWe close all questions without reading them.\n"
  },
  {
    "path": ".github/renovate.json",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:recommended\",\n    \"group:monorepos\",\n    \"group:recommended\",\n    \":dependencyDashboard\"\n  ],\n  \"semanticCommits\": \"disabled\",\n  \"labels\": [\n    \"renovate\"\n  ],\n  \"ignoreDeps\": [\n    \"com.squareup.okhttp3:okhttp\",\n    \"com.squareup.okhttp3:okhttp-tls\",\n    \"com.squareup.okhttp3:mockwebserver\"\n  ],\n  \"packageRules\": [\n    {\n      \"groupName\": \"bnd\",\n      \"matchPackageNames\": [\n        \"/biz.*/\"\n      ]\n    },\n    {\n      \"groupName\": \"graalvm\",\n      \"matchPackageNames\": [\n        \"/org.graalvm.*/\"\n      ]\n    },\n    {\n      \"matchPackageNames\": [\n        \"org.objenesis:objenesis\"\n      ],\n      \"allowedVersions\": \"<=2.6\"\n    },\n    {\n      \"extends\": [\n        \"monorepo:jetty\"\n      ],\n      \"allowedVersions\": \"<10.0\",\n      \"description\": \"JDK 11 requirement\"\n    },\n    {\n      \"extends\": [\n        \"monorepo:junit5\"\n      ],\n      \"allowedVersions\": \"<5.14.0\",\n    },\n    {\n      \"matchPackageNames\": [\n        \"org.junit-pioneer:junit-pioneer\"\n      ],\n      \"allowedVersions\": \"<2.0.0\",\n      \"description\": \"JDK 11 requirement\"\n    },\n    {\n      \"matchPackageNames\": [\n        \"androidx.activity:activity-ktx\"\n      ],\n      \"allowedVersions\": \"<=1.11.0\",\n      \"description\": \"Android minSdk 23 requirement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: build\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    types: [opened, labeled, unlabeled, synchronize]\n\npermissions:\n  contents: read\n\nenv:\n  GRADLE_OPTS: \"-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false\"\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    if: github.repository == 'square/okhttp' && github.ref == 'refs/heads/master'\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 21\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Upload Artifacts\n        run: ./gradlew clean publish --stacktrace\n        env:\n          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_CENTRAL_USERNAME }}\n          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_CENTRAL_PASSWORD }}\n          ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SECRET_KEY }}\n          ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SECRET_PASSPHRASE }}\n\n  validation:\n    name: \"Validation\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: gradle/actions/wrapper-validation@v5\n      - name: Validate Renovate\n        uses: rinchsan/renovate-config-validator@v0.2.0\n        with:\n          pattern: '.github/renovate.json'\n\n  checks:\n    permissions:\n      checks: write # for mikepenz/action-junit-report\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: |\n            11\n            21\n\n      - uses: graalvm/setup-graalvm@v1\n        with:\n          distribution: 'graalvm'\n          java-version: 21\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          native-image-job-reports: true\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Checks\n        run: ./gradlew check -PgraalBuild=true -x jvmTest -x test -x allTests -x java9Test\n\n  jvm:\n    permissions:\n      checks: write # for mikepenz/action-junit-report\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        java-version:\n          - 8\n          - 11\n          - 17\n          - 21\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: |\n            ${{ matrix.java-version }}\n            21\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Tests\n        run: ./gradlew test allTests -Ptest.java.version=${{ matrix.java-version }}\n\n      - name: Publish Test Report\n        if: github.repository == 'square/okhttp' && github.ref == 'refs/heads/master' && matrix.java-version == '11'\n        uses: mikepenz/action-junit-report@v6\n        with:\n          report_paths: '**/build/test-results/*/TEST-*.xml'\n          check_name: OpenJDK 11 Test Report\n\n      - name: Publish Test Results\n        uses: EnricoMi/publish-unit-test-result-action@v2\n        if: github.repository == 'square/okhttp' && github.ref == 'refs/heads/master' && matrix.java-version == '11'\n        with:\n          files: |\n            **/build/test-results/*/TEST-*.xml\n\n  openjdk8alpn:\n    runs-on: ubuntu-latest\n    if: github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'jdkversions')\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Install Old JDK 8\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'zulu'\n          java-version: 8.0.242\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 21\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Tests\n        run: ./gradlew test allTests -Ptest.java.version=8 -Pokhttp.platform=jdk8alpn -Palpn.boot.version=8.1.13.v20181017 -Dorg.gradle.java.installations.paths=/opt/hostedtoolcache/Java_Adopt_jdk/8.0.242-8.1/x64\n\n  providers:\n    runs-on: ubuntu-latest\n    if: github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'providers')\n    strategy:\n      matrix:\n        include:\n          - provider: openjsse\n            java-version: 8\n          - provider: bouncycastle\n            java-version: 21\n          - provider: corretto\n            java-version: 21\n          - provider: conscrypt\n            java-version: 21\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: |\n            ${{ matrix.java-version }}\n            21\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Tests\n        run: ./gradlew test allTests -Pokhttp.platform=${{ matrix.provider }} -Ptest.java.version=${{ matrix.java-version }}\n\n  openjdklatest:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDKs\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: |\n            11\n            17\n            24\n\n      - name: Allow incompatible JVM versions\n        run: |\n          echo 'kotlin.jvm.target.validation.mode=ignore' >> ./gradle.properties\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Tests\n        run: ./gradlew test allTests -Ptest.java.version=24\n\n  openjdkearlyaccess:\n    runs-on: ubuntu-latest\n    if: false # https://youtrack.jetbrains.com/issue/KTOR-8489\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: |\n            11\n            17\n            25-ea\n\n      - name: Allow incompatible JVM versions\n        run: |\n          echo 'kotlin.jvm.target.validation.mode=ignore' >> ./gradle.properties\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Tests\n        run: ./gradlew test allTests -Ptest.java.version=25\n\n  testwindows:\n    runs-on: windows-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 21\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Tests\n        run: ./gradlew test allTests\n\n  graal:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 21\n\n      - uses: graalvm/setup-graalvm@v1\n        with:\n          distribution: 'graalvm'\n          java-version: 24\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          native-image-job-reports: true\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Build okcurl\n        run: ./gradlew okcurl:nativeBuild\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run native-image tests\n        run: ./gradlew -PgraalBuild=true native-image-tests:nativeTest\n\n  android:\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n\n    strategy:\n      fail-fast: false\n      matrix:\n        api-level:\n          - 21\n          - 23\n          - 29\n          - 34\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 21\n\n      - name: Enable KVM group perms\n        # https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/\n        run: |\n          echo 'KERNEL==\"kvm\", GROUP=\"kvm\", MODE=\"0666\", OPTIONS+=\"static_node=kvm\"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules\n          sudo udevadm control --reload-rules\n          sudo udevadm trigger --name-match=kvm\n\n      - name: Verify KVM\n        run: |\n          sudo apt-get install -y cpu-checker\n          kvm-ok || echo \"KVM is not accelerated\"\n          kvm-ok || exit 1\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Gradle cache\n        run: ./gradlew :android-test:test\n\n      - name: AVD System Image Cache\n        uses: actions/cache@v5\n        id: avd-cache\n        with:\n          key: avd-${{ runner.os }}-${{ matrix.api-level }}-${{ matrix.api-level >= 30 && 'x86_64' || 'x86' }}\n          path: |\n            ~/.android/avd/*\n            ~/.android/adb*\n            # Added the actual system image path\n            ${{ env.ANDROID_HOME }}/system-images/android-${{ matrix.api-level }}\n\n      - name: Create AVD and generate snapshot for caching\n        if: steps.avd-cache.outputs.cache-hit != 'true'\n        uses: reactivecircus/android-emulator-runner@v2\n        with:\n          api-level: ${{ matrix.api-level }}\n          force-avd-creation: false\n          arch: ${{ matrix.api-level >= 30 && 'x86_64' || 'x86' }}\n          # No window, no audio, and use swiftshader for headless environments\n          emulator-options: >\n            -no-window\n            -gpu swiftshader_indirect\n            -noaudio\n            -no-boot-anim\n            -camera-back none\n            -memory 2048\n          disable-animations: true\n          script: echo \"Generated AVD snapshot for caching.\"\n\n      - name: Run Tests\n        uses: reactivecircus/android-emulator-runner@v2\n        with:\n          api-level: ${{ matrix.api-level }}\n          arch: ${{ matrix.api-level == '34' && 'x86_64' || 'x86' }}\n          script: ./gradlew -PandroidBuild=true connectedCheck\n        env:\n          API_LEVEL: ${{ matrix.api-level }}\n\n      - name: Build Release App\n        run: ./gradlew android-test-app:lint android-test-app:assembleRelease\n\n  loom:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: |\n            21\n            24\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Tests\n        run: ./gradlew test allTests -Pokhttp.platform=loom -Ptest.java.version=24 -PcontainerTests=true\n\n  maven:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 21\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Publish local snapshot\n        run: ./gradlew publishToMavenLocal\n\n      - name: Run maven test\n        working-directory: ./maven-tests\n        run: ./mvnw -q verify\n\n  java9_modules:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 24\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Test module-tests\n        run: ./gradlew module-tests:test -PokhttpModuleTests=true\n\n      - name: Run with Jlink\n        run: ./gradlew module-tests:imageRun -PokhttpModuleTests=true\n\n"
  },
  {
    "path": ".github/workflows/containers.yml",
    "content": "name: containers\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    types: [opened, labeled, unlabeled, synchronize]\n\npermissions:\n  contents: read\n\nenv:\n  GRADLE_OPTS: \"-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false\"\n\njobs:\n  test_containers:\n    permissions:\n      checks: write # for actions/upload-artifact\n    runs-on: ubuntu-latest\n    if: github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'containers')\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 21\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Run Container Tests\n        run: ./gradlew container-tests:test -PcontainerTests=true\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: docs\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    types: [opened, labeled, unlabeled, synchronize]\n\npermissions:\n  contents: read\n\nenv:\n  GRADLE_OPTS: \"-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false\"\n\njobs:\n  test_docs:\n    permissions:\n      checks: write # for actions/upload-artifact\n    runs-on: ubuntu-latest\n    if: github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'documentation')\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Configure JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version: 21\n\n      - uses: actions/setup-python@v6\n        with:\n          python-version: 3.x\n\n      - run: pip install mkdocs-material mkdocs-redirects\n\n      - name: Generate Docs\n        run: ./test_docs.sh\n\n      - uses: actions/upload-artifact@v7\n        with:\n          name: docs\n          path: site/\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: publish\n\non:\n  push:\n    tags:\n      - '**'\n\nenv:\n  GRADLE_OPTS: \"-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false\"\n\njobs:\n  publish:\n    runs-on: macos-15\n\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-java@v5\n        with:\n          distribution: 'temurin'\n          java-version-file: .github/workflows/.java-version\n\n      - run: ./gradlew publish\n        env:\n          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_CENTRAL_USERNAME }}\n          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_CENTRAL_PASSWORD }}\n          ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SECRET_KEY }}\n          ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SECRET_PASSPHRASE }}\n"
  },
  {
    "path": ".gitignore",
    "content": ".classpath\n.kotlin\n.project\n.settings\n.gradle\neclipsebin\n\nbin\ngen\nbuild\nout\nlib\ngenerated\n\ntarget\npom.xml.*\nrelease.properties\nlocal.properties\n\n.idea\n*.iml\n*.ipr\n*.iws\n*.log\nclasses\n\nobj\n\n.DS_Store\n\n# Special Mkdocs files\ndocs/5.x\ndocs/changelog.md\ndocs/contributing.md\ndocs/index.md\n\n# jenv\n/.java-version\n/site/\n/docs/changelogs/changelog.md\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"okhttp-hpacktests/src/test/resources/hpack-test-case\"]\n\tpath = okhttp-hpacktests/src/test/resources/hpack-test-case\n\turl = https://github.com/http2jp/hpack-test-case.git\n"
  },
  {
    "path": ".junit.run/Not Slow.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Not Slow\" type=\"JUnit\" factoryName=\"JUnit\" singleton=\"false\">\n    <useClassPathOnly />\n    <option name=\"MAIN_CLASS_NAME\" value=\"\" />\n    <option name=\"METHOD_NAME\" value=\"\" />\n    <option name=\"TEST_OBJECT\" value=\"tags\" />\n    <option name=\"VM_PARAMETERS\" value=\"-ea -Djunit.jupiter.extensions.autodetection.enabled=true\" />\n    <option name=\"PARAMETERS\" value=\"\" />\n    <option name=\"TEST_SEARCH_SCOPE\">\n      <value defaultName=\"wholeProject\" />\n    </option>\n    <tag value=\"!Slow &amp; !Slowish &amp; !Remote &amp; !Android\" />\n    <method v=\"2\">\n      <option name=\"Make\" enabled=\"true\" />\n    </method>\n  </configuration>\n</component>"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"java.configuration.updateBuildConfiguration\": \"interactive\",\n    \"java.import.gradle.wrapper.enabled\": true\n}"
  },
  {
    "path": "BUG-BOUNTY.md",
    "content": "Serious about security\n======================\n\nSquare recognizes the important contributions the security research community\ncan make. We therefore encourage reporting security issues with the code\ncontained in this repository.\n\nIf you believe you have discovered a security vulnerability, please follow the\nguidelines at https://bugcrowd.com/engagements/blockopensource.\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "Change Log\n==========\n\n## Version 5.3.2\n\n_2025-11-18_\n\n *  Fix: Don't delay triggering timeouts. In Okio 3.16.0 we introduced a regression that caused\n    timeouts to fire later than they were supposed to.\n\n *  Upgrade: [Okio 3.16.4][okio_3_16_4].\n\n\n## Version 5.3.1\n\n_2025-11-16_\n\nThis release is the same as 5.3.0. Okio 3.16.3 didn't have a necessary fix!\n\n*  Upgrade: [Okio 3.16.3][okio_3_16_3].\n\n\n## Version 5.3.0\n\n_2025-10-30_\n\n *  New: Add tags to `Call`, including computable tags. Use this to attach application-specific\n    metadata to a `Call` in an `EventListener` or `Interceptor`. The tag can be read in any other\n    `EventListener` or `Interceptor`.\n\n    ```kotlin\n      override fun intercept(chain: Interceptor.Chain): Response {\n        chain.call().tag(MyAnalyticsTag::class) {\n          MyAnalyticsTag(...)\n        }\n\n        return chain.proceed(chain.request())\n      }\n    ```\n\n *  New: Support request bodies on HTTP/1.1 connection upgrades.\n *  New: `EventListener.plus()` makes it easier to observe events in multiple listeners.\n *  Fix: Don't spam logs with _‘Method isLoggable in android.util.Log not mocked.’_ when using\n    OkHttp in Robolectric and Paparazzi tests.\n *  Upgrade: [Kotlin 2.2.21][kotlin_2_2_21].\n *  Upgrade: [Okio 3.16.2][okio_3_16_2].\n *  Upgrade: [ZSTD-KMP 0.4.0][zstd_kmp_0_4_0]. This update fixes a bug that caused APKs to fail\n    [16 KB ELF alignment checks][elf_alignment].\n\n\n## Version 5.2.3\n\n_2025-11-18_\n\n *  Fix: Don't delay triggering timeouts. In Okio 3.16.0 we introduced a regression that caused\n    timeouts to fire later than they were supposed to.\n\n *  Upgrade: [Okio 3.16.4][okio_3_16_4].\n\n\n## Version 5.2.2\n\n_2025-11-16_\n\nThis release is the same as 5.2.1. Okio 3.16.3 didn't have a necessary fix!\n\n *  Upgrade: [Okio 3.16.3][okio_3_16_3].\n\n\n## Version 5.2.1\n\n_2025-10-09_\n\n *  Fix: Don't crash when calling `Socket.shutdownOutput()` or `shutdownInput()` on an `SSLSocket`\n    on Android API 21 through 23. This method throws an `UnsupportedOperationException`, so we now\n    catch that and close the underlying stream instead.\n\n *  Upgrade: [Okio 3.16.1][okio_3_16_1].\n\n\n## Version 5.2.0\n\n_2025-10-07_\n\n *  New: Support [HTTP 101] responses with `Response.socket`. This mechanism is only supported on\n    HTTP/1.1. We also reimplemented our websocket client to use this new mechanism.\n\n *  New: The `okhttp-zstd` module negotiates [Zstandard (zstd)][zstd] compression with servers that\n    support it. It integrates a new (unstable) [ZSTD-KMP] library, also from Square. Enable it like\n    this:\n\n    ```kotlin\n    val client = OkHttpClient.Builder()\n      .addInterceptor(CompressionInterceptor(Zstd, Gzip))\n      .build()\n    ```\n\n *  New: Support the `QUERY` HTTP method. You will need to set the `Request.cacheUrlOverride`\n    property to cache calls made with this method. The `RequestBody.sha256()` may be helpful here;\n    use it to compose a cache URL from the query body.\n\n *  New: Publish events when calls must wait to execute. `EventListener.dispatcherQueueStart()`\n    is invoked when a call starts waiting, and `dispatcherQueueEnd()` is invoked when it's done.\n\n *  New: `Request.toCurl()` returns a copy-pasteable [curl] command consistent with Chrome’s and\n    Firefox’s ‘copy as cURL’ features.\n\n *  New: Support [JPMS]. We replaced our `Automatic-Module-Name` metadata with proper\n    `module-info.java` files.\n\n *  Fix: Recover gracefully when worker threads are interrupted. When we introduced fast fallback in\n    OkHttp 5.0, we started using background threads while connecting. Sadly that code didn't handle\n    interruptions well. This is now fixed.\n\n *  Upgrade: [Kotlin 2.2.20][kotlin_2_2_20].\n *  Upgrade: [Okio 3.16.0][okio_3_16_0].\n\n\n## Version 5.1.0\n\n_2025-07-07_\n\n *  New: `Response.peekTrailers()`. When we changed `Response.trailers()` to block instead of\n    throwing in 5.0.0, we inadvertently removed the ability for callers to peek the trailers\n    (by catching the `IllegalStateException` if they weren't available). This new API restores that\n    capability.\n\n *  Fix: Don't crash on `trailers()` if the response doesn't have a body. We broke [Retrofit] users\n    who read the trailers on the `raw()` OkHttp response, after its body was decoded.\n\n\n## Version 5.0.0\n\n_2025-07-02_\n\nThis is our first stable release of OkHttp since 2023. Here's the highlights if you're upgrading\nfrom OkHttp 4.x:\n\n**OkHttp is now packaged as separate JVM and Android artifacts.** This allows us to offer\nplatform-specific features and optimizations. If your build system handles [Gradle module metadata],\nthis change should be automatic.\n\n**MockWebServer has a new coordinate and package name.** We didn’t like that our old artifact\ndepends on JUnit 4 so the new one doesn’t. It also has a better API built on immutable values. (We\nintend to continue publishing the old `okhttp3.mockwebserver` artifact so there’s no urgency to\nmigrate.)\n\n| Coordinate                                       | Package Name          | Description                       |\n|:-------------------------------------------------| :-------------------- | :-------------------------------- |\n| com.squareup.okhttp3:mockwebserver3:5.0.0        | mockwebserver3        | Core module. No JUnit dependency! |\n| com.squareup.okhttp3:mockwebserver3-junit4:5.0.0 | mockwebserver3.junit4 | Optional JUnit 4 integration.     |\n| com.squareup.okhttp3:mockwebserver3-junit5:5.0.0 | mockwebserver3.junit5 | Optional JUnit 5 integration.     |\n| com.squareup.okhttp3:mockwebserver:5.0.0         | okhttp3.mockwebserver | Obsolete. Depends on JUnit 4.     |\n\n**OkHttp now supports Happy Eyeballs ([RFC 8305][rfc_8305]) for IPv4+IPv6 networks.** It attempts\nboth IPv6 and IPv4 connections concurrently, keeping whichever connects first.\n\n**We’ve improved our Kotlin APIs.** You can skip the builder:\n\n```kotlin\nval request = Request(\n  url = \"https://cash.app/\".toHttpUrl(),\n)\n```\n\n**OkHttp now supports [GraalVM].**\n\nHere’s what has changed since 5.0.0-alpha.17:\n\n *  Upgrade: [Okio 3.15.0][okio_3_15_0].\n *  Upgrade: [Kotlin 2.2.0][kotlin_2_2_0].\n *  Fix: Don't crash with a `NoSuchMethodError` when using OkHttp with the Sentry SDK.\n *  Fix: Retain the query data in the old `okhttp3.mockwebserver.RecordedRequest.path` property. We\n    inadvertently changed this behavior when we introduced the `mockwebserver3` API.\n\n\n## Version 5.0.0-alpha.17\n\n_2025-06-29_\n\nThis release stabilizes many APIs for the imminent OkHttp 5.0.0 release.\n\n *  New: `TrailersSource`, a public API for HTTP trailers. Production callers shouldn't need this\n    as the API to read response trailers is unchanged. Testers may use this new stable API to\n    supply trailers for a `Response`.\n\n *  New: `Path.asRequestBody()` is now a non-experimental API.\n\n *  New: `FileDescriptor.toRequestBody()` is now a non-experimental API.\n\n *  New: Stop using experimental coroutines APIs in our `okhttp-coroutines` artifact.\n\n *  Breaking: Move `gzip` from `RequestBody` to `Request.Builder`. This new API handles both\n    compressing the request body and also adding the corresponding `Content-Encoding` header. Note\n    that this function is sensitive to when it is called: the response body must be supplied before\n    it can be compressed.\n\n *  Breaking: Remove `AddressPolicy`, `AsyncDns`, and `ConnectionListener` from the public API. We\n    intend to ship a public API for these features, but we don't want to hold OkHttp 5.0.0 until\n    those APIs are stable.\n\n *  Fix: Change `MockWebServer.close()` to cancel ongoing calls that are blocked on a delay.\n\n *  Upgrade: [Okio 3.13.0][okio_3_13_0].\n\nThis release also stabilizes many APIs in the `mockwebserver3` artifact that's new in 5.0.\n\n *  Breaking: `RecordedRequest.body` is now nullable. Null is used when the request does not have a\n    body.\n\n *  Breaking: `RecordedRequest.chunkSizes` is now nullable. Null is used when the request does not\n    use chunked encoding. This is different from an empty list - that indicates the request is\n    chunked but has no data.\n\n *  Breaking: Replace `SocketPolicy` with a new type, `SocketEffect`. It splits triggers (request\n    start, response body, etc.) from effects (closing the socket, closing the stream, etc.).\n\n *  Breaking: Rename `RecordedRequest.sequenceNumber` to `exchangeIndex` and introduce\n    `connectionIndex` on that type. These properties may be useful when testing features like\n    connection reuse.\n\n *  Breaking: Replace our parameters-based JUnit 5 extension with a new annotation, `@StartStop`.\n    Put this annotation on a `MockWebServer` property and the extension will start it before your\n    test executes and stop it after it completes. No further configuration is required.\n\n    ```kotlin\n    @StartStop val server = MockWebServer()\n    ```\n\n *  Breaking: Don't automatically start `MockWebServer` after calls to accessors like `port`. Now\n    these accessors will throw an `IllegalStateException` if the service has not yet been started.\n\n *  Breaking: Rename `RecordedRequest.path` to `RecordedRequest.target`. (This property is\n    _sometimes_ a path, but it can also be a path and query, or a full URL.)\n\n *  Breaking: Decompose the `RecordedRequest.requestLine` into three properties, `method`, `target`,\n    and `version`. This better suits HTTP/2 where the request line had to be synthesized from\n    component headers.\n\n *  Breaking: Change `RecordedRequest.body` from a mutable `Buffer` to an immutable `ByteString`.\n\n *  Breaking: Adopt Okio's new `Socket` interface for `MockResponse.socketHandler`.\n\nNote that any _Breaking_ changes above impact only APIs introduced in earlier 5.0.0-alpha releasees.\nWe don't break binary compatibility with non-alpha APIs.\n\n\n## Version 5.0.0-alpha.16\n\n_2025-05-29_\n\n *  Fix: The previous release would crash when running on Robolectric. We didn't anticipate\n    running our Android artifact on the JVM platform!\n\n\n## Version 5.0.0-alpha.15\n\n_2025-05-28_\n\n**This release introduces separate JVM and Android artifacts.** Until now, we've distributed OkHttp\nas a JVM library that _detects_ Android capabilities at runtime, but that doesn't offer\nAndroid-specific APIs. With this release we're starting to publish OkHttp as an AAR for Android\nusers in addition to our existing JAR for JVM users.\n\nThis first Android-specific artifact adopts Android's `assets` mechanism to embed the public suffix\ndata. We will build more Android integration in future releases.\n\nThe okhttp-android artifact first introduced in `5.0.0-alpha.7` is no longer available:\n\n *  The `AndroidAsyncDns` class moved to the `okhttp` artifact.\n *  The `AndroidLogging` class is no longer necessary. `LoggingEventListener` and\n    `HttpLoggingInterceptor` write to logcat by default.\n\nThe rest of this release is our highest-quality release yet. Though we continue to use the word\n_alpha_ in the version name, the only unstable thing in it is some non-final APIs tagged\n`@ExperimentalOkHttpApi`. You can safely use this release in production.\n\n *  Fix: Attempt to read the response even if sending the request failed. This makes it possible\n    to handle response statuses like `HTTP/1.1 431 \"Request Header Fields Too Large`.\n\n *  Fix: Handle multiple 1xx responses.\n\n *  Fix: Address a performance bug in our internal task runner. We had a race condition that could\n    result in it OkHttp starting a thread for each queued task, even when a single thread could run\n    all of them.\n\n *  Fix: Address a performance bug in `MultipartReader`. We were scanning the entire input stream\n    for a delimiter when we only needed to scan enough to return a result.\n\n *  Fix: Don't double-compress the public suffix database. OkHttp is usually distributed in a\n    compressed file (like a JAR or APK), so compressing its internal data was redundant.\n\n *  Fix: Call `ProxySelector.connectFailed()` when a connection's initial TCP handshake fails.\n\n *  Fix: Change the signature of `Dispatcher` to accept a nullable `ExecutorService`. Changing this\n    parameter to be non-null was an unintended signature change in OkHttp 4.0.\n\n *  New: `EventListener.retryDecision()` is called each time a request fails with an `IOException`.\n    It notifies your listener if OkHttp will retry.\n\n *  New: `EventListener.followUpDecision()` is called each time a response is received. It notifies\n    your listener if OkHttp has decided to make a follow-up request. Some common follow-ups are\n    authentication challenges and redirects.\n\n *  New: Handy constants for `Headers.EMPTY`, `RequestBody.EMPTY`, and `ResponseBody.EMPTY`.\n\n *  New: OkHttp now calls `StrictMode.noteSlowCall()` when initializing TLS on Android. Use\n    `StrictMode` to detect if your `OkHttpClient` is being initialized on the main thread.\n\n *  Upgrade: [Okio 3.12.0][okio_3_12_0].\n\n *  Upgrade: [Kotlin 2.1.21][kotlin_2_1_21].\n\n *  Upgrade: [kotlinx.coroutines 1.10.2][coroutines_1_10_2]. This is used by the optional\n    `okhttp-coroutines` artifact.\n\n *  Upgrade: [AndroidX Startup 1.2.0][startup_1_2_0]. The Android variant of the `okhttp` artifact\n    now depends on this. This is a new dependency.\n\n *  Upgrade: [AndroidX Annotation 1.9.1][annotation_1_9_1]. As above, the Android variant of the\n    `okhttp` artifact now depends on this. This is also a new dependency.\n\n\n## Version 5.0.0-alpha.14\n\n_2024-04-17_\n\n *  Breaking: Move coroutines extensions to okhttp3.coroutines. Previously this artifact shared the\n    `okhttp3` package name with our core module, which is incompatible with the Java Platform Module\n    System.\n\n *  Fix in okhttp-coroutines: Publish a valid artifact. The coroutines JAR file in 5.0.0-alpha.13\n    was corrupt and should not be used.\n\n\n## Version 5.0.0-alpha.13\n\n_2024-04-16_\n\n *  Breaking: Tag unstable new APIs as `@ExperimentalOkHttpApi`. We intend to release OkHttp 5.0\n    without stabilizing these new APIs first.\n\n    Do not use these experimental APIs in modules that may be executed using a version of OkHttp\n    different from the version that the module was compiled with. Do not use them in published\n    libraries. Do not use them if you aren't willing to track changes to them.\n\n *  Breaking: Drop support for Kotlin Multiplatform.\n\n    We planned to support multiplatform in OkHttp 5.0, but after building it, we weren't happy with\n    the implementation trade-offs. We can't use our HTTP client engine on Kotlin/JS, and we weren't\n    prepared to build a TLS API for Kotlin/Native.\n\n    We'd prefer a multiplatform HTTP client API that's backed by OkHttp on Android and JVM, and\n    other engines on other platforms. [Ktor] does this pretty well today!\n\n *  Breaking: Use `kotlin.time.Duration` in APIs like `OkHttpClient.Builder.callTimeout()`. This\n    update also drops support for the `DurationUnit` functions introduced in earlier alpha releases\n    of OkHttp 5.\n\n *  Breaking: Reorder the parameters in the Cache constructor that was introduced in 5.0.0-alpha.3.\n\n *  New: `Request.Builder.cacheUrlOverride()` customizes the cache key used for a request. This can\n    be used to make canonical URLs for the cache that omit insignificant query parameters or other\n    irrelevant data.\n\n    This feature may be used with `POST` requests to cache their responses. In such cases the\n    request body is not used to determine the cache key, so you must manually add cache-relevant\n    data to the override URL. For example, you could add a `request-body-sha256` query parameter so\n    requests with the same POST data get the same cache entry.\n\n *  New: `HttpLoggingInterceptor.redactQueryParams()` configures the query parameters to redact\n    in logs. For best security, don't put sensitive information in query parameters.\n\n *  New: `ConnectionPool.setPolicy()` configures a minimum connection pool size for a target\n    address. Use this to proactively open HTTP connections.\n\n    Connections opened to fulfill this policy are subject to the connection pool's\n    `keepAliveDuration` but do not count against the pool-wide `maxIdleConnections` limit.\n\n    This feature increases the client's traffic and the load on the server. Talking to your server's\n    operators before adopting it.\n\n *  New in okhttp-android: `HttpLoggingInterceptor.androidLogging()` and\n    `LoggingEventListener.androidLogging()` write HTTP calls or events to Logcat.\n\n *  New: `OkHttpClient.webSocketCloseTimeout` configures how long a web socket connection will wait\n    for a graceful shutdown before it performs an abrupt shutdown.\n\n *  Fix: Honor `RequestBody.isOneShot()` in `MultipartBody`\n\n *  Fix in `okhttp-coroutines`: Don't leak response bodies in `executeAsync()`. We had a bug where\n    we didn't call `Response.close()` if the coroutine was canceled before its response was\n    returned.\n\n *  Upgrade: [Okio 3.9.0][okio_3_9_0].\n\n *  Upgrade: [Kotlin 1.9.23][kotlin_1_9_23].\n\n *  Upgrade: [Unicode® IDNA 15.1.0][idna_15_1_0]\n\n\n## Version 5.0.0-alpha.12\n\n_2023-12-17_\n\nWe took too long to cut this release and there's a lot of changes in it. We've been busy.\n\nAlthough this release is labeled _alpha_, the only unstable thing in it is our new APIs. This\nrelease has many critical bug fixes and is safe to run in production. We're eager to stabilize our\nnew APIs so we can get out of alpha.\n\n *  New: Support Java 21's virtual threads (‘OpenJDK Project Loom’). We changed OkHttp's internals\n    to use `Lock` and `Condition` instead of `synchronized` for best resource utilization.\n\n *  New: Switch our Internationalized Domain Name (IDN) implementation to [UTS #46 Nontransitional\n    Processing][uts46]. With this fix, the `ß` code point no longer maps to `ss`. OkHttp now embeds\n    its own IDN mapping table in the library.\n\n *  New: Prefer the client's configured precedence order for TLS cipher suites. (OkHttp used to\n    prefer the JDK’s precedence order.) This change may cause your HTTP calls to negotiate a\n    different cipher suite than before! OkHttp's defaults cipher suites are selected for good\n    security and performance.\n\n *  New: `ConnectionListener` publishes events for connects, disconnects, and use of pooled\n    connections.\n\n *  Fix: Immediately update the connection's flow control window instead of waiting for the\n    receiving stream to process it.\n\n    This change may increase OkHttp's memory use for applications that make many concurrent HTTP\n    calls and that can receive data faster than they can process it. Previously, OkHttp limited\n    HTTP/2 to 16 MiB of unacknowledged data per connection. With this fix there is a limit of 16 MiB\n    of unacknowledged data per stream and no per-connection limit.\n\n *  Fix: Don't close a `Deflater` while we're still using it to compress a web socket message. We\n    had a severe bug where web sockets were closed on the wrong thread, which caused\n    `NullPointerException` crashes in `Deflater`.\n\n *  Fix: Don't crash after a web socket fails its connection upgrade. We incorrectly released\n    the web socket's connections back to the pool before their resources were cleaned up.\n\n *  Fix: Don't infinite loop when a received web socket message has self-terminating compressed\n    data.\n\n *  Fix: Don't fail the call when the response code is ‘HTTP 102 Processing’ or ‘HTTP 103 Early\n    Hints’.\n\n *  Fix: Honor interceptors' changes to connect and read timeouts.\n\n *  Fix: Recover gracefully when a cached response is corrupted on disk.\n\n *  Fix: Don't leak file handles when a cache disk write fails.\n\n *  Fix: Don't hang when the public suffix database cannot be loaded. We had a bug where a failure\n    reading the public suffix database would cause subsequent reads to hang when they should have\n    crashed.\n\n *  Fix: Avoid `InetAddress.getCanonicalHostName()` in MockWebServer. This avoids problems if the\n    host machine's IP address has additional DNS registrations.\n\n *  New: Create a JPMS-compatible artifact for `JavaNetCookieJar`. Previously, multiple OkHttp\n    artifacts defined classes in the `okhttp3` package, but this is forbidden by the Java module\n    system. We've fixed this with a new package (`okhttp3.java.net.cookiejar`) and a new artifact,\n    `com.squareup.okhttp3:okhttp-java-net-cookiehandler`. (The original artifact now delegates to\n    this new one.)\n\n    ```kotlin\n    implementation(\"com.squareup.okhttp3:okhttp-java-net-cookiehandler:5.0.0-alpha.12\")\n    ```\n\n *  New: `Cookie.sameSite` determines whether cookies should be sent on cross-site requests. This\n    is used by servers to defend against Cross-Site Request Forgery (CSRF) attacks.\n\n *  New: Log the total time of the HTTP call in `HttpLoggingInterceptor`.\n\n *  New: `OkHttpClient.Builder` now has APIs that use `kotlin.time.Duration`.\n\n *  New: `mockwebserver3.SocketPolicy` is now a sealed interface. This is one of several\n    backwards-incompatible API changes that may impact early adopters of this alpha API.\n\n *  New: `mockwebserver3.Stream` for duplex streams.\n\n *  New: `mockwebserver3.MockResponseBody` for streamed response bodies.\n\n *  New: `mockwebserver3.MockResponse` is now immutable, with a `Builder`.\n\n *  New: `mockwebserver3.RecordedRequest.handshakeServerNames` returns the SNI (Server Name\n    Indication) attribute from the TLS handshake.\n\n *  Upgrade: [Kotlin 1.9.21][kotlin_1_9_21].\n\n *  Upgrade: [Okio 3.7.0][okio_3_7_0].\n\n\n## Version 5.0.0-alpha.11\n\n_2022-12-24_\n\n *  New: Enable fast fallback by default. It's our implementation of Happy Eyeballs,\n    [RFC 8305][rfc_8305]. Disable with `OkHttpClient.Builder.fastFallback(false)`.\n *  Fix: Don't log response bodies for server-sent events.\n *  Fix: Skip early hints (status code 103) responses.\n *  Fix: Don't log sensitive headers in `Request.toString()`.\n *  Fix: Don't crash when the dispatcher's `ExecutorService` is shutdown with many\n    calls still enqueued.\n *  Upgrade: [GraalVM 22][graalvm_22].\n *  Upgrade: [Kotlin 1.7.10][kotlin_1_7_10].\n\n\n## Version 5.0.0-alpha.10\n\n_2022-06-26_\n\n *  Fix: Configure the multiplatform artifact (`com.squareup.okhttp3:okhttp:3.x.x`) to depend on the\n    JVM artifact (`com.squareup.okhttp3:okhttp-jvm:3.x.x`) for Maven builds. This should work-around\n    an issue where Maven doesn't interpret Gradle metadata.\n *  Fix: Make another attempt at supporting Kotlin 1.5.31 at runtime. We were crashing on\n    `DurationUnit` which was a typealias in 1.5.x.\n *  Upgrade: [Okio 3.2.0][okio_3_2_0].\n\n\n## Version 5.0.0-alpha.9\n\n_2022-06-16_\n\n *  New: Enforce label length limits in URLs. `HttpUrl` now rejects URLs whose domains aren't valid.\n    This includes overly-long domain names (longer than 253 characters), overly-long labels (more\n    than 63 characters between dots), and empty labels.\n *  New: Don't include the `Content-Length` header in multipart bodies. Servers must delimit\n    OkHttp's request bodies using the boundary only. (This change makes OkHttp more consistent with\n    browsers and other HTTP clients.)\n *  New: Drop the `tunnelProxy` argument in `MockWebServer.useHttps()`. This change only impacts\n    the OkHttp 5.x API which uses the `mockwebserver3` package.\n *  Fix: Don't call `toDuration()` which isn't available in kotlin-stdlib 1.4.\n\n\n## Version 5.0.0-alpha.8\n\n_2022-06-08_\n\n *  Fix: Change how `H2_PRIOR_KNOWLEDGE` works with HTTP proxies. Previously OkHttp assumed the\n    proxy itself was a prior knowledge HTTP/2 server. With this update, OkHttp attempts a `CONNECT`\n    tunnel just as it would with HTTPS. For prior knowledge with proxies OkHttp's is now consistent\n    with these curl arguments:\n\n    ```\n    curl \\\n      --http2-prior-knowledge \\\n      --proxy localhost:8888 \\\n      --proxytunnel \\\n      http://squareup.com/robots.txt\n    ```\n\n *  Fix: Support executing OkHttp on kotlin-stdlib versions as old as 1.4. The library still builds\n    on up-to-date Kotlin releases (1.6.21) but no longer needs that version as a runtime dependency.\n    This should make it easier to use OkHttp in Gradle plugins.\n\n *  Fix: Don't start the clock on response timeouts until the request body is fully transmitted.\n    This is only relevant for duplex request bodies, because they are written concurrently when\n    reading the response body.\n\n *  New: `MockResponse.inTunnel()` is a new `mockwebserver3` API to configure responses that are\n    served while creating a proxy tunnel. This obsoletes both the `tunnelProxy` argument on\n    `MockWebServer` and the `UPGRADE_TO_SSL_AT_END` socket option. (Only APIs on `mockwebserver3`\n    are changed; the old `okhttp3.mockwebserver` APIs remain as they always have been.\n\n\n## Version 5.0.0-alpha.7\n\n_2022-04-26_\n\n**This release introduces new Kotlin-friendly APIs.** When we migrated OkHttp from Java to Kotlin in\nOkHttp 4.0, we kept our Java-first APIs. With 5.0 we're continuing to support Java and adding\nadditional improvements for Kotlin users. In this alpha we're excited to skip-the-builder for\nrequests and remove a common source of non-null assertions (`!!`) on the response body.\n\nThe alpha releases in the 5.0.0 series have production-quality code and an unstable API. We expect\nto make changes to the APIs introduced in 5.0.0-alpha.X. These releases are safe for production use\nand 'alpha' strictly signals that we're still experimenting with some new APIs. If you're eager for\nthe fixes or features below, please upgrade.\n\n *  New: Named and default parameters constructor for `Request`:\n\n    ```\n    val request = Request(\n      url = \"https://cash.app/\".toHttpUrl(),\n    )\n    ```\n\n *  New: `Response.body` is now non-null. This was generally the case in OkHttp 4.x, but the Kotlin\n    type declaration was nullable to support rare cases like the body on `Response.cacheResponse`,\n    `Response.networkResponse`, and `Response.priorResponse`. In such cases the body is now\n    non-null, but attempts to read its content will fail.\n *  New: Kotlin-specific APIs for request tags. Kotlin language users can lookup tags with a type\n    parameter only, like `request.tag<MyTagClass>()`.\n *  New: MockWebServer has improved support for HTTP/1xx responses. Once you've migrated to the new\n    `mockwebserver3` package, there's a new field, `MockResponse.informationalResponses`.\n *  Fix: Don't interpret trailers as headers after an HTTP/100 response. This was a bug only when\n    the HTTP response body itself is empty.\n *  Fix: Don't crash when a fast fallback call has both a deferred connection and a held connection.\n *  Fix: `OkHttpClient` no longer implements `Cloneable`. It never should have; the class is\n    immutable. This is left over from OkHttp 2.x (!) when that class was mutable. We're using the\n    5.x upgrade as an opportunity to remove very obsolete APIs.\n *  Fix: Recover gracefully when Android's `NativeCrypto` crashes with `\"ssl == null\"`. This occurs\n    when OkHttp retrieves ALPN state on a closed connection.\n *  Upgrade: [Kotlin 1.6.21][kotlin_1_6_21].\n *  Upgrade: [Okio 3.1.0][okio_3_1_0].\n\n\n## Version 5.0.0-alpha.6\n\n_2022-03-14_\n\n *  Fix: Don't attempt to close pooled connections. We saw occasional fast fallback calls crash in\n    the previous alpha due to an unexpected race.\n\n\n## Version 5.0.0-alpha.5\n\n_2022-02-21_\n\n *  Fix: Don't include [Assertk][assertk] in OkHttp's production dependencies. This regression was\n    introduced in the 5.0.0-alpha.4 release.\n *  Fix: Don't ask `Dns` implementations to resolve strings that are already IP addresses.\n *  Fix: Change fast fallback to race TCP handshakes only. To avoid wasted work, OkHttp will not\n    attempt multiple TLS handshakes for the same call concurrently.\n *  Fix: Don't crash loading the public suffix database in GraalVM native images. The function\n    `HttpUrl.topPrivateDomain()` uses a resource file to identify private domains, but we didn't\n    include this file on GraalVM.\n\n\n## Version 5.0.0-alpha.4\n\n_2022-02-01_\n\n**This release introduces fast fallback to better support mixed IPv4+IPv6 networks.** Fast fallback\nis what we're calling our implementation of Happy Eyeballs, [RFC 8305][rfc_8305]. With this\nfeature OkHttp will attempt both IPv6 and IPv4 connections concurrently, keeping whichever connects\nfirst. Fast fallback gives IPv6 connections a 250 ms head start so IPv6 is preferred on networks\nwhere it's available.\n\nTo opt-in, configure your `OkHttpClient.Builder`:\n\n\n```\nOkHttpClient client = new OkHttpClient.Builder()\n    .fastFallback(true)\n    .build();\n```\n\n *  New: Change the build from Kotlin-JVM to Kotlin-multiplatform (which includes JVM). Both\n    native and JavaScript platforms are unstable preview releases and subject to\n    backwards-incompatible changes in forthcoming releases.\n *  Fix: Don't crash loading the public suffix database resource in obfuscated builds.\n *  Fix: Don't silently ignore calls to `EventSource.cancel()` made from\n    `EventSourceListener.onOpen()`.\n *  Fix: Enforce the max intermediates constraint when using pinned certificates with Conscrypt.\n    This impacts Conscrypt when the server's presented certificates form both a trusted-but-unpinned\n    chain and an untrusted-but-pinned chain.\n *  Upgrade: [Kotlin 1.6.10][kotlin_1_6_10].\n\n\n## Version 5.0.0-alpha.3\n\n_2021-11-22_\n\n *  Fix: Change `Headers.toString()` to redact authorization and cookie headers.\n *  Fix: Don't do DNS to get the hostname for `RecordedRequest.requestUrl`. This was doing a DNS\n    lookup for the local hostname, but we really just wanted the `Host` header.\n *  Fix: Don't crash with a `InaccessibleObjectException` when detecting the platform trust manager\n    on Java 17+.\n *  Fix: Don't crash if a cookie's value is a lone double quote character.\n *  Fix: Don't crash when canceling an event source created by `EventSources.processResponse()`.\n *  New: `Cache` now has a public constructor that takes an [okio.FileSystem]. This should make it\n    possible to implement decorators for cache encryption or compression.\n *  New: `Cookie.newBuilder()` to build upon an existing cookie.\n *  New: Use TLSv1.3 when running on JDK 8u261 or newer.\n *  New: `QueueDispatcher.clear()` may be used to reset a MockWebServer instance.\n *  New: `FileDescriptor.toRequestBody()` may be particularly useful for users of Android's Storage\n    Access Framework.\n *  Upgrade: [Kotlin 1.5.31][kotlin_1_5_31].\n *  Upgrade: [Okio 3.0.0][okio_3_0_0].\n\n\n## Version 5.0.0-alpha.2\n\n_2021-01-30_\n\n**In this release MockWebServer has a new Maven coordinate and package name.** A longstanding\nproblem with MockWebServer has been its API dependency on JUnit 4. We've reorganized things to\nremove that dependency while preserving backwards compatibility.\n\n| Maven Coordinate                                         | Package Name          | Description                       |\n| :------------------------------------------------------- | :-------------------- | :-------------------------------- |\n| com.squareup.okhttp3:mockwebserver3:5.0.0-alpha.2        | mockwebserver3        | Core module. No JUnit dependency! |\n| com.squareup.okhttp3:mockwebserver3-junit4:5.0.0-alpha.2 | mockwebserver3.junit4 | Optional JUnit 4 integration.     |\n| com.squareup.okhttp3:mockwebserver3-junit5:5.0.0-alpha.2 | mockwebserver3.junit5 | Optional JUnit 5 integration.     |\n| com.squareup.okhttp3:mockwebserver:5.0.0-alpha.2         | okhttp3.mockwebserver | Obsolete. Depends on JUnit 4.     |\n\nThe new APIs use `mockwebserver3` in both the Maven coordinate and package name. This new API is\n**not stable** and will likely change before the final 5.0.0 release.\n\nIf you have code that subclasses `okhttp3.mockwebserver.QueueDispatcher`, this update is not source\nor binary compatible. Migrating to the new `mockwebserver3` package will fix this problem.\n\n *  New: DNS over HTTPS is now a stable feature of OkHttp. We introduced this as an experimental\n    module in 2018. We are confident in its stable API and solid implementation.\n *  Fix: Work around a crash in Android 10 and 11 that may be triggered when two threads\n    concurrently close an SSL socket. This would have appeared in crash logs as\n    `NullPointerException: bio == null`.\n *  Fix: Use plus `+` instead of `%20` to encode space characters in `FormBody`. This was a\n    longstanding bug in OkHttp. The fix makes OkHttp consistent with major web browsers.\n *  Fix: Don't crash if Conscrypt returns a null version.\n *  Fix: Include the public suffix data as a resource in GraalVM native images.\n *  Fix: Fail fast when the cache is corrupted.\n *  Fix: Fail fast when a private key cannot be encoded.\n *  Fix: Fail fast when attempting to verify a non-ASCII hostname.\n *  Upgrade: [GraalVM 21][graalvm_21].\n *  Upgrade: [Kotlin 1.4.20][kotlin_1_4_20].\n\n\n## Version 5.0.0-alpha.1\n\n_2021-01-30_\n\n**This release adds initial support for [GraalVM][graalvm].**\n\nGraalVM is an exciting new platform and we're eager to adopt it. The startup time improvements over\nthe JVM are particularly impressive. Try it with okcurl:\n\n```\n$ ./gradlew okcurl:nativeImage\n$ ./okcurl/build/graal/okcurl https://cash.app/robots.txt\n```\n\nThis is our first release that supports GraalVM. Our code on this platform is less mature than JVM\nand Android! Please report any issues you encounter: we'll fix them urgently.\n\n *  Fix: Attempt to read the response body even if the server canceled the request. This will cause\n    some calls to return nice error codes like `HTTP/1.1 429 Too Many Requests` instead of transport\n    errors like `SocketException: Connection reset` and `StreamResetException: stream was reset:\n    CANCEL`.\n *  New: Support OSGi metadata.\n *  Upgrade: [Okio 2.9.0][okio_2_9_0].\n\n    ```kotlin\n    implementation(\"com.squareup.okio:okio:2.9.0\")\n    ```\n\nNote that this was originally released on 2020-10-06 as 4.10.0-RC1. The only change from that\nrelease is the version name.\n\n## Version 4.x\n\nSee [4.x Change log](https://square.github.io/okhttp/changelogs/changelog_4x/) for the legacy version changelogs.\n\n[GraalVM]: https://www.graalvm.org/\n[Gradle module metadata]: https://docs.gradle.org/current/userguide/publishing_gradle_module_metadata.html\n[HTTP 101]: https://httpwg.org/specs/rfc9110.html#status.101\n[JPMS]: https://openjdk.org/projects/jigsaw/spec/\n[Ktor]: https://ktor.io/\n[Retrofit]: https://square.github.io/retrofit/\n[ZSTD-KMP]: https://github.com/square/zstd-kmp\n[androidx_startup]: https://developer.android.com/jetpack/androidx/releases/startup\n[annotation_1_9_1]: https://developer.android.com/jetpack/androidx/releases/annotation#annotation-1.9.1\n[assertk]: https://github.com/willowtreeapps/assertk\n[coroutines_1_10_2]: https://github.com/Kotlin/kotlinx.coroutines/releases/tag/1.10.2\n[curl]: https://curl.se/\n[elf_alignment]: https://developer.android.com/guide/practices/page-sizes\n[graalvm]: https://www.graalvm.org/\n[graalvm_21]: https://www.graalvm.org/release-notes/21_0/\n[graalvm_22]: https://www.graalvm.org/release-notes/22_2/\n[idna_15_1_0]: https://www.unicode.org/reports/tr46/#Modifications\n[kotlin_1_4_20]: https://github.com/JetBrains/kotlin/releases/tag/v1.4.20\n[kotlin_1_5_31]: https://github.com/JetBrains/kotlin/releases/tag/v1.5.31\n[kotlin_1_6_10]: https://github.com/JetBrains/kotlin/releases/tag/v1.6.10\n[kotlin_1_6_21]: https://github.com/JetBrains/kotlin/releases/tag/v1.6.21\n[kotlin_1_7_10]: https://github.com/JetBrains/kotlin/releases/tag/v1.7.10\n[kotlin_1_9_21]: https://github.com/JetBrains/kotlin/releases/tag/v1.9.21\n[kotlin_1_9_23]: https://github.com/JetBrains/kotlin/releases/tag/v1.9.23\n[kotlin_2_1_21]: https://github.com/JetBrains/kotlin/releases/tag/v2.1.21\n[kotlin_2_2_0]: https://github.com/JetBrains/kotlin/releases/tag/v2.2.0\n[kotlin_2_2_20]: https://github.com/JetBrains/kotlin/releases/tag/v2.2.20\n[kotlin_2_2_21]: https://github.com/JetBrains/kotlin/releases/tag/v2.2.21\n[loom]: https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html\n[okio_2_9_0]: https://square.github.io/okio/changelog/#version-290\n[okio_3_0_0]: https://square.github.io/okio/changelog/#version-300\n[okio_3_12_0]: https://square.github.io/okio/changelog/#version-3120\n[okio_3_13_0]: https://square.github.io/okio/changelog/#version-3130\n[okio_3_15_0]: https://square.github.io/okio/changelog/#version-3150\n[okio_3_16_0]: https://square.github.io/okio/changelog/#version-3160\n[okio_3_16_1]: https://square.github.io/okio/changelog/#version-3161\n[okio_3_16_2]: https://square.github.io/okio/changelog/#version-3162\n[okio_3_16_3]: https://square.github.io/okio/changelog/#version-3163\n[okio_3_16_4]: https://square.github.io/okio/changelog/#version-3164\n[okio_3_1_0]: https://square.github.io/okio/changelog/#version-310\n[okio_3_2_0]: https://square.github.io/okio/changelog/#version-320\n[okio_3_7_0]: https://square.github.io/okio/changelog/#version-370\n[okio_3_9_0]: https://square.github.io/okio/changelog/#version-390\n[rfc_8305]: https://tools.ietf.org/html/rfc8305\n[startup_1_2_0]: https://developer.android.com/jetpack/androidx/releases/startup#1.2.0\n[uts46]: https://www.unicode.org/reports/tr46\n[zstd]: https://github.com/facebook/zstd\n[zstd_kmp_0_4_0]: https://github.com/square/zstd-kmp/blob/main/CHANGELOG.md#version-040\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contributing\n============\n\nKeeping the project small and stable limits our ability to accept new contributors. We are not\nseeking new committers at this time, but some small contributions are welcome.\n\nIf you've found a security problem, please follow our [bug bounty][security] program.\n\nIf you've found a bug, please contribute a failing test case so we can study and fix it.\n\nIf you have a new feature idea, please build it in an external library. There are\n[many libraries][works_with_okhttp] that sit on top or hook in via existing APIs. If you build\nsomething that integrates with OkHttp, tell us so that we can link it!\n\nBefore code can be accepted all contributors must complete our\n[Individual Contributor License Agreement (CLA)][cla].\n\n\nCode Contributions\n------------------\n\nGet working code on a personal branch with tests passing before you submit a PR:\n\n```\n./gradlew clean check\n```\n\nPlease make every effort to follow existing conventions and style in order to keep the code as\nreadable as possible.\n\nContribute code changes through GitHub by forking the repository and sending a pull request. We\nsquash all pull requests on merge.\n\n\nGradle Setup\n------------\n\n```\n$ cat local.properties\nsdk.dir=PATH_TO_ANDROID_HOME/sdk\norg.gradle.caching=true\n```\n\nRunning Android Tests\n---------------------\n\n$ ANDROID_SDK_ROOT=PATH_TO_ANDROID_HOME/sdk ./gradlew :android-test:connectedCheck -PandroidBuild=true\n\nCommitter's Guides\n------------------\n\n * [Concurrency][concurrency]\n * [Debug Logging][debug_logging]\n * [Releasing][releasing]\n\n [cla]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1\n [concurrency]: https://square.github.io/okhttp/concurrency/\n [debug_logging]: https://square.github.io/okhttp/debug_logging/\n [releasing]: https://square.github.io/okhttp/releasing/\n [security]: https://square.github.io/okhttp/security/\n [works_with_okhttp]: https://square.github.io/okhttp/works_with_okhttp/\n [okhttp_build]: https://github.com/square/okhttp/blob/master/okhttp/build.gradle\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "OkHttp\n======\n\nSee the [project website][okhttp] for documentation and APIs.\n\nHTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP\nefficiently makes your stuff load faster and saves bandwidth.\n\nOkHttp is an HTTP client that’s efficient by default:\n\n * HTTP/2 support allows all requests to the same host to share a socket.\n * Connection pooling reduces request latency (if HTTP/2 isn’t available).\n * Transparent GZIP shrinks download sizes.\n * Response caching avoids the network completely for repeat requests.\n\nOkHttp perseveres when the network is troublesome: it will silently recover from common connection\nproblems. If your service has multiple IP addresses, OkHttp will attempt alternate addresses if the\nfirst connect fails. This is necessary for IPv4+IPv6 and services hosted in redundant data\ncenters. OkHttp supports modern TLS features (TLS 1.3, ALPN, certificate pinning). It can be\nconfigured to fall back for broad connectivity.\n\nUsing OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It\nsupports both synchronous blocking calls and async calls with callbacks.\n\nA well behaved user agent\n-------------------------\n\nOkHttp follows modern HTTP specifications such as\n\n* HTTP Semantics - [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110)\n* HTTP Caching- [RFC 9111](https://datatracker.ietf.org/doc/html/rfc9111)\n* HTTP/1.1 - [RFC 9112](https://datatracker.ietf.org/doc/html/rfc9112)\n* HTTP/2 - [RFC 9113](https://datatracker.ietf.org/doc/html/rfc9113)\n* Websockets - [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455)\n* SSE - [Server-sent events](https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events)\n\nWhere the spec is ambiguous, OkHttp follows modern user agents such as popular Browsers or common HTTP Libraries.\n\nOkHttp is principled and avoids being overly configurable, especially when such configuration is\nto workaround a buggy server, test invalid scenarios or that contradict the relevant RFC.\nOther HTTP libraries exist that fill that gap allowing extensive customisation including potentially\ninvalid requests.\n\nExample Limitations\n\n* Does not allow GET with a body.\n* Cache is not an interface with alternative implementations.\n\nGet a URL\n---------\n\nThis program downloads a URL and prints its contents as a string. [Full source][get_example].\n\n```java\nOkHttpClient client = new OkHttpClient();\n\nString run(String url) throws IOException {\n  Request request = new Request.Builder()\n      .url(url)\n      .build();\n\n  try (Response response = client.newCall(request).execute()) {\n    return response.body().string();\n  }\n}\n```\n\n\nPost to a Server\n----------------\n\nThis program posts data to a service. [Full source][post_example].\n\n```java\npublic static final MediaType JSON = MediaType.get(\"application/json\");\n\nOkHttpClient client = new OkHttpClient();\n\nString post(String url, String json) throws IOException {\n  RequestBody body = RequestBody.create(json, JSON);\n  Request request = new Request.Builder()\n      .url(url)\n      .post(body)\n      .build();\n  try (Response response = client.newCall(request).execute()) {\n    return response.body().string();\n  }\n}\n```\n\nFurther examples are on the [OkHttp Recipes page][recipes].\n\n\nRequirements\n------------\n\nOkHttp works on Android 5.0+ (API level 21+) and Java 8+.\n\nOn Android, OkHttp uses [AndroidX Startup][androidx_startup]. If you disable the initializer in the manifest,\nthen apps are responsible for calling `OkHttp.initialize(applicationContext)` in `Application.onCreate`.\n\nOkHttp depends on [Okio][okio] for high-performance I/O and the [Kotlin standard library][kotlin]. Both are small libraries with strong backward-compatibility.\n\nWe highly recommend you keep OkHttp up-to-date. As with auto-updating web browsers, staying current\nwith HTTPS clients is an important defense against potential security problems. [We\ntrack][tls_history] the dynamic TLS ecosystem and adjust OkHttp to improve connectivity and\nsecurity.\n\nOkHttp uses your platform's built-in TLS implementation. On Java platforms OkHttp also supports\n[Conscrypt][conscrypt], which integrates [BoringSSL](https://github.com/google/boringssl) with Java. OkHttp will use Conscrypt if it is\nthe first security provider:\n\n```java\nSecurity.insertProviderAt(Conscrypt.newProvider(), 1);\n```\n\nThe OkHttp `3.12.x` branch supports Android 2.3+ (API level 9+) and Java 7+. These platforms lack\nsupport for TLS 1.2 and should not be used.\n\n\nReleases\n--------\n\nOur [change log][changelog] has release history.\n\nThe latest release is available on [Maven Central](https://search.maven.org/artifact/com.squareup.okhttp3/okhttp/5.3.0/jar).\n\n```kotlin\nimplementation(\"com.squareup.okhttp3:okhttp:5.3.0\")\n```\n\nSnapshot builds are [available][snap]. [R8 and ProGuard][r8_proguard] rules are available.\n\nAlso, we have a [bill of materials (BOM)][bom] available to help you keep OkHttp artifacts up to date and be sure about version compatibility.\n\n```kotlin\n    dependencies {\n       // define a BOM and its version\n       implementation(platform(\"com.squareup.okhttp3:okhttp-bom:5.3.0\"))\n\n       // define any required OkHttp artifacts without version\n       implementation(\"com.squareup.okhttp3:okhttp\")\n       implementation(\"com.squareup.okhttp3:logging-interceptor\")\n    }\n```\n\nMaven and JVM Projects\n----------------------\n\nOkHttp is published as a Kotlin Multiplatform project. While Gradle handles this automatically,\nMaven projects must select between `okhttp-jvm` and `okhttp-android`. The `okhttp` artifact will be empty in\nMaven projects.\n\n```xml\n<dependencyManagement>\n  <dependencies>\n    <dependency>\n      <groupId>com.squareup.okhttp3</groupId>\n      <artifactId>okhttp-bom</artifactId>\n      <version>5.2.0</version>\n      <type>pom</type>\n      <scope>import</scope>\n    </dependency>\n  </dependencies>\n</dependencyManagement>\n```\n\n\n\n```xml\n<dependency>\n  <groupId>com.squareup.okhttp3</groupId>\n  <artifactId>okhttp-jvm</artifactId>\n  <!-- Remove after OkHttp 5.2.0 with updated BOM. -->\n  <version>5.1.0</version>\n</dependency>\n\n<dependency>\n  <groupId>com.squareup.okhttp3</groupId>\n  <artifactId>mockwebserver3</artifactId>\n</dependency>\n\n<dependency>\n  <groupId>com.squareup.okhttp3</groupId>\n  <artifactId>logging-interceptor</artifactId>\n</dependency>\n```\n\nMockWebServer\n-------------\n\nOkHttp includes a library for testing HTTP, HTTPS, and HTTP/2 clients.\n\nThe latest release is available on [Maven Central](https://search.maven.org/artifact/com.squareup.okhttp3/mockwebserver/5.3.0/jar).\n\n```kotlin\ntestImplementation(\"com.squareup.okhttp3:mockwebserver3:5.3.0\")\n```\n\nMockWebServer is used for firstly for internal testing, and for basic testing of apps using OkHttp client.\nIt is not a full featured HTTP testing library that is developed standalone. It is not being actively developed\nfor new features. As such you might find your needs outgrow MockWebServer and you may which to use a\nmore full featured testing library such as [MockServer](https://www.mock-server.com/).\n\nGraalVM Native Image\n--------------------\n\nBuilding your native images with [GraalVM] should work automatically.\n\nSee the okcurl module for an example build.\n\n```shell\n$ ./gradlew okcurl:nativeImage\n$ ./okcurl/build/graal/okcurl https://httpbin.org/get\n```\n\nJava Modules\n------------\n\nOkHttp (5.2+) implements Java 9 Modules.\n\nWith this in place Java builds should fail if apps attempt to use internal packages.\n\n```\nerror: package okhttp3.internal.platform is not visible\n    okhttp3.internal.platform.Platform.get();\n                    ^\n  (package okhttp3.internal.platform is declared in module okhttp3,\n    which does not export it to module com.bigco.sdk)\n```\n\nThe stable public API is based on the list of defined modules:\n\n- okhttp3\n- okhttp3.brotli\n- okhttp3.coroutines\n- okhttp3.dnsoverhttps\n- okhttp3.java.net.cookiejar\n- okhttp3.logging\n- okhttp3.sse\n- okhttp3.tls\n- okhttp3.urlconnection\n- mockwebserver3\n- mockwebserver3.junit4\n- mockwebserver3.junit5\n\nLicense\n-------\n\n```\nCopyright 2019 Square, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n [GraalVM]: https://www.graalvm.org/\n [androidx_startup]: https://developer.android.com/jetpack/androidx/releases/startup\n [bom]: https://docs.gradle.org/6.2/userguide/platforms.html#sub:bom_import\n [changelog]: https://square.github.io/okhttp/changelog/\n [conscrypt]: https://github.com/google/conscrypt/\n [get_example]: https://raw.github.com/square/okhttp/master/samples/guide/src/main/java/okhttp3/guide/GetExample.java\n [kotlin]: https://kotlinlang.org/\n [okhttp3_pro]: https://raw.githubusercontent.com/square/okhttp/master/okhttp/src/main/resources/META-INF/proguard/okhttp3.pro\n [okhttp]: https://square.github.io/okhttp/\n [okhttp_312x]: https://github.com/square/okhttp/tree/okhttp_3.12.x\n [okio]: https://github.com/square/okio\n [post_example]: https://raw.github.com/square/okhttp/master/samples/guide/src/main/java/okhttp3/guide/PostExample.java\n [r8_proguard]: https://square.github.io/okhttp/features/r8_proguard/\n [recipes]: https://square.github.io/okhttp/recipes/\n [snap]: https://s01.oss.sonatype.org/content/repositories/snapshots/\n [tls_history]: https://square.github.io/okhttp/tls_configuration_history/\n"
  },
  {
    "path": "android-test/build.gradle.kts",
    "content": "import okhttp3.buildsupport.androidBuild\n\nplugins {\n  id(\"okhttp.base-conventions\")\n  id(\"com.android.library\")\n  id(\"de.mannodermaus.android-junit5\")\n}\n\nandroid {\n  compileSdk = 36\n\n  namespace = \"okhttp.android.test\"\n\n  defaultConfig {\n    minSdk = 21\n\n    // Make sure to use the AndroidJUnitRunner (or a sub-class) in order to hook in the JUnit 5 Test Builder\n    testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n    testInstrumentationRunnerArguments += mapOf(\n      \"runnerBuilder\" to \"de.mannodermaus.junit5.AndroidJUnit5Builder\",\n      \"notPackage\" to \"org.bouncycastle\",\n      \"configurationParameters\" to \"junit.jupiter.extensions.autodetection.enabled=true\"\n    )\n  }\n\n  if (androidBuild) {\n    sourceSets[\"androidTest\"].java.srcDirs(\n      \"../okhttp-brotli/src/test/java\",\n      \"../okhttp-dnsoverhttps/src/test/java\",\n      \"../okhttp-logging-interceptor/src/test/java\",\n      \"../okhttp-sse/src/test/java\"\n    )\n  }\n\n  compileOptions {\n    targetCompatibility(JavaVersion.VERSION_11)\n    sourceCompatibility(JavaVersion.VERSION_11)\n  }\n\n  testOptions {\n    targetSdk = 34\n    unitTests.isIncludeAndroidResources = true\n  }\n\n\n  // issue merging due to conflict with httpclient and something else\n  packagingOptions.resources.excludes += setOf(\n    \"META-INF/DEPENDENCIES\",\n    \"META-INF/LICENSE.md\",\n    \"META-INF/LICENSE-notice.md\",\n    \"README.txt\",\n    \"org/bouncycastle/LICENSE\",\n    \"META-INF/versions/9/OSGI-INF/MANIFEST.MF\"\n  )\n}\n\ndependencies {\n  implementation(libs.kotlin.reflect)\n  implementation(libs.playservices.safetynet)\n  \"friendsImplementation\"(projects.okhttp)\n  \"friendsImplementation\"(projects.okhttpDnsoverhttps)\n\n  testImplementation(projects.okhttp)\n  testImplementation(libs.junit)\n  testImplementation(libs.junit.ktx)\n  testImplementation(libs.assertk)\n  testImplementation(projects.okhttpTls)\n  \"friendsTestImplementation\"(projects.loggingInterceptor)\n  testImplementation(libs.androidx.test.runner)\n  testImplementation(libs.robolectric)\n  testImplementation(libs.androidx.espresso.core)\n  testImplementation(libs.square.okio.fakefilesystem)\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(libs.conscrypt.openjdk)\n  testImplementation(libs.junit.jupiter.engine)\n  testImplementation(libs.junit.vintage.engine)\n\n  androidTestImplementation(projects.okhttpTestingSupport) {\n    exclude(\"org.openjsse\", \"openjsse\")\n    exclude(\"org.conscrypt\", \"conscrypt-openjdk-uber\")\n    exclude(\"software.amazon.cryptools\", \"AmazonCorrettoCryptoProvider\")\n  }\n  androidTestImplementation(libs.assertk)\n  androidTestImplementation(libs.bouncycastle.bcprov)\n  androidTestImplementation(libs.bouncycastle.bctls)\n  androidTestImplementation(libs.conscrypt.android)\n  androidTestImplementation(projects.mockwebserver3Junit4)\n  androidTestImplementation(projects.mockwebserver3Junit5)\n  androidTestImplementation(projects.okhttpBrotli)\n  androidTestImplementation(projects.okhttpZstd)\n  androidTestImplementation(projects.okhttpDnsoverhttps)\n  androidTestImplementation(projects.loggingInterceptor)\n  androidTestImplementation(projects.okhttpSse)\n  androidTestImplementation(projects.okhttpTls)\n  androidTestImplementation(libs.androidx.junit)\n  androidTestImplementation(libs.androidx.espresso.core)\n  androidTestImplementation(libs.http.client5)\n  androidTestImplementation(libs.kotlin.test.common)\n  androidTestImplementation(libs.kotlin.test.junit)\n  androidTestImplementation(libs.square.moshi)\n  androidTestImplementation(libs.square.moshi.kotlin)\n  androidTestImplementation(libs.square.okio.fakefilesystem)\n\n  androidTestImplementation(libs.androidx.test.runner)\n  androidTestImplementation(libs.junit.jupiter.api)\n  androidTestImplementation(libs.junit5android.core)\n  androidTestRuntimeOnly(libs.junit5android.runner)\n}\n\njunitPlatform {\n  filters {\n    excludeTags(\"Remote\")\n  }\n}\n"
  },
  {
    "path": "android-test/src/androidDeviceTest/README.md",
    "content": "Android Test\n============\n\nA gradle module for running Android instrumentation tests on a device or emulator.\n\n1. Add an Emulator named `pixel5`, if you don't already have one\n\n```\n$ sdkmanager --install \"system-images;android-29;google_apis;x86\"\n$ echo \"no\" | avdmanager --verbose create avd --force --name \"pixel5\" --device \"pixel\" --package \"system-images;android-29;google_apis;x86\" --tag \"google_apis\" --abi \"x86\"\n```\n\n2. Run an Emulator using Android Studio or from command line.\n\n```\n$ emulator -no-window -no-snapshot-load @pixel5\n```\n\n2. Turn on logs with logcat\n\n```\n$ adb logcat '*:E' OkHttp:D Http2:D TestRunner:D TaskRunner:D OkHttpTest:D GnssHAL_GnssInterface:F DeviceStateChecker:F memtrack:F\n...\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] responseHeadersEnd: Response{protocol=h2, code=200, message=, url=https://1.1.1.1/dns-query?dns=AAABAAABAAAAAAAAA3d3dwhmYWNlYm9vawNjb20AABwAAQ}\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] responseBodyStart\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] responseBodyEnd: byteCount=128\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] connectionReleased\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] callEnd\n01-01 12:53:32.816 10999 11090 D OkHttp  : [54 ms] responseHeadersStart\n01-01 12:53:32.816 10999 11090 D OkHttp  : [54 ms] responseHeadersEnd: Response{protocol=h2, code=200, message=, url=https://1.1.1.1/dns-query?dns=AAABAAABAAAAAAAAA3d3dwhmYWNlYm9vawNjb20AAAEAAQ}\n01-01 12:53:32.817 10999 11090 D OkHttp  : [55 ms] responseBodyStart\n01-01 12:53:32.818 10999 11090 D OkHttp  : [56 ms] responseBodyEnd: byteCount=128\n01-01 12:53:32.818 10999 11090 D OkHttp  : [56 ms] connectionReleased\n01-01 12:53:32.818 10999 11090 D OkHttp  : [56 ms] callEnd\n```\n\n3. Run tests using gradle\n\n```\n$ ANDROID_SDK_ROOT=/Users/myusername/Library/Android/sdk ./gradlew :android-test:connectedCheck -PandroidBuild=true\n...\n> Task :android-test:connectedDebugAndroidTest\n...\n11:55:40 V/InstrumentationResultParser: Time: 13.271\n11:55:40 V/InstrumentationResultParser:\n11:55:40 V/InstrumentationResultParser: OK (12 tests)\n...\n11:55:40 I/XmlResultReporter: XML test result file generated at /Users/myusername/workspace/okhttp/android-test/build/outputs/androidTest-results/connected/TEST-pixel3a-Q(AVD) - 10-android-test-.xml. Total tests 13, passed 11, assumption_failure 1, ignored 1,\n...\nBUILD SUCCESSFUL in 1m 30s\n63 actionable tasks: 61 executed, 2 up-to-date\n\n```\n\nn.b. use ANDROID_SERIAL=emulator-5554 or similar if you need to select between devices.\n"
  },
  {
    "path": "android-test/src/androidDeviceTest/java/okhttp/android/test/OkHttpTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.test\n\nimport android.content.Context\nimport android.os.Build\nimport androidx.test.core.app.ApplicationProvider\nimport androidx.test.platform.app.InstrumentationRegistry\nimport com.google.android.gms.common.GooglePlayServicesNotAvailableException\nimport com.google.android.gms.security.ProviderInstaller\nimport com.squareup.moshi.Moshi\nimport com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport java.security.KeyStore\nimport java.security.SecureRandom\nimport java.security.Security\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateException\nimport java.security.cert.X509Certificate\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.logging.Handler\nimport java.util.logging.Level\nimport java.util.logging.LogRecord\nimport java.util.logging.Logger\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.TrustManagerFactory\nimport javax.net.ssl.X509TrustManager\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Cache\nimport okhttp3.Call\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.FollowUpDecision\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.RequestHeadersEnd\nimport okhttp3.CallEvent.RequestHeadersStart\nimport okhttp3.CallEvent.ResponseBodyEnd\nimport okhttp3.CallEvent.ResponseBodyStart\nimport okhttp3.CallEvent.ResponseHeadersEnd\nimport okhttp3.CallEvent.ResponseHeadersStart\nimport okhttp3.CallEvent.SecureConnectEnd\nimport okhttp3.CallEvent.SecureConnectStart\nimport okhttp3.CertificatePinner\nimport okhttp3.CompressionInterceptor\nimport okhttp3.Connection\nimport okhttp3.DelegatingSSLSocket\nimport okhttp3.DelegatingSSLSocketFactory\nimport okhttp3.EventListener\nimport okhttp3.EventRecorder\nimport okhttp3.Gzip\nimport okhttp3.Headers\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.TlsVersion\nimport okhttp3.brotli.Brotli\nimport okhttp3.dnsoverhttps.DnsOverHttps\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.http2.Http2\nimport okhttp3.internal.platform.Android10Platform\nimport okhttp3.internal.platform.AndroidPlatform\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.PlatformRegistry\nimport okhttp3.logging.LoggingEventListener\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport okhttp3.zstd.Zstd\nimport okio.ByteString.Companion.toByteString\nimport org.bouncycastle.jce.provider.BouncyCastleProvider\nimport org.bouncycastle.jsse.provider.BouncyCastleJsseProvider\nimport org.conscrypt.Conscrypt\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertNotNull\nimport org.junit.jupiter.api.Assertions.assertNull\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.opentest4j.TestAbortedException\n\n@Tag(\"Slow\")\nclass OkHttpTest {\n  @Suppress(\"RedundantVisibilityModifier\")\n  @JvmField\n  @RegisterExtension\n  public val platform = PlatformRule()\n\n  @Suppress(\"RedundantVisibilityModifier\")\n  @JvmField\n  @RegisterExtension\n  public val clientTestRule =\n    OkHttpClientTestRule().apply {\n      logger = Logger.getLogger(OkHttpTest::class.java.name)\n    }\n\n  private var client: OkHttpClient =\n    clientTestRule\n      .newClientBuilder()\n      .addInterceptor(CompressionInterceptor(Zstd, Brotli, Gzip))\n      .build()\n\n  private val moshi =\n    Moshi\n      .Builder()\n      .add(KotlinJsonAdapterFactory())\n      .build()\n\n  private val handshakeCertificates = localhost()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setup() {\n    // Needed because of Platform.resetForTests\n    PlatformRegistry.applicationContext = ApplicationProvider.getApplicationContext<Context>()\n  }\n\n  @Test\n  fun testPlatform() {\n    assertTrue(Platform.isAndroid)\n\n    if (Build.VERSION.SDK_INT >= 29) {\n      assertTrue(Platform.get() is Android10Platform)\n    } else {\n      assertTrue(Platform.get() is AndroidPlatform)\n    }\n  }\n\n  @Test\n  fun testRequest() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://api.twitter.com/robots.txt\").build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n    }\n  }\n\n  @Test\n  fun testLocalhostInsecure() {\n    assumeTrue(Build.VERSION.SDK_INT >= 24)\n\n    val clientCertificates =\n      HandshakeCertificates\n        .Builder()\n        .apply {\n          if (Build.VERSION.SDK_INT >= 24) {\n            addInsecureHost(server.hostName)\n          }\n        }.build()\n\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n        .build()\n\n    localhostInsecureRequest()\n  }\n\n  @Test\n  fun testRequestWithSniRequirement() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://docs.fabric.io/android/changelog.html\").build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n    }\n  }\n\n  @Test\n  fun testConscryptRequest() {\n    assumeNetwork()\n\n    try {\n      Security.insertProviderAt(Conscrypt.newProviderBuilder().build(), 1)\n\n      val request = Request.Builder().url(\"https://facebook.com/robots.txt\").build()\n\n      var socketClass: String? = null\n\n      // Need fresh client to reset sslSocketFactoryOrNull\n      client =\n        OkHttpClient\n          .Builder()\n          .eventListenerFactory(\n            clientTestRule.wrap(\n              object : EventListener() {\n                override fun connectionAcquired(\n                  call: Call,\n                  connection: Connection,\n                ) {\n                  socketClass = connection.socket().javaClass.name\n                }\n              },\n            ),\n          ).build()\n\n      val response = client.newCall(request).execute()\n\n      response.use {\n        assertEquals(Protocol.HTTP_2, response.protocol)\n        assertEquals(200, response.code)\n        // see https://github.com/google/conscrypt/blob/b9463b2f74df42d85c73715a5f19e005dfb7b802/android/src/main/java/org/conscrypt/Platform.java#L613\n        when {\n          Build.VERSION.SDK_INT >= 24 -> {\n            // Conscrypt 2.5+ defaults to SSLEngine-based SSLSocket\n            assertEquals(\"org.conscrypt.Java8EngineSocket\", socketClass)\n          }\n\n          Build.VERSION.SDK_INT < 22 -> {\n            assertEquals(\"org.conscrypt.KitKatPlatformOpenSSLSocketImplAdapter\", socketClass)\n          }\n\n          else -> {\n            assertEquals(\"org.conscrypt.ConscryptFileDescriptorSocket\", socketClass)\n          }\n        }\n        assertEquals(TlsVersion.TLS_1_3, response.handshake?.tlsVersion)\n      }\n    } finally {\n      Security.removeProvider(\"Conscrypt\")\n      client.close()\n    }\n  }\n\n  @Test\n  fun testConscryptRequestLocalhostInsecure() {\n    try {\n      Security.insertProviderAt(Conscrypt.newProviderBuilder().build(), 1)\n\n      val clientCertificates =\n        HandshakeCertificates\n          .Builder()\n          .addInsecureHost(server.hostName)\n          .build()\n\n      // Need fresh client to reset sslSocketFactoryOrNull\n      client =\n        OkHttpClient\n          .Builder()\n          .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n          .build()\n\n      localhostInsecureRequest()\n    } finally {\n      Security.removeProvider(\"Conscrypt\")\n      client.close()\n    }\n  }\n\n  @Test\n  fun testRequestUsesPlayProvider() {\n    assumeNetwork()\n\n    try {\n      try {\n        ProviderInstaller.installIfNeeded(InstrumentationRegistry.getInstrumentation().targetContext)\n      } catch (gpsnae: GooglePlayServicesNotAvailableException) {\n        throw TestAbortedException(\"Google Play Services not available\", gpsnae)\n      }\n\n      val request = Request.Builder().url(\"https://facebook.com/robots.txt\").build()\n\n      var socketClass: String? = null\n\n      // Need fresh client to reset sslSocketFactoryOrNull\n      client =\n        OkHttpClient\n          .Builder()\n          .eventListenerFactory(\n            clientTestRule.wrap(\n              object : EventListener() {\n                override fun connectionAcquired(\n                  call: Call,\n                  connection: Connection,\n                ) {\n                  socketClass = connection.socket().javaClass.name\n                }\n              },\n            ),\n          ).build()\n\n      val response = client.newCall(request).execute()\n\n      response.use {\n        assertEquals(Protocol.HTTP_2, response.protocol)\n        assertEquals(200, response.code)\n        assertEquals(\"com.google.android.gms.org.conscrypt.Java8FileDescriptorSocket\", socketClass)\n        assertEquals(TlsVersion.TLS_1_2, response.handshake?.tlsVersion)\n      }\n    } finally {\n      Security.removeProvider(\"GmsCore_OpenSSL\")\n      client.close()\n    }\n  }\n\n  @Test\n  fun testRequestUsesPlayProviderLocalhostInsecure() {\n    try {\n      try {\n        ProviderInstaller.installIfNeeded(InstrumentationRegistry.getInstrumentation().targetContext)\n      } catch (gpsnae: GooglePlayServicesNotAvailableException) {\n        throw TestAbortedException(\"Google Play Services not available\", gpsnae)\n      }\n\n      val clientCertificates =\n        HandshakeCertificates\n          .Builder()\n          .addPlatformTrustedCertificates()\n          .addInsecureHost(server.hostName)\n          .build()\n\n      // Need fresh client to reset sslSocketFactoryOrNull\n      client =\n        OkHttpClient\n          .Builder()\n          .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n          .build()\n\n      localhostInsecureRequest()\n    } finally {\n      Security.removeProvider(\"GmsCore_OpenSSL\")\n      client.close()\n    }\n  }\n\n  private fun localhostInsecureRequest() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n\n    server.enqueue(MockResponse())\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    client.newCall(request).execute().use {\n      assertEquals(200, it.code)\n      assertEquals(listOf<Certificate>(), it.handshake?.peerCertificates)\n    }\n  }\n\n  @Test\n  fun testRequestUsesAndroidConscrypt() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://facebook.com/robots.txt\").build()\n\n    var socketClass: String? = null\n\n    client =\n      client\n        .newBuilder()\n        .eventListenerFactory(\n          clientTestRule.wrap(\n            object : EventListener() {\n              override fun connectionAcquired(\n                call: Call,\n                connection: Connection,\n              ) {\n                socketClass = connection.socket().javaClass.name\n              }\n            },\n          ),\n        ).build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(Protocol.HTTP_2, response.protocol)\n      if (Build.VERSION.SDK_INT >= 29) {\n        assertEquals(TlsVersion.TLS_1_3, response.handshake?.tlsVersion)\n      } else {\n        assertEquals(TlsVersion.TLS_1_2, response.handshake?.tlsVersion)\n      }\n      assertEquals(200, response.code)\n      assertTrue(socketClass?.startsWith(\"com.android.org.conscrypt.\") == true)\n    }\n  }\n\n  @Test\n  fun testRequestUsesAndroidConscryptLocalhostInsecure() {\n    assumeTrue(Build.VERSION.SDK_INT >= 24)\n\n    val clientCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addPlatformTrustedCertificates()\n        .apply {\n          if (Build.VERSION.SDK_INT >= 24) {\n            addInsecureHost(server.hostName)\n          }\n        }.build()\n\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n        .build()\n\n    localhostInsecureRequest()\n  }\n\n  @Test\n  fun testHttpRequestNotBlockedOnLegacyAndroid() {\n    assumeTrue(Build.VERSION.SDK_INT < 23)\n\n    val request = Request.Builder().url(\"http://squareup.com/robots.txt\").build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n    }\n  }\n\n  @Test\n  @Disabled(\"cleartext required for additional okhttp wide tests\")\n  fun testHttpRequestBlocked() {\n    assumeTrue(Build.VERSION.SDK_INT >= 23)\n\n    val request = Request.Builder().url(\"http://squareup.com/robots.txt\").build()\n\n    try {\n      client.newCall(request).execute()\n      fail<Any>(\"expected cleartext blocking\")\n    } catch (_: java.net.UnknownServiceException) {\n    }\n  }\n\n  data class HowsMySslResults(\n    val unknown_cipher_suite_supported: Boolean,\n    val beast_vuln: Boolean,\n    val session_ticket_supported: Boolean,\n    val tls_compression_supported: Boolean,\n    val ephemeral_keys_supported: Boolean,\n    val rating: String,\n    val tls_version: String,\n    val able_to_detect_n_minus_one_splitting: Boolean,\n    val insecure_cipher_suites: Map<String, List<String>>,\n    val given_cipher_suites: List<String>?,\n  )\n\n  @Test\n  @Disabled\n  fun testSSLFeatures() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://www.howsmyssl.com/a/check\").build()\n\n    val response = client.newCall(request).execute()\n\n    val results =\n      response.use {\n        moshi.adapter(HowsMySslResults::class.java).fromJson(response.body.string())!!\n      }\n\n    Platform.get().log(\"results $results\", Platform.WARN)\n\n    assertTrue(results.session_ticket_supported)\n    assertEquals(\"Probably Okay\", results.rating)\n    // TODO map to expected versions automatically, test ignored for now.  Run manually.\n    assertEquals(\"TLS 1.3\", results.tls_version)\n    assertEquals(0, results.insecure_cipher_suites.size)\n\n    assertEquals(TlsVersion.TLS_1_3, response.handshake?.tlsVersion)\n    assertEquals(Protocol.HTTP_2, response.protocol)\n  }\n\n  @Test\n  fun testMockWebserverRequest() {\n    enableTls()\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n      assertEquals(Protocol.HTTP_2, response.protocol)\n      val tlsVersion = response.handshake?.tlsVersion\n      assertTrue(tlsVersion == TlsVersion.TLS_1_2 || tlsVersion == TlsVersion.TLS_1_3)\n      assertEquals(\n        \"CN=localhost\",\n        (response.handshake!!.peerCertificates.first() as X509Certificate).subjectDN.name,\n      )\n    }\n  }\n\n  @Test\n  fun testCertificatePinningFailure() {\n    enableTls()\n\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(server.hostName, \"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\")\n        .build()\n    client = client.newBuilder().certificatePinner(certificatePinner).build()\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    try {\n      client.newCall(request).execute()\n      fail<Any>(\"\")\n    } catch (_: SSLPeerUnverifiedException) {\n    }\n  }\n\n  @Test\n  fun testCertificatePinningSuccess() {\n    enableTls()\n\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\n          server.hostName,\n          CertificatePinner.pin(handshakeCertificates.trustManager.acceptedIssuers[0]),\n        ).build()\n    client = client.newBuilder().certificatePinner(certificatePinner).build()\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n    }\n  }\n\n  @Test\n  fun testEventListener() {\n    val eventRecorder = EventRecorder()\n\n    enableTls()\n\n    client =\n      client\n        .newBuilder()\n        .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n        .build()\n\n    server.enqueue(MockResponse(body = \"abc1\"))\n    server.enqueue(MockResponse(body = \"abc2\"))\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    client.newCall(request).execute().use { response ->\n      assertEquals(200, response.code)\n    }\n\n    assertEquals(\n      listOf(\n        CallStart::class,\n        ProxySelectStart::class,\n        ProxySelectEnd::class,\n        DnsStart::class,\n        DnsEnd::class,\n        ConnectStart::class,\n        SecureConnectStart::class,\n        SecureConnectEnd::class,\n        ConnectEnd::class,\n        ConnectionAcquired::class,\n        RequestHeadersStart::class,\n        RequestHeadersEnd::class,\n        ResponseHeadersStart::class,\n        ResponseHeadersEnd::class,\n        FollowUpDecision::class,\n        ResponseBodyStart::class,\n        ResponseBodyEnd::class,\n        ConnectionReleased::class,\n        CallEnd::class,\n      ),\n      eventRecorder.recordedEventTypes(),\n    )\n\n    eventRecorder.clearAllEvents()\n\n    client.newCall(request).execute().use { response ->\n      assertEquals(200, response.code)\n    }\n\n    assertEquals(\n      listOf(\n        CallStart::class,\n        ConnectionAcquired::class,\n        RequestHeadersStart::class,\n        RequestHeadersEnd::class,\n        ResponseHeadersStart::class,\n        ResponseHeadersEnd::class,\n        FollowUpDecision::class,\n        ResponseBodyStart::class,\n        ResponseBodyEnd::class,\n        ConnectionReleased::class,\n        CallEnd::class,\n      ),\n      eventRecorder.recordedEventTypes(),\n    )\n  }\n\n  @Test\n  fun testSessionReuse() {\n    val sessionIds = mutableListOf<String>()\n\n    enableTls()\n\n    client =\n      client\n        .newBuilder()\n        .eventListenerFactory(\n          clientTestRule.wrap(\n            object : EventListener() {\n              override fun connectionAcquired(\n                call: Call,\n                connection: Connection,\n              ) {\n                val sslSocket = connection.socket() as SSLSocket\n\n                sessionIds.add(\n                  sslSocket.session.id\n                    .toByteString()\n                    .hex(),\n                )\n              }\n            },\n          ),\n        ).build()\n\n    server.enqueue(MockResponse(body = \"abc1\"))\n    server.enqueue(MockResponse(body = \"abc2\"))\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    client.newCall(request).execute().use { response ->\n      assertEquals(200, response.code)\n    }\n\n    client.connectionPool.evictAll()\n    assertEquals(0, client.connectionPool.connectionCount())\n\n    client.newCall(request).execute().use { response ->\n      assertEquals(200, response.code)\n    }\n\n    assertEquals(2, sessionIds.size)\n    assertEquals(sessionIds[0], sessionIds[1])\n  }\n\n  @Test\n  fun testDnsOverHttps() {\n    assumeNetwork()\n\n    client =\n      client\n        .newBuilder()\n        .eventListenerFactory(clientTestRule.wrap(LoggingEventListener.Factory()))\n        .build()\n\n    val dohDns = buildCloudflareIp(client)\n    val dohEnabledClient =\n      client\n        .newBuilder()\n        .eventListener(EventListener.NONE)\n        .dns(dohDns)\n        .build()\n\n    dohEnabledClient.get(\"https://www.twitter.com/robots.txt\")\n    dohEnabledClient.get(\"https://www.facebook.com/robots.txt\")\n  }\n\n  @Test\n  fun testCustomTrustManager() {\n    assumeNetwork()\n\n    val trustManager =\n      object : X509TrustManager {\n        override fun checkClientTrusted(\n          chain: Array<out X509Certificate>?,\n          authType: String?,\n        ) {}\n\n        override fun checkServerTrusted(\n          chain: Array<out X509Certificate>?,\n          authType: String?,\n        ) {}\n\n        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()\n      }\n\n    val sslContext =\n      Platform.get().newSSLContext().apply {\n        init(null, arrayOf(trustManager), null)\n      }\n    val sslSocketFactory = sslContext.socketFactory\n\n    val hostnameVerifier = HostnameVerifier { _, _ -> true }\n\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(sslSocketFactory, trustManager)\n        .hostnameVerifier(hostnameVerifier)\n        .build()\n\n    client.get(\"https://www.facebook.com/robots.txt\")\n  }\n\n  @Test\n  fun testCustomSSLSocketFactoryWithoutALPN() {\n    enableTls()\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val sslSocketFactory = client.sslSocketFactory\n    val trustManager = client.x509TrustManager!!\n\n    val delegatingSocketFactory =\n      object : DelegatingSSLSocketFactory(sslSocketFactory) {\n        override fun configureSocket(sslSocket: SSLSocket): SSLSocket =\n          object : DelegatingSSLSocket(sslSocket) {\n            override fun getApplicationProtocol(): String = throw UnsupportedOperationException()\n          }\n      }\n\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(delegatingSocketFactory, trustManager)\n        .build()\n\n    val request = Request.Builder().url(server.url(\"/\").toString()).build()\n    val response = client.newCall(request).execute()\n    response.use {\n      assertEquals(200, response.code)\n      assertEquals(Protocol.HTTP_1_1, response.protocol)\n    }\n  }\n\n  @Test\n  fun testCustomTrustManagerWithAndroidCheck() {\n    assumeNetwork()\n\n    var withHostCalled = false\n    var withoutHostCalled = false\n    val trustManager =\n      object : X509TrustManager {\n        override fun checkClientTrusted(\n          chain: Array<out X509Certificate>?,\n          authType: String?,\n        ) {}\n\n        override fun checkServerTrusted(\n          chain: Array<out X509Certificate>?,\n          authType: String?,\n        ) {\n          withoutHostCalled = true\n        }\n\n        // called by Android via reflection in X509TrustManagerExtensions\n        @Suppress(\"unused\", \"UNUSED_PARAMETER\")\n        fun checkServerTrusted(\n          chain: Array<out X509Certificate>,\n          authType: String,\n          hostname: String,\n        ): List<X509Certificate> {\n          withHostCalled = true\n          return chain.toList()\n        }\n\n        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()\n      }\n\n    val sslContext =\n      Platform.get().newSSLContext().apply {\n        init(null, arrayOf(trustManager), null)\n      }\n    val sslSocketFactory = sslContext.socketFactory\n\n    val hostnameVerifier = HostnameVerifier { _, _ -> true }\n\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(sslSocketFactory, trustManager)\n        .hostnameVerifier(hostnameVerifier)\n        .build()\n\n    client.get(\"https://www.facebook.com/robots.txt\")\n\n    if (Build.VERSION.SDK_INT < 24) {\n      assertFalse(withHostCalled)\n      assertTrue(withoutHostCalled)\n    } else {\n      assertTrue(withHostCalled)\n      assertFalse(withoutHostCalled)\n    }\n  }\n\n  @Test\n  fun testUnderscoreRequest() {\n    assumeNetwork()\n\n    val request =\n      Request.Builder().url(\"https://example_underscore_123.s3.amazonaws.com/\").build()\n\n    try {\n      client.newCall(request).execute().close()\n      // Hopefully this passes\n    } catch (ioe: IOException) {\n      // https://github.com/square/okhttp/issues/5840\n      when (ioe.cause) {\n        is IllegalArgumentException -> {\n          assertEquals(\"Android internal error\", ioe.message)\n        }\n\n        is CertificateException -> {\n          assertTrue(ioe.cause?.cause is IllegalArgumentException)\n          assertEquals(\n            true,\n            ioe.cause\n              ?.cause\n              ?.message\n              ?.startsWith(\"Invalid input to toASCII\"),\n          )\n        }\n\n        else -> {\n          throw ioe\n        }\n      }\n    }\n  }\n\n  @Test\n  @Disabled(\"breaks conscrypt test\")\n  fun testBouncyCastleRequest() {\n    assumeNetwork()\n\n    try {\n      Security.insertProviderAt(BouncyCastleProvider(), 1)\n      Security.insertProviderAt(BouncyCastleJsseProvider(), 2)\n\n      var socketClass: String? = null\n\n      val trustManager =\n        TrustManagerFactory\n          .getInstance(TrustManagerFactory.getDefaultAlgorithm())\n          .apply {\n            init(null as KeyStore?)\n          }.trustManagers\n          .first() as X509TrustManager\n\n      val sslContext =\n        Platform.get().newSSLContext().apply {\n          // TODO remove most of this code after https://github.com/bcgit/bc-java/issues/686\n          init(null, arrayOf(trustManager), SecureRandom())\n        }\n\n      client =\n        client\n          .newBuilder()\n          .sslSocketFactory(sslContext.socketFactory, trustManager)\n          .eventListenerFactory(\n            clientTestRule.wrap(\n              object : EventListener() {\n                override fun connectionAcquired(\n                  call: Call,\n                  connection: Connection,\n                ) {\n                  socketClass = connection.socket().javaClass.name\n                }\n              },\n            ),\n          ).build()\n\n      val request = Request.Builder().url(\"https://facebook.com/robots.txt\").build()\n\n      val response = client.newCall(request).execute()\n\n      response.use {\n        assertEquals(Protocol.HTTP_2, response.protocol)\n        assertEquals(200, response.code)\n        assertEquals(\"org.bouncycastle.jsse.provider.ProvSSLSocketWrap\", socketClass)\n        assertEquals(TlsVersion.TLS_1_2, response.handshake?.tlsVersion)\n      }\n    } finally {\n      Security.removeProvider(\"BCJSSE\")\n      Security.removeProvider(\"BC\")\n    }\n  }\n\n  @Test\n  @Disabled(\"TODO: currently logging okhttp3.internal.concurrent.TaskRunner, okhttp3.internal.http2.Http2\")\n  fun testLoggingLevels() {\n    enableTls()\n\n    val testHandler =\n      object : Handler() {\n        val calls = mutableMapOf<String, AtomicInteger>()\n\n        override fun publish(record: LogRecord) {\n          calls\n            .getOrPut(record.loggerName) { AtomicInteger(0) }\n            .incrementAndGet()\n        }\n\n        override fun flush() {\n        }\n\n        override fun close() {\n        }\n      }.apply {\n        level = Level.FINEST\n      }\n\n    Logger\n      .getLogger(\"\")\n      .addHandler(testHandler)\n    Logger\n      .getLogger(\"okhttp3\")\n      .addHandler(testHandler)\n    Logger\n      .getLogger(Http2::class.java.name)\n      .addHandler(testHandler)\n    Logger\n      .getLogger(TaskRunner::class.java.name)\n      .addHandler(testHandler)\n    Logger\n      .getLogger(OkHttpClient::class.java.name)\n      .addHandler(testHandler)\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n\n    val response =\n      client\n        .newCall(request)\n        .execute()\n\n    response.use {\n      assertEquals(200, response.code)\n      assertEquals(Protocol.HTTP_2, response.protocol)\n    }\n\n    // Only logs to the test logger above\n    // Will fail if \"adb shell setprop log.tag.okhttp.Http2 DEBUG\" is called\n    assertEquals(setOf(OkHttpTest::class.java.name), testHandler.calls.keys)\n  }\n\n  fun testCachedRequest() {\n    enableTls()\n\n    server.enqueue(MockResponse(body = \"abc\", headers = Headers.headersOf(\"cache-control\", \"public, max-age=3\")))\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val ctxt = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext\n\n    val cacheSize = 1L * 1024 * 1024 // 1MB\n    val cache = Cache(ctxt.cacheDir.resolve(\"testCache\"), cacheSize)\n\n    try {\n      client =\n        client\n          .newBuilder()\n          .cache(cache)\n          .build()\n\n      val request =\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build()\n\n      client\n        .newCall(request)\n        .execute()\n        .use {\n          assertEquals(200, it.code)\n          assertNull(it.cacheResponse)\n          assertNotNull(it.networkResponse)\n\n          assertEquals(3, it.cacheControl.maxAgeSeconds)\n          assertTrue(it.cacheControl.isPublic)\n        }\n\n      client\n        .newCall(request)\n        .execute()\n        .use {\n          assertEquals(200, it.code)\n          assertNotNull(it.cacheResponse)\n          assertNull(it.networkResponse)\n        }\n\n      assertEquals(1, cache.hitCount())\n      assertEquals(1, cache.networkCount())\n      assertEquals(2, cache.requestCount())\n    } finally {\n      cache.delete()\n    }\n  }\n\n  private fun OkHttpClient.get(url: String) {\n    val request = Request.Builder().url(url).build()\n    val response = this.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n    }\n  }\n\n  fun buildCloudflareIp(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://1.1.1.1/dns-query\".toHttpUrl())\n      .build()\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n\n  private fun assumeNetwork() {\n    try {\n      InetAddress.getByName(\"www.google.com\")\n    } catch (uhe: UnknownHostException) {\n      throw TestAbortedException(uhe.message, uhe)\n    }\n  }\n\n  fun OkHttpClient.close() {\n    dispatcher.executorService.shutdown()\n    connectionPool.evictAll()\n  }\n}\n"
  },
  {
    "path": "android-test/src/androidDeviceTest/java/okhttp/android/test/SingleAndroidTest.kt",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.test\n\nimport java.util.concurrent.TimeUnit\nimport kotlin.test.assertEquals\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport okhttp3.ConnectionPool\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport org.junit.jupiter.api.Test\n\n/**\n * This single Junit 4 test is our Android test suite on API 21-25.\n */\nclass SingleAndroidTest {\n  private val handshakeCertificates = localhost()\n\n  private var client: OkHttpClient =\n    OkHttpClient\n      .Builder()\n      .sslSocketFactory(\n        handshakeCertificates.sslSocketFactory(),\n        handshakeCertificates.trustManager,\n      ).connectionPool(ConnectionPool(0, 1, TimeUnit.SECONDS))\n      .build()\n\n  private val server =\n    MockWebServer()\n\n  @Test\n  fun testHttpsRequest() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n\n    server.enqueue(MockResponse())\n    server.start()\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n    }\n\n    while (client.connectionPool.connectionCount() > 0) {\n      Thread.sleep(1000)\n    }\n  }\n\n  @Test\n  fun testHttpRequest() {\n    server.enqueue(MockResponse())\n    server.start()\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n    }\n\n    while (client.connectionPool.connectionCount() > 0) {\n      Thread.sleep(1000)\n    }\n  }\n}\n"
  },
  {
    "path": "android-test/src/androidDeviceTest/java/okhttp/android/test/StrictModeTest.kt",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.test\n\nimport android.os.StrictMode\nimport android.os.StrictMode.ThreadPolicy\nimport android.os.strictmode.Violation\nimport androidx.test.filters.SdkSuppress\nimport assertk.assertThat\nimport assertk.assertions.hasSize\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.internal.platform.Platform\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.parallel.Isolated\n\n@Isolated\n@SdkSuppress(minSdkVersion = 28)\nclass StrictModeTest {\n  private val violations = mutableListOf<Violation>()\n\n  @AfterEach\n  fun cleanup() {\n    StrictMode.setThreadPolicy(\n      ThreadPolicy\n        .Builder()\n        .permitAll()\n        .build(),\n    )\n  }\n\n  @Test\n  fun testInit() {\n    Platform.resetForTests()\n\n    applyStrictMode()\n\n    // Not currently safe\n    // See https://github.com/square/okhttp/pull/8248\n    OkHttpClient()\n\n    assertThat(violations).hasSize(1)\n    assertThat(violations[0].message).isEqualTo(\"newSSLContext\")\n  }\n\n  @Test\n  fun testNewCall() {\n    Platform.resetForTests()\n\n    val client = OkHttpClient()\n\n    applyStrictMode()\n\n    // Safe on main\n    client.newCall(Request(\"https://google.com/robots.txt\".toHttpUrl()))\n\n    assertThat(violations).isEmpty()\n  }\n\n  private fun applyStrictMode() {\n    StrictMode.setThreadPolicy(\n      ThreadPolicy\n        .Builder()\n        .detectCustomSlowCalls()\n        .penaltyListener({ it.run() }) {\n          violations.add(it)\n        }.build(),\n    )\n  }\n}\n"
  },
  {
    "path": "android-test/src/androidDeviceTest/java/okhttp/android/test/alpn/AlpnOverrideTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.test.alpn\n\nimport android.os.Build\nimport android.util.Log\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.Call\nimport okhttp3.Connection\nimport okhttp3.ConnectionSpec\nimport okhttp3.DelegatingSSLSocketFactory\nimport okhttp3.EventListener\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\n\n/**\n * Tests for ALPN overriding on Android.\n */\n@Tag(\"Remote\")\nclass AlpnOverrideTest {\n  class CustomSSLSocketFactory(\n    delegate: SSLSocketFactory,\n  ) : DelegatingSSLSocketFactory(delegate) {\n    override fun configureSocket(sslSocket: SSLSocket): SSLSocket {\n      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n        val parameters = sslSocket.sslParameters\n        Log.d(\"CustomSSLSocketFactory\", \"old applicationProtocols: $parameters.applicationProtocols\")\n        parameters.applicationProtocols = arrayOf(\"x-amzn-http-ca\")\n        sslSocket.sslParameters = parameters\n      }\n\n      return sslSocket\n    }\n  }\n\n  var client = OkHttpClient()\n\n  @Test\n  fun getWithCustomSocketFactory() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(CustomSSLSocketFactory(client.sslSocketFactory), client.x509TrustManager!!)\n        .connectionSpecs(\n          listOf(\n            ConnectionSpec\n              .Builder(ConnectionSpec.MODERN_TLS)\n              .supportsTlsExtensions(false)\n              .build(),\n          ),\n        ).eventListener(\n          object : EventListener() {\n            override fun connectionAcquired(\n              call: Call,\n              connection: Connection,\n            ) {\n              val sslSocket = connection.socket() as SSLSocket\n              println(\"Requested \" + sslSocket.sslParameters.applicationProtocols.joinToString())\n              println(\"Negotiated \" + sslSocket.applicationProtocol)\n            }\n          },\n        ).build()\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://www.google.com\")\n        .build()\n    client.newCall(request).execute().use { response ->\n      assertThat(response.code).isEqualTo(200)\n    }\n  }\n}\n"
  },
  {
    "path": "android-test/src/androidDeviceTest/java/okhttp/android/test/letsencrypt/LetsEncryptClientTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.test.letsencrypt\n\nimport android.os.Build\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.security.cert.X509Certificate\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.decodeCertificatePem\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\n\n/**\n * Test for new Let's Encrypt Root Certificate.\n */\n@Tag(\"Remote\")\nclass LetsEncryptClientTest {\n  @Test fun get() {\n    // These tests wont actually run before Android 8.0 as per\n    // https://github.com/mannodermaus/android-junit5\n    // Raised https://github.com/mannodermaus/android-junit5/issues/228 to reevaluate\n    val androidMorEarlier = Build.VERSION.SDK_INT <= 23\n\n    val clientBuilder = OkHttpClient.Builder()\n\n    if (androidMorEarlier) {\n      val cert: X509Certificate =\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n        TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n        cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n        WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n        ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n        MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n        h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n        0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n        A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n        T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n        B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n        B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n        KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n        OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n        jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n        qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n        rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n        HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n        hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n        ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n        3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n        NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n        ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n        TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n        jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n        oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n        4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n        mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n        emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent().decodeCertificatePem()\n\n      val handshakeCertificates =\n        HandshakeCertificates\n          .Builder()\n          // TODO reenable in official answers\n//      .addPlatformTrustedCertificates()\n          .addTrustedCertificate(cert)\n          .build()\n\n      clientBuilder\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        )\n    }\n\n    val client = clientBuilder.build()\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://valid-isrgrootx1.letsencrypt.org/robots.txt\")\n        .build()\n    client.newCall(request).execute().use { response ->\n      assertThat(response.code).isEqualTo(404)\n      assertThat(response.protocol).isEqualTo(Protocol.HTTP_2)\n    }\n  }\n}\n"
  },
  {
    "path": "android-test/src/androidDeviceTest/java/okhttp/android/test/sni/SniOverrideTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.test.sni\n\nimport android.os.Build\nimport android.util.Log\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.isEqualTo\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SNIHostName\nimport javax.net.ssl.SNIServerName\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.DelegatingSSLSocketFactory\nimport okhttp3.Dns\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\n\n/**\n * Tests for SNI overriding on Android.\n */\n@Tag(\"Remote\")\nclass SniOverrideTest {\n  var client =\n    OkHttpClient\n      .Builder()\n      .build()\n\n  @Test\n  fun getWithCustomSocketFactory() {\n    assumeTrue(Build.VERSION.SDK_INT >= 24)\n\n    class CustomSSLSocketFactory(\n      delegate: SSLSocketFactory,\n    ) : DelegatingSSLSocketFactory(delegate) {\n      override fun configureSocket(sslSocket: SSLSocket): SSLSocket {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n          val parameters = sslSocket.sslParameters\n          val sni = parameters.serverNames\n          Log.d(\"CustomSSLSocketFactory\", \"old SNI: $sni\")\n          parameters.serverNames = mutableListOf<SNIServerName>(SNIHostName(\"cloudflare-dns.com\"))\n          sslSocket.sslParameters = parameters\n        }\n\n        return sslSocket\n      }\n    }\n\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(CustomSSLSocketFactory(client.sslSocketFactory), client.x509TrustManager!!)\n        .hostnameVerifier { hostname, session ->\n          val s = \"hostname: $hostname peerHost:${session.peerHost}\"\n          Log.d(\"SniOverrideTest\", s)\n          try {\n            val cert = session.peerCertificates[0] as X509Certificate\n            for (name in cert.subjectAlternativeNames) {\n              if (name[0] as Int == 2) {\n                Log.d(\"SniOverrideTest\", \"cert: \" + name[1])\n              }\n            }\n            true\n          } catch (e: Exception) {\n            false\n          }\n        }.build()\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://sni.cloudflaressl.com/cdn-cgi/trace\")\n        .header(\"Host\", \"cloudflare-dns.com\")\n        .build()\n    client.newCall(request).execute().use { response ->\n      assertThat(response.code).isEqualTo(200)\n      assertThat(response.protocol).isEqualTo(Protocol.HTTP_2)\n\n      assertThat(response.body.string()).contains(\"h=cloudflare-dns.com\")\n    }\n  }\n\n  @Test\n  fun getWithDns() {\n    client =\n      client\n        .newBuilder()\n        .dns {\n          Dns.SYSTEM.lookup(\"sni.cloudflaressl.com\")\n        }.build()\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://cloudflare-dns.com/cdn-cgi/trace\")\n        .build()\n    client.newCall(request).execute().use { response ->\n      assertThat(response.code).isEqualTo(200)\n      assertThat(response.protocol).isEqualTo(Protocol.HTTP_2)\n      assertThat(response.body.string()).contains(\"h=cloudflare-dns.com\")\n    }\n  }\n}\n"
  },
  {
    "path": "android-test/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\" package=\"okhttp.android.test\">\n\n  <uses-permission android:name=\"android.permission.INTERNET\" />\n  <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\n  <application android:usesCleartextTraffic=\"true\" tools:targetApi=\"m\"/>\n\n</manifest>\n"
  },
  {
    "path": "android-test/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"app_name\">android-test</string>\n</resources>\n"
  },
  {
    "path": "android-test/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n  <base-config cleartextTrafficPermitted=\"false\">\n  </base-config>\n</network-security-config>"
  },
  {
    "path": "android-test/src/test/kotlin/okhttp/android/test/AndroidLoggingTest.kt",
    "content": "/*\n * Copyright (c) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage okhttp.android.test\n\nimport android.util.Log\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.containsOnly\nimport assertk.assertions.isNull\nimport java.net.UnknownHostException\nimport okhttp3.ConnectionSpec\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.internal.platform.AndroidPlatform\nimport okhttp3.logging.HttpLoggingInterceptor\nimport okhttp3.logging.LoggingEventListener\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.robolectric.RobolectricTestRunner\nimport org.robolectric.shadows.ShadowLog\n\n@RunWith(RobolectricTestRunner::class)\nclass AndroidLoggingTest {\n  val clientBuilder =\n    OkHttpClient.Builder().connectionSpecs(listOf(ConnectionSpec.CLEARTEXT)).dns {\n      throw UnknownHostException(\"shortcircuit\")\n    }\n\n  val request = Request(\"http://google.com/robots.txt\".toHttpUrl())\n\n  @Test\n  fun testHttpLoggingInterceptor() {\n    val interceptor =\n      HttpLoggingInterceptor().apply {\n        level = HttpLoggingInterceptor.Level.BASIC\n      }\n\n    val client = clientBuilder.addInterceptor(interceptor).build()\n\n    try {\n      client.newCall(request).execute()\n    } catch (uhe: UnknownHostException) {\n      // expected\n    }\n\n    val logs = ShadowLog.getLogsForTag(AndroidPlatform.Tag)\n    assertThat(logs.map { it.type }).containsOnly(Log.INFO)\n    assertThat(\n      logs.map {\n        it.msg.replace(\n          \"\\\\d+\".toRegex(),\n          \"\",\n        )\n      },\n    ).containsExactly(\n      \"--> GET http://google.com/robots.txt\",\n      \"<-- HTTP FAILED: java.net.UnknownHostException: shortcircuit. ${request.url} (ms)\",\n    )\n    // We should consider if these logs should retain Exceptions\n    assertThat(logs.last().throwable).isNull()\n  }\n\n  @Test\n  fun testLoggingEventListener() {\n    val client = clientBuilder.eventListenerFactory(LoggingEventListener.Factory()).build()\n\n    try {\n      client.newCall(request).execute()\n    } catch (uhe: UnknownHostException) {\n      // expected\n    }\n\n    val logs = ShadowLog.getLogsForTag(AndroidPlatform.Tag)\n    assertThat(logs.map { it.type }).containsOnly(Log.INFO)\n    assertThat(\n      logs.map {\n        it.msg.replace(\n          \"\\\\[\\\\d+ ms] \".toRegex(),\n          \"\",\n        )\n      },\n    ).containsExactly(\n      \"callStart: Request{method=GET, url=http://google.com/robots.txt}\",\n      \"proxySelectStart: http://google.com/\",\n      \"proxySelectEnd: [DIRECT]\",\n      \"dnsStart: google.com\",\n      \"callFailed: java.net.UnknownHostException: shortcircuit\",\n    )\n    // We should consider if these logs should retain Exceptions\n    assertThat(logs.last().throwable).isNull()\n  }\n}\n"
  },
  {
    "path": "android-test/src/test/kotlin/okhttp/android/test/AndroidSocketAdapterTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.test\n\nimport java.security.Provider\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport okhttp3.DelegatingSSLSocket\nimport okhttp3.DelegatingSSLSocketFactory\nimport okhttp3.Protocol.HTTP_1_1\nimport okhttp3.Protocol.HTTP_2\nimport okhttp3.internal.platform.android.AndroidSocketAdapter\nimport okhttp3.internal.platform.android.ConscryptSocketAdapter\nimport okhttp3.internal.platform.android.DeferredSocketAdapter\nimport okhttp3.internal.platform.android.SocketAdapter\nimport okhttp3.internal.platform.android.StandardAndroidSocketAdapter\nimport org.conscrypt.Conscrypt\nimport org.junit.Assert.assertFalse\nimport org.junit.Assert.assertNotNull\nimport org.junit.Assert.assertNull\nimport org.junit.Assert.assertTrue\nimport org.junit.Assume.assumeFalse\nimport org.junit.Assume.assumeTrue\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.robolectric.ParameterizedRobolectricTestRunner\nimport org.robolectric.ParameterizedRobolectricTestRunner.Parameters\n\n@RunWith(ParameterizedRobolectricTestRunner::class)\nclass AndroidSocketAdapterTest(\n  val adapter: SocketAdapter,\n) {\n  val context: SSLContext by lazy {\n    val provider: Provider = Conscrypt.newProviderBuilder().provideTrustManager(true).build()\n\n    SSLContext.getInstance(\"TLS\", provider).apply {\n      init(null, null, null)\n    }\n  }\n\n  @Test\n  fun testMatchesSupportedSocket() {\n    val socketFactory = context.socketFactory\n\n    val sslSocket = socketFactory.createSocket() as SSLSocket\n    assertTrue(adapter.matchesSocket(sslSocket))\n\n    adapter.configureTlsExtensions(sslSocket, null, listOf(HTTP_2, HTTP_1_1))\n    // not connected\n    assertNull(adapter.getSelectedProtocol(sslSocket))\n  }\n\n  @Test\n  fun testMatchesSupportedAndroidSocketFactory() {\n    assumeTrue(adapter is StandardAndroidSocketAdapter)\n\n    assertTrue(adapter.matchesSocketFactory(context.socketFactory))\n    assertNotNull(adapter.trustManager(context.socketFactory))\n  }\n\n  @Test\n  fun testDoesntMatchSupportedCustomSocketFactory() {\n    assumeFalse(adapter is StandardAndroidSocketAdapter)\n\n    assertFalse(adapter.matchesSocketFactory(context.socketFactory))\n    assertNull(adapter.trustManager(context.socketFactory))\n  }\n\n  @Test\n  fun testCustomSocket() {\n    val socketFactory = DelegatingSSLSocketFactory(context.socketFactory)\n\n    assertFalse(adapter.matchesSocketFactory(socketFactory))\n\n    val sslSocket =\n      object : DelegatingSSLSocket(context.socketFactory.createSocket() as SSLSocket) {}\n    assertFalse(adapter.matchesSocket(sslSocket))\n\n    adapter.configureTlsExtensions(sslSocket, null, listOf(HTTP_2, HTTP_1_1))\n    // not connected\n    assertNull(adapter.getSelectedProtocol(sslSocket))\n  }\n\n  companion object {\n    @JvmStatic\n    @Parameters(name = \"{0}\")\n    fun data(): Collection<SocketAdapter> =\n      listOfNotNull(\n        DeferredSocketAdapter(ConscryptSocketAdapter.factory),\n        DeferredSocketAdapter(AndroidSocketAdapter.factory(\"org.conscrypt\")),\n        StandardAndroidSocketAdapter.buildIfSupported(\"org.conscrypt\"),\n      )\n  }\n}\n"
  },
  {
    "path": "android-test/src/test/kotlin/okhttp/android/test/BaseOkHttpClientUnitTest.kt",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage okhttp.android.test\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport okhttp3.Cache\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okio.Path.Companion.toPath\nimport okio.fakefilesystem.FakeFileSystem\nimport org.junit.AssumptionViolatedException\nimport org.junit.Before\nimport org.junit.Test\n\nabstract class BaseOkHttpClientUnitTest {\n  private lateinit var client: OkHttpClient\n\n  @Before\n  fun setUp() {\n    client =\n      OkHttpClient\n        .Builder()\n        .cache(Cache(FakeFileSystem(), \"/cache\".toPath(), 10_000_000))\n        .build()\n  }\n\n  @Test\n  fun testRequestExternal() {\n    assumeNetwork()\n\n    val request = Request(\"https://www.google.com/robots.txt\".toHttpUrl())\n\n    val networkRequest =\n      request\n        .newBuilder()\n        .build()\n\n    val call = client.newCall(networkRequest)\n\n    call.execute().use { response ->\n      assertThat(response.code).isEqualTo(200)\n      assertThat(response.cacheResponse).isNull()\n    }\n\n    val cachedCall = client.newCall(request)\n\n    cachedCall.execute().use { response ->\n      assertThat(response.code).isEqualTo(200)\n      assertThat(response.cacheResponse).isNotNull()\n    }\n  }\n\n  @Test\n  open fun testPublicSuffixDb() {\n    val httpUrl = \"https://www.google.co.uk\".toHttpUrl()\n    assertThat(httpUrl.topPrivateDomain()).isEqualTo(\"google.co.uk\")\n  }\n\n  private fun assumeNetwork() {\n    try {\n      InetAddress.getByName(\"www.google.com\")\n    } catch (uhe: UnknownHostException) {\n      throw AssumptionViolatedException(uhe.message, uhe)\n    }\n  }\n}\n"
  },
  {
    "path": "android-test/src/test/kotlin/okhttp/android/test/DisabledInitialiserTest.kt",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage okhttp.android.test\n\nimport assertk.all\nimport assertk.assertFailure\nimport assertk.assertions.cause\nimport assertk.assertions.hasClass\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isNotNull\nimport java.io.IOException\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.PlatformRegistry\nimport org.junit.AfterClass\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.robolectric.RobolectricTestRunner\nimport org.robolectric.annotation.Config\n\n@RunWith(RobolectricTestRunner::class)\n@Config(\n  sdk = [23, 26, 30, 33, 35],\n)\nclass DisabledInitialiserTest {\n  @Before\n  fun setContext() {\n    // Ensure we aren't succeeding because of another test\n    Platform.resetForTests()\n    PlatformRegistry.applicationContext = null\n  }\n\n  @Test\n  fun testWithoutContext() {\n    val httpUrl = \"https://www.google.co.uk\".toHttpUrl()\n    assertFailure { httpUrl.topPrivateDomain() }.all {\n      hasMessage(\"Unable to load PublicSuffixDatabase.list resource.\")\n      cause().isNotNull().all {\n        hasMessage(\n          \"Platform applicationContext not initialized. \" +\n            \"Startup Initializer possibly disabled, \" +\n            \"call OkHttp.initialize before test.\",\n        )\n        hasClass<IOException>()\n      }\n    }\n  }\n\n  companion object {\n    @AfterClass\n    @JvmStatic\n    fun resetContext() {\n      // Ensure we don't make other tests fail\n      Platform.resetForTests()\n    }\n  }\n}\n"
  },
  {
    "path": "android-test/src/test/kotlin/okhttp/android/test/NonRobolectricOkHttpClientTest.kt",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage okhttp.android.test\n\nimport assertk.all\nimport assertk.assertFailure\nimport assertk.assertions.cause\nimport assertk.assertions.hasClass\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isNotNull\nimport java.io.IOException\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.junit.runners.JUnit4\n\n/**\n * Android test running with only stubs.\n */\n@RunWith(JUnit4::class)\nclass NonRobolectricOkHttpClientTest : BaseOkHttpClientUnitTest() {\n  @Test\n  override fun testPublicSuffixDb() {\n    assertFailure { super.testPublicSuffixDb() }.all {\n      hasMessage(\"Unable to load PublicSuffixDatabase.list resource.\")\n      cause().isNotNull().all {\n        hasMessage(\n          \"Platform applicationContext not initialized. \" +\n            \"Possibly running Android unit test without Robolectric. \" +\n            \"Android tests should run with Robolectric \" +\n            \"and call OkHttp.initialize before test\",\n        )\n        hasClass<IOException>()\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "android-test/src/test/kotlin/okhttp/android/test/RobolectricOkHttpClientTest.kt",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage okhttp.android.test\n\nimport androidx.test.core.app.ApplicationProvider\nimport okhttp3.OkHttp\nimport org.junit.Before\nimport org.junit.runner.RunWith\nimport org.robolectric.RobolectricTestRunner\nimport org.robolectric.annotation.Config\n\n@RunWith(RobolectricTestRunner::class)\n@Config(\n  sdk = [23, 26, 30, 33, 35],\n)\nclass RobolectricOkHttpClientTest : BaseOkHttpClientUnitTest() {\n  @Before\n  fun setContext() {\n    // This is awkward because Robolectric won't run our initializers and we don't want test deps\n    // https://github.com/robolectric/robolectric/issues/8461\n    OkHttp.initialize(ApplicationProvider.getApplicationContext())\n  }\n}\n"
  },
  {
    "path": "android-test/src/test/kotlin/okhttp/android/test/ShadowDnsResolver.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.test\n\nimport android.net.DnsResolver\nimport android.net.Network\nimport android.os.CancellationSignal\nimport java.net.InetAddress\nimport java.util.concurrent.Executor\nimport org.robolectric.annotation.Implementation\nimport org.robolectric.annotation.Implements\nimport org.robolectric.shadow.api.Shadow\n\n@Implements(DnsResolver::class)\nclass ShadowDnsResolver {\n  var responder: (Request) -> Unit = {\n    it.callback.onAnswer(listOf(), 0)\n  }\n\n  data class Request(\n    val network: Network?,\n    val domain: String,\n    val nsType: Int,\n    val flags: Int,\n    val callback: DnsResolver.Callback<List<InetAddress>>,\n  )\n\n  @Implementation\n  fun query(\n    network: Network?,\n    domain: String,\n    nsType: Int,\n    flags: Int,\n    executor: Executor,\n    cancellationSignal: CancellationSignal?,\n    callback: DnsResolver.Callback<List<InetAddress>>,\n  ) {\n    responder(Request(network, domain, nsType, flags, callback))\n  }\n\n  companion object {\n    @Implementation\n    @JvmStatic\n    fun getInstance(): DnsResolver = Shadow.newInstance(DnsResolver::class.java, arrayOf(), arrayOf())\n  }\n}\n"
  },
  {
    "path": "android-test-app/build.gradle.kts",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\nimport okhttp3.buildsupport.testJavaVersion\n\n\nplugins {\n  id(\"okhttp.base-conventions\")\n  id(\"com.android.application\")\n}\n\nandroid {\n  compileSdk = 36\n\n  namespace = \"okhttp.android.testapp\"\n\n  // Release APKs can't be tested currently with AGP\n  testBuildType = \"debug\"\n\n  defaultConfig {\n    minSdk = 21\n    targetSdk = 36\n    testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n  }\n\n  compileOptions {\n    targetCompatibility(JavaVersion.VERSION_11)\n    sourceCompatibility(JavaVersion.VERSION_11)\n  }\n\n\n  buildTypes {\n    release {\n      isShrinkResources = true\n      isMinifyEnabled = true\n      signingConfig = signingConfigs.getByName(\"debug\")\n      setProguardFiles(listOf(getDefaultProguardFile(\"proguard-android-optimize.txt\"), \"proguard-rules.pro\"))\n      testProguardFiles(\"test-proguard-rules.pro\")\n    }\n  }\n\n  lint {\n    abortOnError = true\n  }\n}\n\ndependencies {\n  implementation(libs.playservices.safetynet)\n  implementation(projects.okhttp)\n  implementation(libs.androidx.activity)\n\n  androidTestImplementation(libs.androidx.junit)\n  androidTestImplementation(libs.androidx.espresso.core)\n  androidTestImplementation(libs.androidx.test.runner)\n  androidTestImplementation(libs.assertk)\n}\n"
  },
  {
    "path": "android-test-app/proguard-rules.pro",
    "content": "# no rules should be needed\n\n-whyareyoukeeping class okhttp3.internal.idn.IdnaMappingTable {\n  *;\n}\n"
  },
  {
    "path": "android-test-app/src/androidTest/kotlin/okhttp/android/testapp/IdnaTest.kt",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.android\n\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\nclass IdnaTest {\n  @Test\n  fun testHostnameFunction() {\n    assertEquals(\"xn--n3h.net\", \"https://☃.net/robots.txt\".toHttpUrl().host)\n  }\n}\n"
  },
  {
    "path": "android-test-app/src/androidTest/kotlin/okhttp/android/testapp/PublicSuffixDatabaseTest.kt",
    "content": "/*\n * Copyright (C) 2023 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.android\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport org.junit.Test\n\n/**\n * Run with \"./gradlew :android-test-app:connectedCheck -PandroidBuild=true\" and make sure ANDROID_SDK_ROOT is set.\n */\nclass PublicSuffixDatabaseTest {\n  @Test\n  fun testTopLevelDomain() {\n    assertThat(\"https://www.google.com/robots.txt\".toHttpUrl().topPrivateDomain()).isEqualTo(\"google.com\")\n  }\n}\n"
  },
  {
    "path": "android-test-app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\" package=\"okhttp.android.testapp\">\n\n  <uses-permission android:name=\"android.permission.INTERNET\" />\n\n  <application\n    android:allowBackup=\"true\"\n    android:label=\"@string/app_name\"\n    android:supportsRtl=\"true\"\n    android:name=\".TestApplication\"\n  >\n    <activity\n      android:name=\".MainActivity\"\n      android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\" />\n\n        <category android:name=\"android.intent.category.LAUNCHER\" />\n      </intent-filter>\n    </activity>\n\n    <activity\n      android:name=\".MainActivity2\"\n      android:process=\":activity2\"\n      android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\" />\n\n        <category android:name=\"android.intent.category.LAUNCHER\" />\n      </intent-filter>\n    </activity>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "android-test-app/src/main/kotlin/okhttp/android/testapp/MainActivity.kt",
    "content": "/*\n * Copyright (C) 2023 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.testapp\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.platform.AndroidPlatform\nimport okio.IOException\n\nopen class MainActivity : ComponentActivity() {\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n\n    val client = OkHttpClient()\n\n    // Ensure we are compiling against the right variant\n    println(AndroidPlatform.isSupported)\n\n    val url = \"https://github.com/square/okhttp\".toHttpUrl()\n    println(url.topPrivateDomain())\n\n    client.newCall(Request(url)).enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          println(\"failed: $e\")\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          println(\"response: ${response.code}\")\n          response.close()\n        }\n      },\n    )\n  }\n}\n"
  },
  {
    "path": "android-test-app/src/main/kotlin/okhttp/android/testapp/MainActivity2.kt",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.testapp\n\nclass MainActivity2 : MainActivity()\n"
  },
  {
    "path": "android-test-app/src/main/kotlin/okhttp/android/testapp/TestApplication.kt",
    "content": "/*\n * Copyright (C) 2023 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.android.testapp\n\nimport android.annotation.SuppressLint\nimport android.app.Application\nimport android.os.Build\nimport okhttp3.OkHttp\n\nclass TestApplication : Application() {\n  override fun onCreate() {\n    super.onCreate()\n\n    if (isSecondaryProcess()) {\n      OkHttp.initialize(applicationContext)\n    }\n  }\n\n  private fun isSecondaryProcess(): Boolean = getProcess() != packageName\n\n  @SuppressLint(\"DiscouragedPrivateApi\")\n  private fun getProcess(): String? =\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n      getProcessName()\n    } else {\n      Class\n        .forName(\"android.app.ActivityThread\")\n        .getDeclaredMethod(\"currentProcessName\")\n        .apply { isAccessible = true }\n        .invoke(null) as String\n    }\n}\n"
  },
  {
    "path": "android-test-app/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"app_name\">android-test</string>\n</resources>\n"
  },
  {
    "path": "android-test-app/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n  <base-config cleartextTrafficPermitted=\"false\">\n  </base-config>\n</network-security-config>\n"
  },
  {
    "path": "android-test-app/test-proguard-rules.pro",
    "content": "-dontwarn **\n"
  },
  {
    "path": "build-logic/build.gradle.kts",
    "content": "plugins {\n  `kotlin-dsl`\n  id(\"com.diffplug.spotless\") version \"8.4.0\"\n}\n\nconfigure<com.diffplug.gradle.spotless.SpotlessExtension> {\n  kotlin {\n    target(\"src/**/*.kt\")\n    ktlint()\n  }\n  kotlinGradle {\n    target(\"*.kts\")\n    targetExclude(\"build/**/*.kts\")\n    ktlint()\n  }\n}\n\nrepositories {\n  google()\n  mavenCentral()\n  gradlePluginPortal()\n}\n\ndependencies {\n  implementation(libs.gradlePlugin.kotlin)\n  implementation(libs.gradlePlugin.mavenPublish)\n  implementation(libs.gradlePlugin.dokka)\n  implementation(libs.gradlePlugin.binaryCompatibilityValidator)\n  implementation(libs.gradlePlugin.android)\n  implementation(libs.gradlePlugin.bnd)\n  implementation(libs.aqute.bnd)\n  implementation(libs.gradlePlugin.animalsniffer)\n  implementation(libs.gradlePlugin.spotless)\n  implementation(libs.gradlePlugin.shadow)\n  implementation(libs.gradlePlugin.graalvm)\n  implementation(libs.gradlePlugin.ksp)\n  implementation(libs.gradlePlugin.mrjar)\n  implementation(libs.gradlePlugin.tapmoc)\n  implementation(libs.androidx.lint.gradle)\n  implementation(libs.kotlin.gradle.plugin.api)\n}\n"
  },
  {
    "path": "build-logic/settings.gradle.kts",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npluginManagement {\n  repositories {\n    mavenCentral()\n    gradlePluginPortal()\n    google()\n  }\n}\nenableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\nrootProject.name = \"build-logic\"\n\ndependencyResolutionManagement {\n  repositories {\n    mavenCentral()\n    gradlePluginPortal()\n    google()\n  }\n  versionCatalogs {\n    create(\"libs\") {\n      from(files(\"../gradle/libs.versions.toml\"))\n    }\n  }\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/AlpnVersions.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// https://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-versions\nprivate fun alpnBootVersionForPatchVersion(patchVersion: Int): String? =\n  when (patchVersion) {\n    in 0..24 -> \"8.1.0.v20141016\"\n    in 25..30 -> \"8.1.2.v20141202\"\n    in 31..50 -> \"8.1.3.v20150130\"\n    in 51..59 -> \"8.1.4.v20150727\"\n    in 60..64 -> \"8.1.5.v20150921\"\n    in 65..70 -> \"8.1.6.v20151105\"\n    in 71..77 -> \"8.1.7.v20160121\"\n    in 78..101 -> \"8.1.8.v20160420\"\n    in 102..111 -> \"8.1.9.v20160720\"\n    in 112..120 -> \"8.1.10.v20161026\"\n    in 121..160 -> \"8.1.11.v20170118\"\n    in 161..181 -> \"8.1.12.v20180117\"\n    in 191..242 -> \"8.1.13.v20181017\"\n    else -> null\n  }\n\n/**\n * Returns the alpn-boot version specific to this OpenJDK 8 JVM, or null if this is not a Java 8 VM.\n * https://github.com/xjdr/xio/blob/master/alpn-boot.gradle\n */\nfun alpnBootVersion(): String? {\n  val version = System.getProperty(\"alpn.boot.version\")\n\n  if (version != null) {\n    return version\n  }\n\n  val javaVersion = System.getProperty(\"java.version\")\n  val match = \"1\\\\.8\\\\.0_(\\\\d+)(-.*)?\".toRegex().find(javaVersion) ?: return null\n\n  return alpnBootVersionForPatchVersion(match.groupValues.first().toInt())\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/BndBuildAction.kt",
    "content": "/*\n * Copyright (c) aQute SARL (2000, 2021). All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport aQute.bnd.gradle.BundleTaskExtension\nimport aQute.bnd.osgi.Builder\nimport aQute.bnd.osgi.Constants\nimport aQute.bnd.osgi.Jar\nimport aQute.bnd.osgi.Processor\nimport aQute.bnd.version.MavenVersion\nimport aQute.lib.io.IO\nimport aQute.lib.utf8properties.UTF8Properties\nimport java.io.File\nimport java.util.Properties\nimport java.util.jar.Manifest\nimport java.util.zip.ZipFile\nimport org.gradle.api.Action\nimport org.gradle.api.GradleException\nimport org.gradle.api.Task\nimport org.gradle.api.file.ConfigurableFileCollection\nimport org.gradle.api.file.FileCollection\nimport org.gradle.api.file.ProjectLayout\nimport org.gradle.api.file.RegularFileProperty\nimport org.gradle.api.provider.MapProperty\nimport org.gradle.api.provider.Provider\nimport org.gradle.api.tasks.bundling.Jar as GradleJar\nimport org.gradle.api.tasks.bundling.ZipEntryCompression\n\n/**\n * A static BuildAction that does not capture the Task instance, enabling Configuration Cache\n * compatibility.\n *\n * This class is based on bundled code from the BND Gradle Plugin.\n * https://github.com/bndtools/bnd/blob/master/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java\n */\nclass BndBuildAction(\n  private val properties: MapProperty<String, Any>,\n  private val classpath: ConfigurableFileCollection,\n  private val sourcepath: FileCollection,\n  private val bundleSymbolicName: Provider<String>,\n  private val bundleVersion: Provider<String>,\n  private val bndfile: RegularFileProperty,\n  private val bnd: Provider<String>,\n  private val layout: ProjectLayout,\n  private val entryCompression: ZipEntryCompression,\n  private val preserveFileTimestamps: Boolean,\n) : Action<Task> {\n  constructor(\n    extension: BundleTaskExtension,\n    task: GradleJar,\n    sourceSet: FileCollection,\n  ) : this(\n    extension.properties,\n    extension.classpath,\n    // Sourcepath default: all source\n    sourceSet,\n    // Symbolic name default logic\n    task.archiveBaseName.zip(task.archiveClassifier) { baseName, classifier ->\n      if (classifier.isNullOrEmpty()) baseName else \"$baseName-$classifier\"\n    },\n    task.archiveVersion.orElse(\"0\").map { version ->\n      MavenVersion.parseMavenString(version).osGiVersion.toString()\n    },\n    extension.bndfile,\n    extension.bnd,\n    task.project.layout,\n    task.entryCompression,\n    task.isPreserveFileTimestamps,\n  )\n\n  override fun execute(task: Task) {\n    task as GradleJar\n    val temporaryDir = task.temporaryDir\n    val projectDir = layout.projectDirectory.asFile\n\n    val gradleProperties = Properties()\n    properties.get().forEach { (k, v) ->\n      if (v is Provider<*>) {\n        val value = v.getOrNull()\n        if (value != null) gradleProperties[k] = value\n      } else {\n        gradleProperties[k] = v\n      }\n    }\n\n    // Set default values if not present\n    if (!gradleProperties.containsKey(Constants.BUNDLE_SYMBOLICNAME)) {\n      gradleProperties[Constants.BUNDLE_SYMBOLICNAME] = bundleSymbolicName.get()\n    }\n    if (!gradleProperties.containsKey(Constants.BUNDLE_VERSION)) {\n      gradleProperties[Constants.BUNDLE_VERSION] = bundleVersion.get()\n    }\n\n    // Do not capture 'task' in gradleProperties to avoid serialization issues\n\n    try {\n      Builder(Processor(gradleProperties, false)).use { builder ->\n        val temporaryBndFile = File.createTempFile(\"bnd\", \".bnd\", temporaryDir)\n        IO.writer(temporaryBndFile).use { writer ->\n          val bndFileVal = bndfile.asFile.getOrNull()\n          if (bndFileVal != null && bndFileVal.isFile) {\n            Processor(gradleProperties).let { p ->\n              p.loadProperties(bndFileVal).store(writer, null)\n            }\n          } else {\n            val bndVal = bnd.getOrElse(\"\")\n            if (bndVal.isNotEmpty()) {\n              val props = UTF8Properties()\n              props.load(bndVal, File(projectDir, \"build.gradle.kts\"), builder)\n              props.replaceHere(projectDir).store(writer, null)\n            }\n          }\n        }\n        builder.setProperties(temporaryBndFile, projectDir)\n\n        builder.setProperty(\"project.output\", temporaryDir.canonicalPath)\n\n        if (builder.`is`(Constants.NOBUNDLES)) return\n\n        val archiveFile = task.archiveFile.get().asFile\n        val archiveFileName = task.archiveFileName.get()\n\n        val archiveCopyFile = File(temporaryDir, archiveFileName)\n        IO.copy(archiveFile, archiveCopyFile)\n\n        val bundleJar = Jar(archiveFileName, archiveCopyFile)\n\n        if (builder.getProperty(Constants.REPRODUCIBLE) == null && !preserveFileTimestamps) {\n          builder.setProperty(Constants.REPRODUCIBLE, \"true\")\n        }\n        if (builder.getProperty(Constants.COMPRESSION) == null) {\n          builder.setProperty(\n            Constants.COMPRESSION,\n            when (entryCompression) {\n              ZipEntryCompression.STORED -> Jar.Compression.STORE.name\n              else -> Jar.Compression.DEFLATE.name\n            },\n          )\n        }\n\n        bundleJar.updateModified(archiveFile.lastModified(), \"time of Jar task generated jar\")\n        bundleJar.manifest = Manifest()\n        builder.setJar(bundleJar)\n\n        val validClasspath = classpath.filter { it.exists() && (it.isDirectory || isZip(it)) }\n        builder.setProperty(\"project.buildpath\", validClasspath.asPath)\n        builder.setClasspath(validClasspath.files.toTypedArray())\n\n        val validSourcepath = sourcepath.filter { it.exists() }\n        builder.setProperty(\"project.sourcepath\", validSourcepath.asPath)\n        builder.setSourcepath(validSourcepath.files.toTypedArray())\n\n        val builtJar = builder.build()\n        if (!builder.isOk) {\n          builder.getErrors().forEach { task.logger.error(\"Error: $it\") }\n          builder.getWarnings().forEach { task.logger.warn(\"Warning: $it\") }\n          throw GradleException(\"Bundle $archiveFileName has errors\")\n        }\n\n        builtJar.write(archiveFile)\n        archiveFile.setLastModified(System.currentTimeMillis())\n      }\n    } catch (e: Exception) {\n      throw GradleException(\"Bnd build failed\", e)\n    }\n  }\n\n  private fun isZip(file: File): Boolean =\n    try {\n      ZipFile(file).close()\n      true\n    } catch (e: Exception) {\n      false\n    }\n\n  companion object {\n    /**\n     * BND is incompatible with Kotlin/Multiplatform because it assumes the JVM source set's name is\n     * 'main'. Work around this by creating a 'main' source set that forwards to 'jvmMain'.\n     */\n    fun installWorkaround(project: org.gradle.api.Project): org.gradle.api.tasks.SourceSet {\n      val sourceSets =\n        project.extensions\n          .getByType(org.gradle.api.plugins.JavaPluginExtension::class.java)\n          .sourceSets\n      val existingMain = sourceSets.findByName(\"main\")\n      if (existingMain != null) return existingMain\n\n      val jvmMainSourceSet = sourceSets.getByName(\"jvmMain\")\n      val mainSourceSet =\n        object : org.gradle.api.tasks.SourceSet by jvmMainSourceSet {\n          override fun getName() = \"main\"\n\n          override fun getProcessResourcesTaskName() = \"${jvmMainSourceSet.processResourcesTaskName}ForFakeMain\"\n\n          override fun getCompileJavaTaskName() = \"${jvmMainSourceSet.compileJavaTaskName}ForFakeMain\"\n\n          override fun getClassesTaskName() = \"${jvmMainSourceSet.classesTaskName}ForFakeMain\"\n\n          override fun getCompileOnlyConfigurationName(): String = jvmMainSourceSet.compileOnlyConfigurationName + \"ForFakeMain\"\n\n          override fun getCompileClasspathConfigurationName(): String = jvmMainSourceSet.compileClasspathConfigurationName + \"ForFakeMain\"\n\n          override fun getImplementationConfigurationName(): String = jvmMainSourceSet.implementationConfigurationName + \"ForFakeMain\"\n\n          override fun getAnnotationProcessorConfigurationName(): String =\n            jvmMainSourceSet.annotationProcessorConfigurationName + \"ForFakeMain\"\n\n          override fun getRuntimeClasspathConfigurationName(): String = jvmMainSourceSet.runtimeClasspathConfigurationName + \"ForFakeMain\"\n\n          override fun getRuntimeOnlyConfigurationName(): String = jvmMainSourceSet.runtimeOnlyConfigurationName + \"ForFakeMain\"\n\n          override fun getTaskName(\n            verb: String?,\n            target: String?,\n          ) = \"${jvmMainSourceSet.getTaskName(verb, target)}ForFakeMain\"\n        }\n      sourceSets.add(mainSourceSet)\n      project.tasks.named { it.endsWith(\"ForFakeMain\") }.configureEach { onlyIf { false } }\n\n      return mainSourceSet\n    }\n  }\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/JavaModules.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport me.champeau.mrjar.MultiReleaseExtension\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPluginExtension\nimport org.gradle.api.tasks.compile.JavaCompile\nimport org.gradle.jvm.toolchain.JavaToolchainService\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.getByType\nimport org.gradle.kotlin.dsl.named\nimport org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile\n\nfun Project.applyJavaModules(\n  moduleName: String,\n  defaultVersion: Int = 8,\n  javaModuleVersion: Int = 9,\n  enableValidation: Boolean = true,\n) {\n  plugins.apply(\"me.champeau.mrjar\")\n\n  configure<MultiReleaseExtension> {\n    targetVersions(defaultVersion, javaModuleVersion)\n  }\n\n  tasks.named<JavaCompile>(\"compileJava9Java\").configure {\n    val compileKotlinTask = tasks.getByName(\"compileKotlin\") as KotlinJvmCompile\n    dependsOn(compileKotlinTask)\n\n    if (enableValidation) {\n      compileKotlinTask.source(file(\"src/main/java9\"))\n    }\n\n    // Ignore warnings about using 'requires transitive' on automatic modules.\n    // not needed when compiling with recent JDKs, e.g. 17\n    options.compilerArgs.add(\"-Xlint:-requires-transitive-automatic\")\n\n    // Patch the compileKotlinJvm output classes into the compilation so exporting packages works correctly.\n    options.compilerArgs.addAll(\n      listOf(\n        \"--patch-module\",\n        \"$moduleName=${compileKotlinTask.destinationDirectory.get().asFile}\",\n      ),\n    )\n\n    classpath = compileKotlinTask.libraries\n    modularity.inferModulePath.set(true)\n\n    val javaToolchains = project.extensions.getByType<JavaToolchainService>()\n    val javaPluginExtension = project.extensions.getByType<JavaPluginExtension>()\n    javaCompiler.set(javaToolchains.compilerFor(javaPluginExtension.toolchain))\n  }\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/Osgi.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport aQute.bnd.gradle.BundleTaskExtension\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.MinimalExternalModuleDependency\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport org.gradle.api.plugins.ExtensionAware\nimport org.gradle.api.tasks.SourceSetContainer\nimport org.gradle.api.tasks.bundling.Jar as GradleJar\nimport org.gradle.kotlin.dsl.dependencies\nimport org.gradle.kotlin.dsl.get\nimport org.gradle.kotlin.dsl.getByName\nimport org.gradle.kotlin.dsl.named\nimport org.gradle.kotlin.dsl.provideDelegate\n\nfun Project.applyOsgi(vararg bndProperties: String) {\n  plugins.withId(\"org.jetbrains.kotlin.jvm\") { applyOsgi(\"jar\", \"osgiApi\", bndProperties) }\n}\n\nprivate fun Project.applyOsgi(\n  jarTaskName: String,\n  osgiApiConfigurationName: String,\n  bndProperties: Array<out String>,\n) {\n  val osgi = project.sourceSets.create(\"osgi\")\n  val osgiApi = project.configurations.getByName(osgiApiConfigurationName)\n\n  project.dependencies { osgiApi(kotlinOsgi) }\n\n  val jarTask = tasks.getByName<GradleJar>(jarTaskName)\n  val bundleExtension =\n    jarTask.extensions.create(\n      BundleTaskExtension.NAME,\n      BundleTaskExtension::class.java,\n      jarTask,\n    )\n  bundleExtension.run {\n    setClasspath(osgi.compileClasspath + sourceSets[\"main\"].compileClasspath)\n    properties.empty()\n    bnd(*bndProperties)\n  }\n  jarTask.doLast(\n    \"buildBundle\",\n    BndBuildAction(bundleExtension, jarTask, sourceSets[\"main\"].allSource),\n  )\n}\n\nfun Project.applyOsgiMultiplatform(vararg bndProperties: String) {\n  // BND is incompatible with Kotlin/Multiplatform because it assumes the JVM source set's\n  // name is\n  // 'main'. Work around this by creating a 'main' source set that forwards to 'jvmMain'.\n  //\n  // The forwarding SourceSet also needs to fake out some task names to prevent them from\n  // being\n  // registered twice.\n  //\n  // https://github.com/bndtools/bnd/issues/6590\n  val mainSourceSet = BndBuildAction.installWorkaround(project)\n\n  val osgiApi = configurations.create(\"osgiApi\")\n  dependencies { osgiApi(kotlinOsgi) }\n\n  tasks.named<GradleJar>(\"jvmJar\").configure {\n    val bundleExtension =\n      extensions.create(\n        BundleTaskExtension.NAME,\n        BundleTaskExtension::class.java,\n        this,\n      )\n    val osgiApiArtifacts = osgiApi.artifacts\n    val jvmMainClasses = tasks.named(\"jvmMainClasses\").map { it.outputs.files }\n    bundleExtension.apply {\n      classpath(osgiApiArtifacts)\n      classpath(jvmMainClasses)\n      properties.empty()\n      bnd(*bndProperties)\n    }\n    doLast(\"buildBundle\", BndBuildAction(bundleExtension, this, mainSourceSet.allSource))\n  }\n}\n\nval Project.sourceSets: SourceSetContainer\n  get() = (this as ExtensionAware).extensions[\"sourceSets\"] as SourceSetContainer\n\nprivate val Project.kotlinOsgi: MinimalExternalModuleDependency\n  get() =\n    extensions\n      .getByType(VersionCatalogsExtension::class.java)\n      .named(\"libs\")\n      .findLibrary(\"kotlin.stdlib.osgi\")\n      .get()\n      .get()\n"
  },
  {
    "path": "build-logic/src/main/kotlin/okhttp.base-conventions.gradle.kts",
    "content": "import org.gradle.api.artifacts.VersionCatalogsExtension\nimport okhttp3.buildsupport.platform\nimport okhttp3.buildsupport.testJavaVersion\nimport org.gradle.kotlin.dsl.withType\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nval libs = extensions.getByType<VersionCatalogsExtension>().named(\"libs\")\n\ngroup = \"com.squareup.okhttp3\"\nversion = \"5.4.0-SNAPSHOT\"\n\nval platform = project.platform\nval testJavaVersion = project.testJavaVersion\n\n\n// Friend configurations - moved here to be shared across all modules (including android-test)\nval friendsApi = configurations.maybeCreate(\"friendsApi\").apply {\n  isCanBeResolved = true\n  isCanBeConsumed = false\n  isTransitive = true\n}\nval friendsImplementation = configurations.maybeCreate(\"friendsImplementation\").apply {\n  isCanBeResolved = true\n  isCanBeConsumed = false\n  isTransitive = false\n}\nval friendsTestImplementation = configurations.maybeCreate(\"friendsTestImplementation\").apply {\n  isCanBeResolved = true\n  isCanBeConsumed = false\n  isTransitive = false\n}\n\nconfigurations.configureEach {\n  if (name == \"implementation\") {\n    extendsFrom(friendsApi, friendsImplementation)\n  }\n  if (name == \"api\") {\n    extendsFrom(friendsApi)\n  }\n  if (name == \"testImplementation\") {\n    extendsFrom(friendsTestImplementation)\n  }\n}\n\ntasks.withType<KotlinCompile>().configureEach {\n  friendPaths.from(friendsApi.incoming.artifactView { }.files)\n  friendPaths.from(friendsImplementation.incoming.artifactView { }.files)\n  friendPaths.from(friendsTestImplementation.incoming.artifactView { }.files)\n}\n\nval resolvableConfigurations = configurations.filter { it.isCanBeResolved }\ntasks.register(\"downloadDependencies\") {\n  description = \"Download all dependencies to the Gradle cache\"\n  doLast {\n    for (configuration in resolvableConfigurations) {\n      configuration.files\n    }\n  }\n}\n\nnormalization {\n  runtimeClasspath {\n    metaInf {\n      ignoreAttribute(\"Bnd-LastModified\")\n    }\n  }\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/okhttp.dokka-multimodule-conventions.gradle.kts",
    "content": "val okhttpDokka: String? by project\nval dokkaBuild = okhttpDokka?.toBoolean() == true\n\nif (dokkaBuild) {\n  apply(plugin = \"org.jetbrains.dokka\")\n\n  dependencies {\n    add(\"dokka\", project(\":okhttp\"))\n    add(\"dokka\", project(\":okhttp-brotli\"))\n    add(\"dokka\", project(\":okhttp-coroutines\"))\n    add(\"dokka\", project(\":okhttp-dnsoverhttps\"))\n    add(\"dokka\", project(\":okhttp-java-net-cookiejar\"))\n    add(\"dokka\", project(\":logging-interceptor\"))\n    add(\"dokka\", project(\":okhttp-sse\"))\n    add(\"dokka\", project(\":okhttp-tls\"))\n    add(\"dokka\", project(\":okhttp-urlconnection\"))\n    add(\"dokka\", project(\":okhttp-zstd\"))\n    add(\"dokka\", project(\":mockwebserver\"))\n    add(\"dokka\", project(\":mockwebserver3\"))\n    add(\"dokka\", project(\":mockwebserver3-junit4\"))\n    add(\"dokka\", project(\":mockwebserver3-junit5\"))\n  }\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/okhttp.jvm-conventions.gradle.kts",
    "content": "import org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\nimport tapmoc.TapmocExtension\nimport tapmoc.configureKotlinCompatibility\n\nplugins {\n  id(\"okhttp.base-conventions\")\n  id(\"com.gradleup.tapmoc\")\n}\n\nval libs = extensions.getByType<VersionCatalogsExtension>().named(\"libs\")\n\nfun library(alias: String) = libs.findLibrary(alias).get().get().let {\n  \"${it.module.group}:${it.module.name}:${it.versionConstraint.requiredVersion}\"\n}\nfun version(alias: String) = libs.findVersion(alias).get().toString()\n\nextensions.configure<JavaPluginExtension> {\n  toolchain {\n    languageVersion.set(JavaLanguageVersion.of(21))\n  }\n}\n\n// Introduce in a separate change\n//configureJavaCompatibility(javaVersion = 8)\n\nconfigureKotlinCompatibility(version = version(\"kotlinCoreLibrariesVersion\"))\n\ntasks.withType<JavaCompile> {\n  options.encoding = Charsets.UTF_8.toString()\n  if (name.contains(\"Java9\")) {\n    sourceCompatibility = \"9\"\n    targetCompatibility = \"9\"\n  } else {\n    sourceCompatibility = \"1.8\"\n    targetCompatibility = \"1.8\"\n  }\n}\n\ntasks.withType<KotlinCompile> {\n  compilerOptions {\n    jvmTarget.set(JvmTarget.JVM_1_8)\n    freeCompilerArgs.addAll(\n      \"-Xjvm-default=all\",\n      \"-Xexpect-actual-classes\",\n    )\n  }\n}\n\nextensions.configure<TapmocExtension> {\n  // Fail the build if any api dependency exposes incompatible Kotlin metadata, Kotlin stdlib, or Java bytecode version.\n  checkDependencies()\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/okhttp.publish-conventions.gradle.kts",
    "content": "import com.vanniktech.maven.publish.JavadocJar\nimport com.vanniktech.maven.publish.KotlinJvm\nimport com.vanniktech.maven.publish.KotlinMultiplatform\nimport com.vanniktech.maven.publish.MavenPublishBaseExtension\nimport kotlinx.validation.ApiValidationExtension\nimport kotlinx.validation.KotlinApiBuildTask\nimport kotlinx.validation.KotlinApiCompareTask\n\nplugins {\n  id(\"com.vanniktech.maven.publish.base\")\n  id(\"binary-compatibility-validator\")\n}\n\nconfigure<MavenPublishBaseExtension> {\n  publishToMavenCentral(automaticRelease = true)\n  signAllPublications()\n  pom {\n    name.set(project.name)\n    description.set(\"Square’s meticulous HTTP client for Java and Kotlin.\")\n    url.set(\"https://square.github.io/okhttp/\")\n    licenses {\n      license {\n        name.set(\"The Apache Software License, Version 2.0\")\n        url.set(\"https://www.apache.org/licenses/LICENSE-2.0.txt\")\n        distribution.set(\"repo\")\n      }\n    }\n    scm {\n      connection.set(\"scm:git:https://github.com/square/okhttp.git\")\n      developerConnection.set(\"scm:git:ssh://git@github.com/square/okhttp.git\")\n      url.set(\"https://github.com/square/okhttp\")\n    }\n    developers {\n      developer {\n        name.set(\"Square, Inc.\")\n      }\n    }\n  }\n\n  if (project.name == \"okhttp\") {\n    configure(KotlinMultiplatform(javadocJar = JavadocJar.Empty()))\n  } else if (plugins.hasPlugin(JavaBasePlugin::class.java)) {\n    configure(KotlinJvm(javadocJar = JavadocJar.Empty()))\n  }\n}\n\nconfigure<ApiValidationExtension> {\n  ignoredPackages += \"okhttp3.logging.internal\"\n  ignoredPackages += \"mockwebserver3.internal\"\n  ignoredPackages += \"okhttp3.internal\"\n  ignoredPackages += \"mockwebserver3.junit5.internal\"\n  ignoredPackages += \"okhttp3.brotli.internal\"\n  ignoredPackages += \"okhttp3.sse.internal\"\n  ignoredPackages += \"okhttp3.tls.internal\"\n}\n\nif (project.name == \"okhttp\") {\n  // Workaround for https://github.com/Kotlin/binary-compatibility-validator/issues/312\n  val apiBuild = tasks.register<KotlinApiBuildTask>(\"androidApiBuild\") {\n    outputApiFile = project.layout.buildDirectory.file(\"${this.name}/okhttp.api\")\n    inputClassesDirs.from(tasks.getByName(\"compileAndroidMain\").outputs)\n  }\n  val apiCheck = tasks.register<KotlinApiCompareTask>(\"androidApiCheck\") {\n    group = \"verification\"\n    projectApiFile = project.file(\"api/android/okhttp.api\")\n    generatedApiFile = apiBuild.flatMap(KotlinApiBuildTask::outputApiFile)\n  }\n  val apiDump = tasks.register<Copy>(\"androidApiDump\") {\n    from(apiBuild.flatMap(KotlinApiBuildTask::outputApiFile))\n    destinationDir = project.file(\"api/android\")\n  }\n\n  tasks.named(\"apiDump\").configure { dependsOn(apiDump) }\n  tasks.named(\"apiCheck\").configure { dependsOn(apiCheck) }\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/okhttp.quality-conventions.gradle.kts",
    "content": "import com.vanniktech.maven.publish.MavenPublishBaseExtension\nimport ru.vyarus.gradle.plugin.animalsniffer.AnimalSnifferExtension\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport org.gradle.kotlin.dsl.withType\nimport ru.vyarus.gradle.plugin.animalsniffer.AnimalSniffer\n\nplugins {\n  id(\"okhttp.base-conventions\")\n  id(\"checkstyle\")\n  id(\"ru.vyarus.animalsniffer\")\n  id(\"com.android.lint\")\n  id(\"com.diffplug.spotless\")\n}\n\nval libs = extensions.getByType<VersionCatalogsExtension>().named(\"libs\")\n\nfun library(alias: String) = libs.findLibrary(alias).get().get().let {\n  \"${it.module.group}:${it.module.name}:${it.versionConstraint.requiredVersion}\"\n}\nfun version(alias: String) = libs.findVersion(alias).get().toString()\n\ntasks.withType<Checkstyle>().configureEach {\n  exclude(\"**/CipherSuite.java\")\n}\n\nval checkstyleConfig = configurations.maybeCreate(\"checkstyleConfig\")\ndependencies {\n  add(\"checkstyleConfig\", library(\"checkstyle\")) {\n    isTransitive = false\n  }\n}\n\nconfigure<CheckstyleExtension> {\n  config = resources.text.fromArchiveEntry(checkstyleConfig, \"google_checks.xml\")\n  toolVersion = version(\"checkstyle\")\n\n  val sourceSets = project.extensions.findByType<SourceSetContainer>()\n  val main = sourceSets?.findByName(\"main\") ?: sourceSets?.findByName(\"jvmMain\")\n  if (main != null) {\n    this.sourceSets = listOf(main)\n  }\n}\n\nval androidSignature = configurations.maybeCreate(\"androidSignature\")\nval jvmSignature = configurations.maybeCreate(\"jvmSignature\")\n\nconfigure<AnimalSnifferExtension> {\n  annotation = \"okhttp3.internal.SuppressSignatureCheck\"\n\n  val sourceSets = project.extensions.findByType<SourceSetContainer>()\n  val main = sourceSets?.findByName(\"main\") ?: sourceSets?.findByName(\"jvmMain\")\n  if (main != null) {\n    this.sourceSets = listOf(main)\n  }\n\n  signatures = androidSignature + jvmSignature\n  failWithoutSignatures = false\n}\n\n// Default to only published modules\nproject.tasks.withType<AnimalSniffer> {\n  val hasMavenPublish = project.extensions.findByType<MavenPublishBaseExtension>() != null\n  onlyIf {\n    hasMavenPublish\n  }\n}\n\ndependencies {\n  // Logic for signatures should be moved to the applying module or configured via extension\n  // For now, we'll keep the standard ones and allow modules to add more\n  androidSignature(library(\"signature-android-apilevel21\")) { artifact { type = \"signature\" } }\n  jvmSignature(library(\"codehaus-signature-java18\")) { artifact { type = \"signature\" } }\n\n  \"lintChecks\"(library(\"androidx-lint-gradle\"))\n}\n\nconfigure<com.android.build.api.dsl.Lint> {\n  xmlReport = true\n  checkDependencies = true\n}\n\nconfigure<com.diffplug.gradle.spotless.SpotlessExtension> {\n  kotlin {\n    target(\"src/**/*.kt\")\n    targetExclude(\"**/kotlinTemplates/**/*.kt\")\n    ktlint()\n    suppressLintsFor {\n      step = \"ktlint\"\n      shortCode = \"standard:mixed-condition-operators\"\n    }\n  }\n  kotlinGradle {\n    target(\"*.kts\")\n    ktlint()\n  }\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/okhttp.testing-conventions.gradle.kts",
    "content": "import org.gradle.api.tasks.testing.logging.TestExceptionFormat\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport okhttp3.buildsupport.platform\nimport okhttp3.buildsupport.testJavaVersion\n\nplugins {\n  id(\"okhttp.base-conventions\")\n}\n\nval libs = extensions.getByType<VersionCatalogsExtension>().named(\"libs\")\n\nfun library(alias: String) = libs.findLibrary(alias).get().get().let {\n  \"${it.module.group}:${it.module.name}:${it.versionConstraint.requiredVersion}\"\n}\n\n\nval testRuntimeOnly = configurations.maybeCreate(\"testRuntimeOnly\")\ndependencies {\n  testRuntimeOnly(library(\"junit-jupiter-engine\"))\n  testRuntimeOnly(library(\"junit-vintage-engine\"))\n  testRuntimeOnly(library(\"junit-platform-launcher\"))\n}\n\ntasks.withType<Test> {\n  useJUnitPlatform()\n  jvmArgs(\"-Dokhttp.platform=$platform\")\n\n  if (platform == \"loom\") {\n    jvmArgs(\"-Djdk.tracePinnedThreads=short\")\n  }\n  if (platform == \"openjsse\") {\n    if (testJavaVersion > 8) {\n      throw GradleException(\"OpenJSSE is only supported on Java 8\")\n    }\n  }\n\n  val javaToolchains = project.extensions.getByType<JavaToolchainService>()\n  javaLauncher.set(javaToolchains.launcherFor {\n    languageVersion.set(JavaLanguageVersion.of(testJavaVersion))\n  })\n\n  maxParallelForks = Runtime.getRuntime().availableProcessors() * 2\n  testLogging {\n    exceptionFormat = TestExceptionFormat.FULL\n  }\n\n  systemProperty(\"okhttp.platform\", platform)\n  systemProperty(\"junit.jupiter.extensions.autodetection.enabled\", \"true\")\n}\n\ntasks.withType<Test>().configureEach {\n  environment(\"OKHTTP_ROOT\", rootDir)\n}\n\nplugins.withId(\"org.jetbrains.kotlin.jvm\") {\n  val test = tasks.named(\"test\")\n  tasks.register(\"jvmTest\") {\n    description = \"Get 'gradlew jvmTest' to run the tests of JVM-only modules\"\n    dependsOn(test)\n  }\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/okhttp3/buildsupport/OkHttpBuildUtils.kt",
    "content": "/*\n * Copyright (c) 2026 OkHttp Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.buildsupport\n\nimport org.gradle.api.Project\n\nval Project.platform: String\n  get() = findProperty(\"okhttp.platform\")?.toString() ?: \"jdk9\"\n\nval Project.testJavaVersion: Int\n  get() = findProperty(\"test.java.version\")?.toString()?.toInt() ?: 21\n\nval Project.androidBuild: Boolean\n  get() = findProperty(\"androidBuild\")?.toString()?.toBoolean() ?: false\n\nval Project.alpnBootVersion: String?\n  get() = findProperty(\"alpn.boot.version\")?.toString()\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "plugins {\n  alias(libs.plugins.spotless) apply false\n  alias(libs.plugins.android.library) apply false\n  alias(libs.plugins.android.application) apply false\n  alias(libs.plugins.kotlin.jvm) apply false\n  alias(libs.plugins.kotlin.multiplatform) apply false\n  alias(libs.plugins.kotlin.serialization) apply false\n  alias(libs.plugins.dokka) apply false\n  alias(libs.plugins.maven.publish) apply false\n  alias(libs.plugins.binary.compatibility.validator) apply false\n  alias(libs.plugins.animalsniffer) apply false\n  alias(libs.plugins.android.junit5) apply false\n  alias(libs.plugins.shadow) apply false\n  alias(libs.plugins.graalvm) apply false\n  alias(libs.plugins.ksp) apply false\n  alias(libs.plugins.burst) apply false\n  id(\"okhttp.dokka-multimodule-conventions\")\n}\n\ntasks.wrapper {\n  distributionType = Wrapper.DistributionType.ALL\n}\n"
  },
  {
    "path": "container-tests/README.md",
    "content": "OkHttp Container Tests\n======================\n\nThis module contains tests against other services\nvia containers.\n"
  },
  {
    "path": "container-tests/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.base-conventions\")\n}\n\nimport okhttp3.buildsupport.platform\nimport okhttp3.buildsupport.testJavaVersion\n\nval platform = project.platform\nval testJavaVersion = project.testJavaVersion\n\ntasks.withType<Test> {\n  useJUnitPlatform()\n  val isCi = providers.environmentVariable(\"CI\")\n  val containerTests = providers.gradleProperty(\"containerTests\")\n  onlyIf(\"By default not in CI\") {\n    !isCi.isPresent\n      || (containerTests.isPresent && containerTests.get().toBoolean())\n  }\n\n  jvmArgs(\n    \"-Dokhttp.platform=$platform\",\n  )\n\n  if (platform == \"loom\") {\n    jvmArgs(\n      \"-Djdk.tracePinnedThreads=short\",\n    )\n  }\n\n  val javaToolchains = project.extensions.getByType<JavaToolchainService>()\n  javaLauncher.set(javaToolchains.launcherFor {\n    languageVersion.set(JavaLanguageVersion.of(testJavaVersion))\n  })\n}\n\ndependencies {\n  api(projects.okhttp)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(libs.junit.jupiter.api)\n  testImplementation(libs.junit.jupiter.params)\n  testImplementation(libs.junit.jupiter.engine)\n  testImplementation(libs.assertk)\n  testImplementation(libs.testcontainers)\n  testImplementation(libs.mockserver)\n  testImplementation(libs.mockserver.client)\n  testImplementation(libs.testcontainers.junit5)\n}\n"
  },
  {
    "path": "container-tests/src/test/java/okhttp3/containers/BasicLoomTest.kt",
    "content": "/*\n * Copyright (C) 2024 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.containers\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isNotEmpty\nimport java.io.ByteArrayOutputStream\nimport java.io.PrintStream\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport okhttp3.Dispatcher\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.containers.BasicMockServerTest.Companion.MOCKSERVER_IMAGE\nimport okhttp3.containers.BasicMockServerTest.Companion.trustMockServer\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junit.jupiter.api.parallel.Isolated\nimport org.mockserver.client.MockServerClient\nimport org.mockserver.model.HttpRequest.request\nimport org.mockserver.model.HttpResponse.response\nimport org.testcontainers.containers.MockServerContainer\nimport org.testcontainers.junit.jupiter.Container\nimport org.testcontainers.junit.jupiter.Testcontainers\n\n@Testcontainers\n@Isolated\nclass BasicLoomTest {\n  @JvmField\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  // Use mock server so we are strictly testing OkHttp client only in this test.\n  // We should test MockWebServer later.\n  @Container\n  val mockServer: MockServerContainer = MockServerContainer(MOCKSERVER_IMAGE)\n\n  val capturedOut = ByteArrayOutputStream()\n\n  private lateinit var executor: ExecutorService\n\n  private lateinit var client: OkHttpClient\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeLoom()\n    assertThat(System.getProperty(\"jdk.tracePinnedThreads\")).isNotEmpty()\n\n    client =\n      OkHttpClient\n        .Builder()\n        .trustMockServer()\n        .dispatcher(Dispatcher(newVirtualThreadPerTaskExecutor()))\n        .build()\n\n    executor = newVirtualThreadPerTaskExecutor()\n\n    // Capture non-deterministic but probable sysout warnings of pinned threads\n    // https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html\n    System.setOut(PrintStream(capturedOut))\n  }\n\n  @AfterEach\n  fun checkForPinning() {\n    assertThat(capturedOut.toString()).isEmpty()\n  }\n\n  private fun newVirtualThreadPerTaskExecutor(): ExecutorService =\n    Executors::class.java.getMethod(\"newVirtualThreadPerTaskExecutor\").invoke(null) as ExecutorService\n\n  @Test\n  fun testHttpsRequest() {\n    MockServerClient(mockServer.host, mockServer.serverPort).use { mockServerClient ->\n      mockServerClient\n        .`when`(\n          request()\n            .withPath(\"/person\")\n            .withQueryStringParameter(\"name\", \"peter\"),\n        ).respond(response().withBody(\"Peter the person!\"))\n\n      val results =\n        (1..20).map {\n          executor.submit {\n            val response =\n              client.newCall(Request((mockServer.secureEndpoint + \"/person?name=peter\").toHttpUrl())).execute()\n\n            val body = response.body.string()\n            assertThat(body).contains(\"Peter the person\")\n          }\n        }\n\n      results.forEach {\n        it.get()\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "container-tests/src/test/java/okhttp3/containers/BasicMockServerTest.kt",
    "content": "/*\n * Copyright (C) 2024 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.containers\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport javax.net.ssl.TrustManagerFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport org.junit.jupiter.api.Test\nimport org.mockserver.client.MockServerClient\nimport org.mockserver.configuration.Configuration\nimport org.mockserver.logging.MockServerLogger\nimport org.mockserver.model.HttpRequest.request\nimport org.mockserver.model.HttpResponse.response\nimport org.mockserver.socket.tls.KeyStoreFactory\nimport org.testcontainers.containers.MockServerContainer\nimport org.testcontainers.junit.jupiter.Container\nimport org.testcontainers.junit.jupiter.Testcontainers\nimport org.testcontainers.utility.DockerImageName\n\n@Testcontainers\nclass BasicMockServerTest {\n  @Container\n  val mockServer: MockServerContainer = MockServerContainer(MOCKSERVER_IMAGE)\n\n  val client =\n    OkHttpClient\n      .Builder()\n      .trustMockServer()\n      .build()\n\n  @Test\n  fun testRequest() {\n    MockServerClient(mockServer.host, mockServer.serverPort).use { mockServerClient ->\n      mockServerClient\n        .`when`(\n          request()\n            .withPath(\"/person\")\n            .withQueryStringParameter(\"name\", \"peter\"),\n        ).respond(response().withBody(\"Peter the person!\"))\n\n      val response = client.newCall(Request((mockServer.endpoint + \"/person?name=peter\").toHttpUrl())).execute()\n\n      assertThat(response.body.string()).contains(\"Peter the person\")\n    }\n  }\n\n  @Test\n  fun testHttpsRequest() {\n    MockServerClient(mockServer.host, mockServer.serverPort).use { mockServerClient ->\n      mockServerClient\n        .`when`(\n          request()\n            .withPath(\"/person\")\n            .withQueryStringParameter(\"name\", \"peter\"),\n        ).respond(response().withBody(\"Peter the person!\"))\n\n      val response = client.newCall(Request((mockServer.secureEndpoint + \"/person?name=peter\").toHttpUrl())).execute()\n\n      assertThat(response.body.string()).contains(\"Peter the person\")\n    }\n  }\n\n  companion object {\n    val MOCKSERVER_IMAGE: DockerImageName =\n      DockerImageName\n        .parse(\"mockserver/mockserver\")\n        .withTag(\"mockserver-5.15.0\")\n\n    fun OkHttpClient.Builder.trustMockServer(): OkHttpClient.Builder =\n      apply {\n        val keyStoreFactory = KeyStoreFactory(Configuration.configuration(), MockServerLogger())\n\n        val socketFactory = keyStoreFactory.sslContext().socketFactory\n\n        val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())\n        trustManagerFactory.init(keyStoreFactory.loadOrCreateKeyStore())\n        val trustManager = trustManagerFactory.trustManagers.first() as X509TrustManager\n\n        sslSocketFactory(socketFactory, trustManager)\n      }\n  }\n}\n"
  },
  {
    "path": "container-tests/src/test/java/okhttp3/containers/BasicProxyTest.kt",
    "content": "/*\n * Copyright (C) 2024 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.containers\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.isEqualTo\nimport java.net.HttpURLConnection\nimport java.net.Proxy\nimport java.net.URI\nimport javax.net.ssl.HttpsURLConnection\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.containers.BasicMockServerTest.Companion.MOCKSERVER_IMAGE\nimport okhttp3.containers.BasicMockServerTest.Companion.trustMockServer\nimport okio.buffer\nimport okio.source\nimport org.junit.jupiter.api.Test\nimport org.mockserver.client.MockServerClient\nimport org.mockserver.configuration.Configuration\nimport org.mockserver.logging.MockServerLogger\nimport org.mockserver.model.HttpRequest.request\nimport org.mockserver.model.HttpResponse.response\nimport org.mockserver.proxyconfiguration.ProxyConfiguration\nimport org.mockserver.socket.tls.KeyStoreFactory\nimport org.testcontainers.containers.MockServerContainer\nimport org.testcontainers.junit.jupiter.Container\nimport org.testcontainers.junit.jupiter.Testcontainers\n\n@Testcontainers\nclass BasicProxyTest {\n  @Container\n  val mockServer: MockServerContainer =\n    MockServerContainer(MOCKSERVER_IMAGE)\n      .withNetworkAliases(\"mockserver\")\n\n  @Test\n  fun testOkHttpDirect() {\n    testRequest {\n      val client = OkHttpClient()\n\n      val response =\n        client\n          .newCall(\n            Request((mockServer.endpoint + \"/person?name=peter\").toHttpUrl()),\n          ).execute()\n\n      assertThat(response.body.string()).contains(\"Peter the person\")\n      assertThat(response.protocol).isEqualTo(Protocol.HTTP_1_1)\n    }\n  }\n\n  @Test\n  fun testOkHttpProxied() {\n    testRequest {\n      it.withProxyConfiguration(ProxyConfiguration.proxyConfiguration(ProxyConfiguration.Type.HTTP, it.remoteAddress()))\n\n      val client =\n        OkHttpClient\n          .Builder()\n          .proxy(Proxy(Proxy.Type.HTTP, it.remoteAddress()))\n          .build()\n\n      val response =\n        client\n          .newCall(\n            Request((mockServer.endpoint + \"/person?name=peter\").toHttpUrl()),\n          ).execute()\n\n      assertThat(response.body.string()).contains(\"Peter the person\")\n    }\n  }\n\n  @Test\n  fun testOkHttpSecureDirect() {\n    testRequest {\n      val client =\n        OkHttpClient\n          .Builder()\n          .trustMockServer()\n          .build()\n\n      val response =\n        client\n          .newCall(\n            Request((mockServer.secureEndpoint + \"/person?name=peter\").toHttpUrl()),\n          ).execute()\n\n      assertThat(response.body.string()).contains(\"Peter the person\")\n      assertThat(response.protocol).isEqualTo(Protocol.HTTP_2)\n    }\n  }\n\n  @Test\n  fun testOkHttpSecureProxiedHttp1() {\n    testRequest {\n      val client =\n        OkHttpClient\n          .Builder()\n          .trustMockServer()\n          .proxy(Proxy(Proxy.Type.HTTP, it.remoteAddress()))\n          .protocols(listOf(Protocol.HTTP_1_1))\n          .build()\n\n      val response =\n        client\n          .newCall(\n            Request((mockServer.secureEndpoint + \"/person?name=peter\").toHttpUrl()),\n          ).execute()\n\n      assertThat(response.body.string()).contains(\"Peter the person\")\n      assertThat(response.protocol).isEqualTo(Protocol.HTTP_1_1)\n    }\n  }\n\n  @Test\n  fun testUrlConnectionDirect() {\n    testRequest {\n      val url = URI(mockServer.endpoint + \"/person?name=peter\").toURL()\n\n      val connection = url.openConnection() as HttpURLConnection\n\n      assertThat(\n        connection.inputStream\n          .source()\n          .buffer()\n          .readUtf8(),\n      ).contains(\"Peter the person\")\n    }\n  }\n\n  @Test\n  fun testUrlConnectionPlaintextProxied() {\n    testRequest {\n      val proxy =\n        Proxy(\n          Proxy.Type.HTTP,\n          it.remoteAddress(),\n        )\n\n      val url = URI(mockServer.endpoint + \"/person?name=peter\").toURL()\n\n      val connection = url.openConnection(proxy) as HttpURLConnection\n\n      assertThat(\n        connection.inputStream\n          .source()\n          .buffer()\n          .readUtf8(),\n      ).contains(\"Peter the person\")\n    }\n  }\n\n  @Test\n  fun testUrlConnectionSecureDirect() {\n    val keyStoreFactory = KeyStoreFactory(Configuration.configuration(), MockServerLogger())\n    HttpsURLConnection.setDefaultSSLSocketFactory(keyStoreFactory.sslContext().socketFactory)\n\n    testRequest {\n      val url = URI(mockServer.secureEndpoint + \"/person?name=peter\").toURL()\n\n      val connection = url.openConnection() as HttpURLConnection\n\n      assertThat(\n        connection.inputStream\n          .source()\n          .buffer()\n          .readUtf8(),\n      ).contains(\"Peter the person\")\n    }\n  }\n\n  @Test\n  fun testUrlConnectionSecureProxied() {\n    val keyStoreFactory = KeyStoreFactory(Configuration.configuration(), MockServerLogger())\n    HttpsURLConnection.setDefaultSSLSocketFactory(keyStoreFactory.sslContext().socketFactory)\n\n    testRequest {\n      val proxy =\n        Proxy(\n          Proxy.Type.HTTP,\n          it.remoteAddress(),\n        )\n\n      val url = URI(mockServer.secureEndpoint + \"/person?name=peter\").toURL()\n\n      val connection = url.openConnection(proxy) as HttpURLConnection\n\n      assertThat(\n        connection.inputStream\n          .source()\n          .buffer()\n          .readUtf8(),\n      ).contains(\"Peter the person\")\n    }\n  }\n\n  private fun testRequest(function: (MockServerClient) -> Unit) {\n    MockServerClient(mockServer.host, mockServer.serverPort).use { mockServerClient ->\n      val request =\n        request()\n          .withPath(\"/person\")\n          .withQueryStringParameter(\"name\", \"peter\")\n\n      mockServerClient\n        .`when`(\n          request,\n        ).respond(response().withBody(\"Peter the person!\"))\n\n      function(mockServerClient)\n    }\n  }\n}\n"
  },
  {
    "path": "container-tests/src/test/java/okhttp3/containers/SocksProxyTest.kt",
    "content": "/*\n * Copyright (C) 2024 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.containers\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.Proxy.Type.SOCKS\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.containers.BasicMockServerTest.Companion.MOCKSERVER_IMAGE\nimport org.junit.jupiter.api.Test\nimport org.mockserver.client.MockServerClient\nimport org.mockserver.model.HttpRequest.request\nimport org.mockserver.model.HttpResponse.response\nimport org.testcontainers.containers.GenericContainer\nimport org.testcontainers.containers.MockServerContainer\nimport org.testcontainers.containers.Network\nimport org.testcontainers.junit.jupiter.Container\nimport org.testcontainers.junit.jupiter.Testcontainers\nimport org.testcontainers.utility.DockerImageName\n\n@Testcontainers\nclass SocksProxyTest {\n  val network: Network = Network.newNetwork()\n\n  @Container\n  val mockServer: MockServerContainer =\n    MockServerContainer(MOCKSERVER_IMAGE)\n      .withNetwork(network)\n      .withNetworkAliases(\"mockserver\")\n\n  @Container\n  val socks5Proxy =\n    GenericContainer(SOCKS5_PROXY)\n      .withNetwork(network)\n      .withExposedPorts(1080)\n\n  @Test\n  fun testLocal() {\n    MockServerClient(mockServer.host, mockServer.serverPort).use { mockServerClient ->\n      mockServerClient\n        .`when`(\n          request()\n            .withPath(\"/person\")\n            .withQueryStringParameter(\"name\", \"peter\"),\n        ).respond(response().withBody(\"Peter the person!\"))\n\n      val client =\n        OkHttpClient\n          .Builder()\n          .proxy(Proxy(SOCKS, InetSocketAddress(socks5Proxy.host, socks5Proxy.firstMappedPort)))\n          .build()\n\n      val response =\n        client\n          .newCall(\n            Request(\"http://mockserver:1080/person?name=peter\".toHttpUrl()),\n          ).execute()\n\n      assertThat(response.body.string()).contains(\"Peter the person\")\n    }\n  }\n\n  companion object {\n    val SOCKS5_PROXY: DockerImageName =\n      DockerImageName\n        .parse(\"serjs/go-socks5-proxy\")\n        .withTag(\"v0.0.3\")\n  }\n}\n"
  },
  {
    "path": "deploy_website.sh",
    "content": "#!/bin/bash\n\n# The website is built using MkDocs with the Material theme.\n# https://squidfunk.github.io/mkdocs-material/\n# It requires python3 to run.\n\nset -ex\n\nREPO=\"git@github.com:square/okhttp.git\"\nDIR=temp-clone\n\n# Delete any existing temporary website clone\nrm -rf $DIR\n\n# Clone the current repo into temp folder\ngit clone $REPO $DIR\n# Replace `git clone` with these lines to hack on the website locally\n# cp -a . \"../okhttp-website\"\n# mv \"../okhttp-website\" \"$DIR\"\n\n# Move working directory into temp folder\ncd $DIR\n\n# Generate the API docs\n./gradlew dokkaHtmlMultiModule\n\nmv ./build/dokka/htmlMultiModule docs/5.x\n\n# Copy in special files that GitHub wants in the project root.\ncat README.md | grep -v 'project website' > docs/index.md\ncp CHANGELOG.md docs/changelogs/changelog.md\ncp CONTRIBUTING.md docs/contribute/contributing.md\n\n# Build the site and push the new files up to GitHub\npython3 -m venv venv\nsource venv/bin/activate\npip install mkdocs-material mkdocs-redirects\nmkdocs gh-deploy\n\n# Restore Javadocs from 1.x, 2.x, and 3.x.\ngit checkout gh-pages\ngit cherry-pick bb229b9dcc9a21a73edbf8d936bea88f52e0a3ff\ngit cherry-pick c695732f1d4aea103b826876c077fbfea630e244\ngit push --set-upstream origin gh-pages\n\n# Delete our temp folder\ncd ..\nrm -rf $DIR\n"
  },
  {
    "path": "docs/assets/css/app.css",
    "content": "@font-face {\n    font-family: cash-market;\n    src: url(\"https://cash-f.squarecdn.com/static/fonts/cash-market/v2/CashMarket-Regular.woff2\") format(\"woff2\");\n    font-weight: 400;\n    font-style: normal\n}\n\n@font-face {\n    font-family: cash-market;\n    src: url(\"https://cash-f.squarecdn.com/static/fonts/cash-market/v2/CashMarket-Medium.woff2\") format(\"woff2\");\n    font-weight: 500;\n    font-style: normal\n}\n\n@font-face {\n    font-family: cash-market;\n    src: url(\"https://cash-f.squarecdn.com/static/fonts/cash-market/v2/CashMarket-Bold.woff2\") format(\"woff2\");\n    font-weight: 700;\n    font-style: normal\n}\n\nbody, input {\n    font-family: cash-market,\"Helvetica Neue\",helvetica,sans-serif;\n}\n\n.md-typeset h1, .md-typeset h2, .md-typeset h3, .md-typeset h4 {\n    font-family: cash-market,\"Helvetica Neue\",helvetica,sans-serif;\n    line-height: normal;\n    font-weight: bold;\n}\n\nbutton.dl {\n  font-weight: 300;\n  font-size: 25px;\n  line-height: 40px;\n  padding: 3px 10px;\n  display: inline-block;\n  border-radius: 6px;\n  color: #f0f0f0;\n  margin: 5px 0;\n  width: auto;\n}\n\n.logo {\n  text-align: center;\n  margin-top: 150px;\n}"
  },
  {
    "path": "docs/changelogs/changelog_1x.md",
    "content": "OkHttp 1.x Change Log\n=====================\n\n## Version 1.6.0\n\n_2014-05-23_\n\n * Offer bridges to make it easier to migrate from OkHttp 1.x to OkHttp 2.0.\n   This adds `OkUrlFactory`, `Cache`, and `@Deprecated` annotations for APIs\n   dropped in 2.0.\n\n## Version 1.5.4\n\n_2014-04-14_\n\n * Drop ALPN support in Android. There's a concurrency bug in all\n   currently-shipping versions.\n * Support asynchronous disconnects by breaking the socket only. This should\n   prevent flakiness from multiple threads concurrently accessing a stream.\n\n## Version 1.5.3\n\n_2014-03-29_\n\n * Fix bug where the Content-Length header was not always dropped when\n   following a redirect from a POST to a GET.\n * Implement basic support for `Thread.interrupt()`. OkHttp now checks\n   for an interruption before doing a blocking call. If it is interrupted,\n   it throws an `InterruptedIOException`.\n\n## Version 1.5.2\n\n_2014-03-17_\n\n * Fix bug where deleting a file that was absent from the `HttpResponseCache`\n   caused an IOException.\n * Fix bug in HTTP/2 where our HPACK decoder wasn't emitting entries in\n   certain eviction scenarios, leading to dropped response headers.\n\n## Version 1.5.1\n\n_2014-03-11_\n\n * Fix 1.5.0 regression where connections should not have been recycled.\n * Fix 1.5.0 regression where transparent Gzip was broken by attempting to\n   recover from another I/O failure.\n * Fix problems where spdy/3.1 headers may not have been compressed properly.\n * Fix problems with spdy/3.1 and http/2 where the wrong window size was being\n   used.\n * Fix 1.5.0 regression where conditional cache responses could corrupt the\n   connection pool.\n\n\n## Version 1.5.0\n\n_2014-03-07_\n\n\n##### OkHttp no longer uses the default SSL context.\n\nApplications that want to use the global SSL context with OkHttp should configure their\nOkHttpClient instances with the following:\n\n```java\nokHttpClient.setSslSocketFactory(HttpsURLConnection.getDefaultSSLSocketFactory());\n```\n\nA simpler solution is to avoid the shared default SSL socket factory. Instead, if you\nneed to customize SSL, do so for your specific OkHttpClient instance only.\n\n##### Synthetic headers have changed\n\nPreviously OkHttp added a synthetic response header, `OkHttp-Selected-Transport`. It\nhas been replaced with a new synthetic header, `OkHttp-Selected-Protocol`.\n\n##### Changes\n\n * New: Support for `HTTP-draft-09/2.0`.\n * New: Support for `spdy/3.1`. Dropped support for `spdy/3`.\n * New: Use ALPN on Android platforms that support it (4.4+)\n * New: CacheControl model and parser.\n * New: Protocol selection in MockWebServer.\n * Fix: Route selection shouldn't use TLS modes that we know will fail.\n * Fix: Cache SPDY responses even if the response body is closed prematurely.\n * Fix: Use strict timeouts when aborting a download.\n * Fix: Support Shoutcast HTTP responses like `ICY 200 OK`.\n * Fix: Don't unzip if there isn't a response body.\n * Fix: Don't leak gzip streams on redirects.\n * Fix: Don't do DNS lookups on invalid hosts.\n * Fix: Exhaust the underlying stream when reading gzip streams.\n * Fix: Support the `PATCH` method.\n * Fix: Support request bodies on `DELETE` method.\n * Fix: Drop the `okhttp-protocols` module.\n * Internal: Replaced internal byte array buffers with pooled buffers (\"OkBuffer\").\n\n\n## Version 1.3.0\n\n_2014-01-11_\n\n * New: Support for \"PATCH\" HTTP method in client and MockWebServer.\n * Fix: Drop `Content-Length` header when redirected from POST to GET.\n * Fix: Correctly read cached header entries with malformed header names.\n * Fix: Do not directly support any authentication schemes other than \"Basic\".\n * Fix: Respect read timeouts on recycled connections.\n * Fix: Transmit multiple cookie values as a single header with delimiter.\n * Fix: Ensure `null` is never returned from a connection's `getHeaderFields()`.\n * Fix: Persist proper `Content-Encoding` header to cache for GZip responses.\n * Fix: Eliminate rare race condition in SPDY streams that would prevent connection reuse.\n * Fix: Change HTTP date formats to UTC to conform to RFC2616 section 3.3.\n * Fix: Support SPDY header blocks with trailing bytes.\n * Fix: Allow `;` as separator for `Cache-Control` header.\n * Fix: Correct bug where HTTPS POST requests were always automatically buffered.\n * Fix: Honor read timeout when parsing SPDY headers.\n\n\n## Version 1.2.1\n\n_2013-08-23_\n\n * Resolve issue with 'jar-with-dependencies' artifact creation.\n * Fix: Support empty SPDY header values.\n\n\n## Version 1.2.0\n\n_2013-08-11_\n\n *  New APIs on OkHttpClient to set default timeouts for connect and read.\n *  Fix bug when caching SPDY responses.\n *  Fix a bug with SPDY plus half-closed streams. (thanks kwuollett)\n *  Fix a bug in `Content-Length` reporting for gzipped streams in the Apache\n    HTTP client adapter. (thanks kwuollett)\n *  Work around the Alcatel `getByInetAddress` bug (thanks k.kocel)\n *  Be more aggressive about testing pooled sockets before reuse. (thanks\n    warpspin)\n *  Include `Content-Type` and `Content-Encoding` in the Apache HTTP client\n    adapter. (thanks kwuollett)\n *  Add a media type class to OkHttp.\n *  Change custom header prefix:\n\n    ```\n    X-Android-Sent-Millis is now OkHttp-Sent-Millis\n    X-Android-Received-Millis is now OkHttp-Received-Millis\n    X-Android-Response-Source is now OkHttp-Response-Source\n    X-Android-Selected-Transport is now OkHttp-Selected-Transport\n    ```\n *  Improve cache invalidation for POST-like requests.\n *  Bring MockWebServer into OkHttp and teach it SPDY.\n\n\n## Version 1.1.1\n\n_2013-06-23_\n\n * Fix: ClassCastException when caching responses that were redirected from\n   HTTP to HTTPS.\n\n\n## Version 1.1.0\n\n_2013-06-15_\n\n * Fix: Connection reuse was broken for most HTTPS connections due to a bug in\n   the way the hostname verifier was selected.\n * Fix: Locking bug in SpdyConnection.\n * Fix: Ignore null header values (for compatibility with HttpURLConnection).\n * Add URLStreamHandlerFactory support so that `URL.openConnection()` uses\n   OkHttp.\n * Expose the transport (\"http/1.1\", \"spdy/3\", etc.) via magic request headers.\n   Use `X-Android-Transports` to write the preferred transports and\n   `X-Android-Selected-Transport` to read the negotiated transport.\n\n\n## Version 1.0.2\n\n_2013-05-11_\n\n * Fix: Remove use of Java 6-only APIs.\n * Fix: Properly handle exceptions from `NetworkInterface` when querying MTU.\n * Fix: Ensure MTU has a reasonable default and upper-bound.\n\n\n## Version 1.0.1\n\n_2013-05-06_\n\n * Correct casing of SSL in method names (`getSslSocketFactory`/`setSslSocketFactory`).\n\n\n## Version 1.0.0\n\n_2013-05-06_\n\nInitial release.\n"
  },
  {
    "path": "docs/changelogs/changelog_2x.md",
    "content": "OkHttp 2.x Change Log\n=====================\n\n## Version 2.7.5\n\n_2016-02-25_\n\n *  Fix: Change the certificate pinner to always build full chains. This\n    prevents a potential crash when using certificate pinning with the Google\n    Play Services security provider.\n\n\n## Version 2.7.4\n\n_2016-02-07_\n\n *  Fix: Don't crash when finding the trust manager if the Play Services (GMS)\n    security provider is installed.\n *  Fix: The previous release introduced a performance regression on Android,\n    caused by looking up CA certificates. This is now fixed.\n\n\n## Version 2.7.3\n\n_2016-02-06_\n\n *  Fix: Permit the trusted CA root to be pinned by `CertificatePinner`.\n\n\n## Version 2.7.2\n\n_2016-01-07_\n\n *  Fix: Don't eagerly release stream allocations on cache hits. We might still\n    need them to handle redirects.\n\n\n## Version 2.7.1\n\n_2016-01-01_\n\n *  Fix: Don't do a health check on newly-created connections. This is\n    unnecessary work that could put the client in an inconsistent state if the\n    health check fails.\n\n\n## Version 2.7.0\n\n_2015-12-13_\n\n *  **Rewritten connection management.** Previously OkHttp's connection pool\n    managed both idle and active connections for HTTP/2, but only idle\n    connections for HTTP/1.x. With this update the connection pool manages both\n    idle and active connections for everything. OkHttp now detects and warns on\n    connections that were allocated but never released, and will enforce HTTP/2\n    stream limits. This update also fixes `Call.cancel()` to not do I/O on the\n    calling thread.\n *  Fix: Don't log gzipped data in the logging interceptor.\n *  Fix: Don't resolve DNS addresses when connecting through a SOCKS proxy.\n *  Fix: Drop the synthetic `OkHttp-Selected-Protocol` response header.\n *  Fix: Support 204 and 205 'No Content' replies in the logging interceptor.\n *  New: Add `Call.isExecuted()`.\n\n\n## Version 2.6.0\n\n_2015-11-22_\n\n *  **New Logging Interceptor.** The `logging-interceptor` subproject offers\n    simple request and response logging. It may be configured to log headers and\n    bodies for debugging. It requires this Maven dependency:\n\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okhttp</groupId>\n       <artifactId>logging-interceptor</artifactId>\n       <version>2.6.0</version>\n     </dependency>\n     ```\n\n    Configure basic logging like this:\n\n    ```java\n    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();\n    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);\n    client.networkInterceptors().add(loggingInterceptor);\n    ```\n\n    **Warning:** Avoid `Level.HEADERS` and `Level.BODY` in production because\n    they could leak passwords and other authentication credentials to insecure\n    logs.\n\n *  **WebSocket API now uses `RequestBody` and `ResponseBody` for messages.**\n    This is a backwards-incompatible API change.\n\n *  **The DNS service is now pluggable.** In some situations this may be useful\n    to manually prioritize specific IP addresses.\n\n *  Fix: Don't throw when converting an `HttpUrl` to a `java.net.URI`.\n    Previously URLs with special characters like `|` and `[` would break when\n    subjected to URI’s overly-strict validation.\n *  Fix: Don't re-encode `+` as `%20` in encoded URL query strings. OkHttp\n    prefers `%20` when doing its own encoding, but will retain `+` when that is\n    provided.\n *  Fix: Enforce that callers call `WebSocket.close()` on IO errors. Error\n    handling in WebSockets is significantly improved.\n *  Fix: Don't use SPDY/3 style header concatenation for HTTP/2 request headers.\n    This could have corrupted requests where multiple headers had the same name,\n    as in cookies.\n *  Fix: Reject bad characters in the URL hostname. Previously characters like\n    `\\0` would cause a late crash when building the request.\n *  Fix: Allow interceptors to change the request method.\n *  Fix: Don’t use the request's `User-Agent` or `Proxy-Authorization` when\n    connecting to an HTTPS server via an HTTP tunnel. The `Proxy-Authorization`\n    header was being leaked to the origin server.\n *  Fix: Digits may be used in a URL scheme.\n *  Fix: Improve connection timeout recovery.\n *  Fix: Recover from `getsockname` crashes impacting Android releases prior to\n    4.2.2.\n *  Fix: Drop partial support for HTTP/1.0. Previously OkHttp would send\n    `HTTP/1.0` on connections after seeing a response with `HTTP/1.0`. The fixed\n    behavior is consistent with Firefox and Chrome.\n *  Fix: Allow a body in `OPTIONS` requests.\n *  Fix: Don't percent-encode non-ASCII characters in URL fragments.\n *  Fix: Handle null fragments.\n *  Fix: Don’t crash on interceptors that throw `IOException` before a\n    connection is attempted.\n *  New: Support [WebDAV][webdav] HTTP methods.\n *  New: Buffer WebSocket frames for better performance.\n *  New: Drop support for `TLS_DHE_DSS_WITH_AES_128_CBC_SHA`, our only remaining\n    DSS cipher suite. This is consistent with Firefox and Chrome which have also\n    dropped these cipher suite.\n\n## Version 2.5.0\n\n_2015-08-25_\n\n *  **Timeouts now default to 10 seconds.** Previously we defaulted to never\n    timing out, and that was a lousy policy. If establishing a connection,\n    reading the next byte from a connection, or writing the next byte to a\n    connection takes more than 10 seconds to complete, you’ll need to adjust\n    the timeouts manually.\n\n *  **OkHttp now rejects request headers that contain invalid characters.** This\n    includes potential security problems (newline characters) as well as simple\n    non-ASCII characters (including international characters and emoji).\n\n *  **Call canceling is more reliable.**  We had a bug where a socket being\n     connected wasn't being closed when the application used `Call.cancel()`.\n\n *  **Changing a HttpUrl’s scheme now tracks the default port.** We had a bug\n    where changing a URL from `http` to `https` would leave it on port 80.\n\n *  **Okio has been updated to 1.6.0.**\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okio</groupId>\n       <artifactId>okio</artifactId>\n       <version>1.6.0</version>\n     </dependency>\n     ```\n\n *  New: `Cache.initialize()`. Call this on a background thread to eagerly\n    initialize the response cache.\n *  New: Fold `MockWebServerRule` into `MockWebServer`. This makes it easier to\n    write JUnit tests with `MockWebServer`. The `MockWebServer` library now\n    depends on JUnit, though it continues to work with all testing frameworks.\n *  Fix: `FormEncodingBuilder` is now consistent with browsers in which\n    characters it escapes. Previously we weren’t percent-encoding commas,\n    parens, and other characters.\n *  Fix: Relax `FormEncodingBuilder` to support building empty forms.\n *  Fix: Timeouts throw `SocketTimeoutException`, not `InterruptedIOException`.\n *  Fix: Change `MockWebServer` to use the same logic as OkHttp when determining\n    whether an HTTP request permits a body.\n *  Fix: `HttpUrl` now uses the canonical form for IPv6 addresses.\n *  Fix: Use `HttpUrl` internally.\n *  Fix: Recover from Android 4.2.2 EBADF crashes.\n *  Fix: Don't crash with an `IllegalStateException` if an HTTP/2 or SPDY\n    write fails, leaving the connection in an inconsistent state.\n *  Fix: Make sure the default user agent is ASCII.\n\n\n## Version 2.4.0\n\n_2015-05-22_\n\n *  **Forbid response bodies on HTTP 204 and 205 responses.** Webservers that\n    return such malformed responses will now trigger a `ProtocolException` in\n    the client.\n\n *  **WebSocketListener has incompatible changes.** The `onOpen()` method is now\n    called on the reader thread, so implementations must return before further\n    websocket messages will be delivered. The `onFailure()` method now includes\n    an HTTP response if one was returned.\n\n## Version 2.4.0-RC1\n\n_2015-05-16_\n\n *  **New HttpUrl API.** It's like `java.net.URL` but good. Note that\n    `Request.Builder.url()` now throws `IllegalArgumentException` on malformed\n    URLs. (Previous releases would throw a `MalformedURLException` when calling\n    a malformed URL.)\n\n *  **We've improved connect failure recovery.** We now differentiate between\n    setup, connecting, and connected and implement appropriate recovery rules\n    for each. This changes `Address` to no longer use `ConnectionSpec`. (This is\n    an incompatible API change).\n\n *  **`FormEncodingBuilder` now uses `%20` instead of `+` for encoded spaces.**\n    Both are permitted-by-spec, but `%20` requires fewer special cases.\n\n *  **Okio has been updated to 1.4.0.**\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okio</groupId>\n       <artifactId>okio</artifactId>\n       <version>1.4.0</version>\n     </dependency>\n     ```\n\n *  **`Request.Builder` no longer accepts null if a request body is required.**\n    Passing null will now fail for request methods that require a body. Instead\n    use an empty body such as this one:\n\n    ```java\n        RequestBody.create(null, new byte[0]);\n    ```\n\n * **`CertificatePinner` now supports wildcard hostnames.** As always with\n   certificate pinning, you must be very careful to avoid [bricking][brick]\n   your app. You'll need to pin both the top-level domain and the `*.` domain\n   for full coverage.\n\n    ```java\n     client.setCertificatePinner(new CertificatePinner.Builder()\n         .add(\"publicobject.com\",   \"sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=\")\n         .add(\"*.publicobject.com\", \"sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=\")\n         .add(\"publicobject.com\",   \"sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=\")\n         .add(\"*.publicobject.com\", \"sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=\")\n         .add(\"publicobject.com\",   \"sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=\")\n         .add(\"*.publicobject.com\", \"sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=\")\n         .add(\"publicobject.com\",   \"sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=\")\n         .add(\"*.publicobject.com\", \"sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=\")\n         .build());\n    ```\n\n *  **Interceptors lists are now deep-copied by `OkHttpClient.clone()`.**\n    Previously clones shared interceptors, which made it difficult to customize\n    the interceptors on a request-by-request basis.\n\n *  New: `Headers.toMultimap()`.\n *  New: `RequestBody.create(MediaType, ByteString)`.\n *  New: `ConnectionSpec.isCompatible(SSLSocket)`.\n *  New: `Dispatcher.getQueuedCallCount()` and\n    `Dispatcher.getRunningCallCount()`. These can be useful in diagnostics.\n *  Fix: OkHttp no longer shares timeouts between pooled connections. This was\n    causing some applications to crash when connections were reused.\n *  Fix: `OkApacheClient` now allows an empty `PUT` and `POST`.\n *  Fix: Websockets no longer rebuffer socket streams.\n *  Fix: Websockets are now better at handling close frames.\n *  Fix: Content type matching is now case insensitive.\n *  Fix: `Vary` headers are not lost with `android.net.http.HttpResponseCache`.\n *  Fix: HTTP/2 wasn't enforcing stream timeouts when writing the underlying\n    connection. Now it is.\n *  Fix: Never return null on `call.proceed()`. This was a bug in call\n    cancelation.\n *  Fix: When a network interceptor mutates a request, that change is now\n    reflected in `Response.networkResponse()`.\n *  Fix: Badly-behaving caches now throw a checked exception instead of a\n    `NullPointerException`.\n *  Fix: Better handling of uncaught exceptions in MockWebServer with HTTP/2.\n\n## Version 2.3.0\n\n_2015-03-16_\n\n *  **HTTP/2 support.** We've done interop testing and haven't seen any\n    problems. HTTP/2 support has been a big effort and we're particularly\n    thankful to Adrian Cole who has helped us to reach this milestone.\n\n *  **RC4 cipher suites are no longer supported by default.** To connect to\n    old, obsolete servers relying on these cipher suites, you must create a\n    custom `ConnectionSpec`.\n\n *  **Beta WebSockets support.**. The `okhttp-ws` subproject offers a new\n    websockets client. Please try it out! When it's ready we intend to include\n    it with the core OkHttp library.\n\n *  **Okio updated to 1.3.0.**\n\n    ```xml\n    <dependency>\n      <groupId>com.squareup.okio</groupId>\n      <artifactId>okio</artifactId>\n      <version>1.3.0</version>\n    </dependency>\n    ```\n\n *  **Fix: improve parallelism of async requests.** OkHttp's Dispatcher had a\n    misconfigured `ExecutorService` that limited the number of worker threads.\n    If you're using `Call.enqueue()` this update should significantly improve\n    request concurrency.\n\n *  **Fix: Lazily initialize the response cache.** This avoids strict mode\n    warnings when initializing OkHttp on Android‘s main thread.\n\n *  **Fix: Disable ALPN on Android 4.4.** That release of the feature was\n    unstable and prone to native crashes in the underlying OpenSSL code.\n *  Fix: Don't send both `If-None-Match` and `If-Modified-Since` cache headers\n    when both are applicable.\n *  Fix: Fail early when a port is out of range.\n *  Fix: Offer `Content-Length` headers for multipart request bodies.\n *  Fix: Throw `UnknownServiceException` if a cleartext connection is attempted\n    when explicitly forbidden.\n *  Fix: Throw a `SSLPeerUnverifiedException` when host verification fails.\n *  Fix: MockWebServer explicitly closes sockets. (On some Android releases,\n    closing the input stream and output stream of a socket is not sufficient.\n *  Fix: Buffer outgoing HTTP/2 frames to limit how many outgoing frames are\n    created.\n *  Fix: Avoid crashing when cache writing fails due to a full disk.\n *  Fix: Improve caching of private responses.\n *  Fix: Update cache-by-default response codes.\n *  Fix: Reused `Request.Builder` instances no longer hold stale URL fields.\n *  New: ConnectionSpec can now be configured to use the SSL socket's default\n    cipher suites. To use, set the cipher suites to `null`.\n *  New: Support `DELETE` with a request body.\n *  New: `Headers.of(Map)` creates headers from a Map.\n\n\n## Version 2.2.0\n\n_2014-12-30_\n\n *  **`RequestBody.contentLength()` now throws `IOException`.**\n    This is a source-incompatible change. If you have code that calls\n    `RequestBody.contentLength()`, your compile will break with this\n    update. The change is binary-compatible, however: code compiled\n    for OkHttp 2.0 and 2.1 will continue to work with this update.\n\n *  **`COMPATIBLE_TLS` no longer supports SSLv3.** In response to the\n    [POODLE](https://googleonlinesecurity.blogspot.ca/2014/10/this-poodle-bites-exploiting-ssl-30.html)\n    vulnerability, OkHttp no longer offers SSLv3 when negotiation an\n    HTTPS connection. If you continue to need to connect to webservers\n    running SSLv3, you must manually configure your own `ConnectionSpec`.\n\n *  **OkHttp now offers interceptors.** Interceptors are a powerful mechanism\n    that can monitor, rewrite, and retry calls. The [interceptors doc][interceptors] is a full\n    introduction to this new API.\n\n *  New: APIs to iterate and selectively clear the response cache.\n *  New: Support for SOCKS proxies.\n *  New: Support for `TLS_FALLBACK_SCSV`.\n *  New: Update HTTP/2 support to `h2-16` and `hpack-10`.\n *  New: APIs to prevent retrying non-idempotent requests.\n *  Fix: Drop NPN support. Going forward we support ALPN only.\n *  Fix: The hostname verifier is now strict. This is consistent with the hostname\n    verifier in modern browsers.\n *  Fix: Improve `CONNECT` handling for misbehaving HTTP proxies.\n *  Fix: Don't retry requests that failed due to timeouts.\n *  Fix: Cache 302s and 308s that include appropriate response headers.\n *  Fix: Improve pooling of connections that use proxy selectors.\n *  Fix: Don't leak connections when using ALPN on the desktop.\n *  Fix: Update Jetty ALPN to `7.1.2.v20141202` (Java 7) and `8.1.2.v20141202` (Java 8).\n    This fixes a bug in resumed TLS sessions where the wrong protocol could be\n    selected.\n *  Fix: Don't crash in SPDY and HTTP/2 when disconnecting before connecting.\n *  Fix: Avoid a reverse DNS-lookup for a numeric proxy address\n *  Fix: Resurrect http/2 frame logging.\n *  Fix: Limit to 20 authorization attempts.\n\n## Version 2.1.0\n\n_2014-11-11_\n\n *  New: Typesafe APIs for interacting with cipher suites and TLS versions.\n *  Fix: Don't crash when mixing authorization challenges with upload retries.\n\n\n## Version 2.1.0-RC1\n\n_2014-11-04_\n\n *  **OkHttp now caches private responses**. We've changed from a shared cache\n    to a private cache, and will now store responses that use an `Authorization`\n    header. This means OkHttp's cache shouldn't be used on middleboxes that sit\n    between user agents and the origin server.\n\n *  **TLS configuration updated.** OkHttp now explicitly enables TLSv1.2,\n    TLSv1.1 and TLSv1.0 where they are supported. It will continue to perform\n    only one fallback, to SSLv3. Applications can now configure this with the\n    `ConnectionSpec` class.\n\n    To disable TLS fallback:\n\n    ```java\n    client.setConnectionSpecs(Arrays.asList(\n        ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT));\n    ```\n\n    To disable cleartext connections, permitting `https` URLs only:\n\n    ```java\n    client.setConnectionSpecs(Arrays.asList(\n        ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS));\n    ```\n\n *  **New cipher suites.** Please confirm that your webservers are reachable\n    with this limited set of cipher suites.\n\n    ```\n                                             Android\n    Name                                     Version\n\n    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256  5.0\n    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256    5.0\n    TLS_DHE_RSA_WITH_AES_128_GCM_SHA256      5.0\n    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA     4.0\n    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA     4.0\n    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA       4.0\n    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA       4.0\n    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA         4.0\n    TLS_ECDHE_RSA_WITH_RC4_128_SHA           4.0\n    TLS_DHE_RSA_WITH_AES_128_CBC_SHA         2.3\n    TLS_DHE_DSS_WITH_AES_128_CBC_SHA         2.3\n    TLS_DHE_RSA_WITH_AES_256_CBC_SHA         2.3\n    TLS_RSA_WITH_AES_128_GCM_SHA256          5.0\n    TLS_RSA_WITH_AES_128_CBC_SHA             2.3\n    TLS_RSA_WITH_AES_256_CBC_SHA             2.3\n    SSL_RSA_WITH_3DES_EDE_CBC_SHA            2.3  (Deprecated in 5.0)\n    SSL_RSA_WITH_RC4_128_SHA                 2.3\n    SSL_RSA_WITH_RC4_128_MD5                 2.3  (Deprecated in 5.0)\n    ```\n\n *  **Okio updated to 1.0.1.**\n\n    ```xml\n    <dependency>\n      <groupId>com.squareup.okio</groupId>\n      <artifactId>okio</artifactId>\n      <version>1.0.1</version>\n    </dependency>\n    ```\n\n *  **New APIs to permit easy certificate pinning.** Be warned, certificate\n    pinning is dangerous and could prevent your application from trusting your\n    server!\n\n *  **Cache improvements.** This release fixes some severe cache problems\n    including a bug where the cache could be corrupted upon certain access\n    patterns. We also fixed a bug where the cache was being cleared due to a\n    corrupted journal. We've added APIs to configure a request's `Cache-Control`\n    headers, and to manually clear the cache.\n\n *  **Request cancellation fixes.** This update fixes a bug where synchronous\n    requests couldn't be canceled by tag. This update avoids crashing when\n    `onResponse()` throws an `IOException`. That failure will now be logged\n    instead of notifying the thread's uncaught exception handler. We've added a\n    new API, `Call.isCanceled()` to check if a call has been canceled.\n\n *  New: Update `MultipartBuilder` to support content length.\n *  New: Make it possible to mock `OkHttpClient` and `Call`.\n *  New: Update to h2-14 and hpack-9.\n *  New: OkHttp includes a user-agent by default, like `okhttp/2.1.0-RC1`.\n *  Fix: Handle response code `308 Permanent Redirect`.\n *  Fix: Don't skip the callback if a call is canceled.\n *  Fix: Permit hostnames with underscores.\n *  Fix: Permit overriding the content-type in `OkApacheClient`.\n *  Fix: Use the socket factory for direct connections.\n *  Fix: Honor `OkUrlFactory` APIs that disable redirects.\n *  Fix: Don't crash on concurrent modification of `SPDY` SPDY settings.\n\n## Version 2.0.0\n\nThis release commits to a stable 2.0 API. Read the 2.0.0-RC1 changes for advice\non upgrading from 1.x to 2.x.\n\n_2014-06-21_\n\n *  **API Change**: Use `IOException` in `Callback.onFailure()`. This is\n    a source-incompatible change, and is different from OkHttp 2.0.0-RC2 which\n    used `Throwable`.\n *  Fix: Fixed a caching bug where we weren't storing rewritten request headers\n    like `Accept-Encoding`.\n *  Fix: Fixed bugs in handling the SPDY window size. This was stalling certain\n    large downloads\n *  Update the language level to Java 7. (OkHttp requires Android 2.3+ or Java 7+.)\n\n## Version 2.0.0-RC2\n\n_2014-06-11_\n\nThis update fixes problems in 2.0.0-RC1. Read the 2.0.0-RC1 changes for\nadvice on upgrading from 1.x to 2.x.\n\n *  Fix: Don't leak connections! There was a regression in 2.0.0-RC1 where\n    connections were neither closed nor pooled.\n *  Fix: Revert builder-style return types from OkHttpClient's timeout methods\n    for binary compatibility with OkHttp 1.x.\n *  Fix: Don't skip client stream 1 on SPDY/3.1. This fixes SPDY connectivity to\n    `https://google.com`, which doesn't follow the SPDY/3.1 spec!\n *  Fix: Always configure NPN headers. This fixes connectivity to\n    `https://facebook.com` when SPDY and HTTP/2 are both disabled. Otherwise an\n    unexpected NPN response is received and OkHttp crashes.\n *  Fix: Write continuation frames when HPACK data is larger than 16383 bytes.\n *  Fix: Don't drop uncaught exceptions thrown in async calls.\n *  Fix: Throw an exception eagerly when a request body is not legal. Previously\n    we ignored the problem at request-building time, only to crash later with a\n    `NullPointerException`.\n *  Fix: Include a backwards-compatible `OkHttp-Response-Source` header with\n    `OkUrlFactory `responses.\n *  Fix: Don't include a default User-Agent header in requests made with the Call\n    API. Requests made with OkUrlFactory will continue to have a default user\n    agent.\n *  New: Guava-like API to create headers:\n\n    ```java\n    Headers headers = Headers.of(name1, value1, name2, value2, ...).\n    ```\n\n *  New: Make the content-type header optional for request bodies.\n *  New: `Response.isSuccessful()` is a convenient API to check response codes.\n *  New: The response body can now be read outside of the callback. Response\n    bodies must always be closed, otherwise they will leak connections!\n *  New: APIs to create multipart request bodies (`MultipartBuilder`) and form\n    encoding bodies (`FormEncodingBuilder`).\n\n## Version 2.0.0-RC1\n\n_2014-05-23_\n\nOkHttp 2 is designed around a new API that is true to HTTP, with classes for\nrequests, responses, headers, and calls. It uses modern Java patterns like\nimmutability and chained builders. The API now offers asynchronous callbacks\nin addition to synchronous blocking calls.\n\n#### API Changes\n\n *  **New Request and Response types,** each with their own builder. There's also\n    a `RequestBody` class to write the request body to the network and a\n    `ResponseBody` to read the response body from the network. The standalone\n    `Headers` class offers full access to the HTTP headers.\n\n *  **Okio dependency added.** OkHttp now depends on\n    [Okio](https://github.com/square/okio), an I/O library that makes it easier\n    to access, store and process data. Using this library internally makes OkHttp\n    faster while consuming less memory. You can write a `RequestBody` as an Okio\n    `BufferedSink` and a `ResponseBody` as an Okio `BufferedSource`. Standard\n    `InputStream` and `OutputStream` access is also available.\n\n *  **New Call and Callback types** execute requests and receive their\n    responses. Both types of calls can be canceled via the `Call` or the\n    `OkHttpClient`.\n\n *  **URLConnection support has moved to the okhttp-urlconnection module.**\n    If you're upgrading from 1.x, this change will impact you. You will need to\n    add the `okhttp-urlconnection` module to your project and use the\n    `OkUrlFactory` to create new instances of `HttpURLConnection`:\n\n    ```java\n    // OkHttp 1.x:\n    HttpURLConnection connection = client.open(url);\n\n    // OkHttp 2.x:\n    HttpURLConnection connection = new OkUrlFactory(client).open(url);\n    ```\n\n *  **Custom caches are no longer supported.** In OkHttp 1.x it was possible to\n    define your own response cache with the `java.net.ResponseCache` and OkHttp's\n    `OkResponseCache` interfaces. Both of these APIs have been dropped. In\n    OkHttp 2 the built-in disk cache is the only supported response cache.\n\n *  **HttpResponseCache has been renamed to Cache.** Install it with\n    `OkHttpClient.setCache(...)` instead of `OkHttpClient.setResponseCache(...)`.\n\n *  **OkAuthenticator has been replaced with Authenticator.** This new\n    authenticator has access to the full incoming response and can respond with\n    whichever followup request is appropriate. The `Challenge` class is now a\n    top-level class and `Credential` is replaced with a utility class called\n    `Credentials`.\n\n *  **OkHttpClient.getFollowProtocolRedirects() renamed to\n    getFollowSslRedirects()**. We reserve the word _protocol_ for the HTTP\n    version being used (HTTP/1.1, HTTP/2). The old name of this method was\n    misleading; it was always used to configure redirects between `https://` and\n    `http://` schemes.\n\n *  **RouteDatabase is no longer public API.** OkHttp continues to track which\n    routes have failed but this is no exposed in the API.\n\n *  **ResponseSource is gone.** This enum exposed whether a response came from\n    the cache, network, or both. OkHttp 2 offers more detail with raw access to\n    the cache and network responses in the new `Response` class.\n\n *  **TunnelRequest is gone.** It specified how to connect to an HTTP proxy.\n    OkHttp 2 uses the new `Request` class for this.\n\n *  **Dispatcher** is a new class that manages the queue of asynchronous calls. It\n    implements limits on total in-flight calls and in-flight calls per host.\n\n#### Implementation changes\n\n * Support Android `TrafficStats` socket tagging.\n * Drop authentication headers on redirect.\n * Added support for compressed data frames.\n * Process push promise callbacks in order.\n * Update to http/2 draft 12.\n * Update to HPACK draft 07.\n * Add ALPN support. Maven will use ALPN on OpenJDK 8.\n * Update NPN dependency to target `jdk7u60-b13` and `Oracle jdk7u55-b13`.\n * Ensure SPDY variants support zero-length DELETE and POST.\n * Prevent leaking a cache item's InputStreams when metadata read fails.\n * Use a string to identify TLS versions in routes.\n * Add frame logger for HTTP/2.\n * Replacing `httpMinorVersion` with `Protocol`. Expose HTTP/1.0 as a potential protocol.\n * Use `Protocol` to describe framing.\n * Implement write timeouts for HTTP/1.1 streams.\n * Avoid use of SPDY stream ID 1, as that's typically used for UPGRADE.\n * Support OAuth in `Authenticator`.\n * Permit a dangling semicolon in media type parsing.\n\n\n## Version 1.x\n\n[Change log](changelog_1x.md)\n\n\n [brick]: https://noncombatant.org/2015/05/01/about-http-public-key-pinning/\n [interceptors]: https://square.github.io/okhttp/interceptors/\n [webdav]: https://tools.ietf.org/html/rfc4918\n"
  },
  {
    "path": "docs/changelogs/changelog_3x.md",
    "content": "OkHttp 3.x Change Log\n=====================\n\n## Version 3.14.9\n\n_2020-05-17_\n\n *  Fix: Don't crash when running as a plugin in Android Studio Canary 4.1. To enable\n    platform-specific TLS features OkHttp must detect whether it's running in a JVM or in Android.\n    The upcoming Android Studio runs in a JVM but has classes from Android and that confused OkHttp!\n\n\n## Version 3.14.8\n\n_2020-04-28_\n\n *  Fix: Don't crash on Java 8u252 which introduces an API previously found only on Java 9 and\n    above. See [Jetty's overview][jetty_8_252] of the API change and its consequences.\n\n## Version 3.14.7\n\n_2020-02-24_\n\n *  Fix: Don't crash on Android 11 due to use of restricted methods. This prevents a crash with the\n    exception, \"Expected Android API level 21+ but was 29\".\n\n\n## Version 3.14.6\n\n_2020-01-11_\n\n *  Fix: Don't crash if the connection is closed when sending a degraded ping. This fixes a\n    regression that was introduced in OkHttp 3.14.5.\n\n\n## Version 3.14.5\n\n_2020-01-03_\n\n *  Fix: Degrade HTTP/2 connections after a timeout. When an HTTP/2 stream times out it may impact\n    the stream only or the entire connection. With this fix OkHttp will now send HTTP/2 pings after\n    a stream timeout to determine whether the connection should remain eligible for pooling.\n\n\n## Version 3.14.4\n\n_2019-09-29_\n\n *  Fix: Cancel calls that fail due to unexpected exceptions. We had a bug where an enqueued call\n    would never call back if it crashed with an unchecked throwable, such as a\n    `NullPointerException` or `OutOfMemoryError`. We now call `Callback.onFailure()` with an\n    `IOException` that reports the call as canceled. The triggering exception is still delivered to\n    the thread's `UncaughtExceptionHandler`.\n *  Fix: Don't evict incomplete entries when iterating the cache. We had a bug where iterating\n    `Cache.urls()` would prevent in-flight entries from being written.\n\n\n## Version 3.14.3\n\n_2019-09-10_\n\n *  Fix: Don't lose HTTP/2 flow control bytes when incoming data races with a stream close. If this\n    happened enough then eventually the connection would stall.\n\n *  Fix: Acknowledge and apply inbound HTTP/2 settings atomically. Previously we had a race where we\n    could use new flow control capacity before acknowledging it, causing strict HTTP/2 servers to\n    fail the call.\n\n *  Fix: Recover gracefully when a coalesced connection immediately goes unhealthy.\n\n## Version 3.14.2\n\n_2019-05-19_\n\n *  Fix: Lock in a route when recovering from an HTTP/2 connection error. We had a bug where two\n    calls that failed at the same time could cause OkHttp to crash with a `NoSuchElementException`\n    instead of the expected `IOException`.\n\n *  Fix: Don't crash with a `NullPointerException` when formatting an error message describing a\n    truncated response from an HTTPS proxy.\n\n\n## Version 3.14.1\n\n_2019-04-10_\n\n *  Fix: Don't crash when an interceptor retries when there are no more routes. This was an\n    edge-case regression introduced with the events cleanup in 3.14.0.\n\n *  Fix: Provide actionable advice when the exchange is non-null. Prior to 3.14, OkHttp would\n    silently leak connections when an interceptor retries without closing the response body. With\n    3.14 we detect this problem but the exception was not helpful.\n\n## Version 3.14.0\n\n_2019-03-14_\n\n *  **This release deletes the long-deprecated `OkUrlFactory` and `OkApacheClient` APIs.** These\n    facades hide OkHttp's implementation behind another client's API. If you still need this please\n    copy and paste [ObsoleteUrlFactory.java][obsolete_url_factory] or\n    [ObsoleteApacheClient.java][obsolete_apache_client] into your project.\n\n *  **OkHttp now supports duplex calls over HTTP/2.** With normal HTTP calls the request must finish\n    before the response starts. With duplex, request and response bodies are transmitted\n    simultaneously. This can be used to implement interactive conversations within a single HTTP\n    call.\n\n    Create duplex calls by overriding the new `RequestBody.isDuplex()` method to return true.\n    This simple option dramatically changes the behavior of the request body and of the entire\n    call.\n\n    The `RequestBody.writeTo()` method may now retain a reference to the provided sink and\n    hand it off to another thread to write to it after `writeTo` returns.\n\n    The `EventListener` may now see requests and responses interleaved in ways not previously\n    permitted. For example, a listener may receive `responseHeadersStart()` followed by\n    `requestBodyEnd()`, both on the same call. Such events may be triggered by different threads\n    even for a single call.\n\n    Interceptors that rewrite or replace the request body may now inadvertently interfere with\n    duplex request bodies. Such interceptors should check `RequestBody.isDuplex()` and avoid\n    accessing the request body when it is.\n\n    Duplex calls require HTTP/2. If HTTP/1 is established instead the duplex call will fail. The\n    most common use of duplex calls is [gRPC][grpc_http2].\n\n *  New: Prevent OkHttp from retransmitting a request body by overriding `RequestBody.isOneShot()`.\n    This is most useful when writing the request body is destructive.\n\n *  New: We've added `requestFailed()` and `responseFailed()` methods to `EventListener`. These\n    are called instead of `requestBodyEnd()` and `responseBodyEnd()` in some failure situations.\n    They may also be fired in cases where no event was published previously. In this release we did\n    an internal rewrite of our event code to fix problems where events were lost or unbalanced.\n\n *  Fix: Don't leak a connection when a call is canceled immediately preceding the `onFailure()`\n    callback.\n\n *  Fix: Apply call timeouts when connecting duplex calls, web sockets, and server-sent events.\n    Once the streams are established no further timeout is enforced.\n\n *  Fix: Retain the `Route` when a connection is reused on a redirect or other follow-up. This was\n    causing some `Authenticator` calls to see a null route when non-null was expected.\n\n *  Fix: Use the correct key size in the name of `TLS_AES_128_CCM_8_SHA256` which is a TLS 1.3\n    cipher suite. We accidentally specified a key size of 256, preventing that cipher suite from\n    being selected for any TLS handshakes. We didn't notice because this cipher suite isn't\n    supported on Android, Java, or Conscrypt.\n\n    We removed this cipher suite and `TLS_AES_128_CCM_SHA256` from the restricted, modern, and\n    compatible sets of cipher suites. These two cipher suites aren't enabled by default in either\n    Firefox or Chrome.\n\n    See our [TLS Configuration History][tls_configuration_history] tracker for a log of all changes\n    to OkHttp's default TLS options.\n\n *  New: Upgrade to Conscrypt 2.0.0. OkHttp works with other versions of Conscrypt but this is the\n    version we're testing against.\n\n    ```kotlin\n    implementation(\"org.conscrypt:conscrypt-openjdk-uber:2.0.0\")\n    ```\n\n *  New: Update the embedded public suffixes list.\n\n\n## Version 3.13.1\n\n_2019-02-05_\n\n *  Fix: Don't crash when using a custom `X509TrustManager` or `SSLSocket` on Android. When we\n    removed obsolete code for Android 4.4 we inadvertently also removed support for custom\n    subclasses. We've restored that support!\n\n\n## Version 3.13.0\n\n_2019-02-04_\n\n *  **This release bumps our minimum requirements to Java 8+ or Android 5+.** Cutting off old\n    devices is a serious change and we don't do it lightly! [This post][require_android_5] explains\n    why we're doing this and how to upgrade.\n\n    The OkHttp 3.12.x branch will be our long-term branch for Android 2.3+ (API level 9+) and Java\n    7+. These platforms lack support for TLS 1.2 and should not be used. But because upgrading is\n    difficult we will backport critical fixes to the 3.12.x branch through December 31, 2021. (This\n    commitment was originally through December 31, 2020; we have since extended it.)\n\n *  **TLSv1 and TLSv1.1 are no longer enabled by default.** Major web browsers are working towards\n    removing these versions altogether in early 2020. If your servers aren't ready yet you can\n    configure OkHttp 3.13 to allow TLSv1 and TLSv1.1 connections:\n\n    ```\n    OkHttpClient client = new OkHttpClient.Builder()\n        .connectionSpecs(Arrays.asList(ConnectionSpec.COMPATIBLE_TLS))\n        .build();\n    ```\n\n *  New: You can now access HTTP trailers with `Response.trailers()`. This method may only be called\n    after the entire HTTP response body has been read.\n\n *  New: Upgrade to Okio 1.17.3. If you're on Kotlin-friendly Okio 2.x this release requires 2.2.2\n    or newer.\n\n    ```kotlin\n    implementation(\"com.squareup.okio:okio:1.17.3\")\n    ```\n\n *  Fix: Don't miss cancels when sending HTTP/2 request headers.\n *  Fix: Don't miss whole operation timeouts when calls redirect.\n *  Fix: Don't leak connections if web sockets have malformed responses or if `onOpen()` throws.\n *  Fix: Don't retry when request bodies fail due to `FileNotFoundException`.\n *  Fix: Don't crash when URLs have IPv4-mapped IPv6 addresses.\n *  Fix: Don't crash when building `HandshakeCertificates` on Android API 28.\n *  Fix: Permit multipart file names to contain non-ASCII characters.\n *  New: API to get MockWebServer's dispatcher.\n *  New: API to access headers as `java.time.Instant`.\n *  New: Fail fast if a `SSLSocketFactory` is used as a `SocketFactory`.\n *  New: Log the TLS handshake in `LoggingEventListener`.\n\n## Version 3.12.13\n\n_2021-01-30_\n\n *  Fix: Work around a crash in Android 10 and 11 that may be triggered when two threads\n    concurrently close an SSL socket. This would have appeared in crash logs as\n    `NullPointerException: bio == null`.\n\n\n## Version 3.12.12\n\n_2020-05-17_\n\n *  Fix: Don't crash when running as a plugin in Android Studio Canary 4.1. To enable\n    platform-specific TLS features OkHttp must detect whether it's running in a JVM or in Android.\n    The upcoming Android Studio runs in a JVM but has classes from Android and that confused OkHttp!\n\n\n## Version 3.12.11\n\n_2020-04-28_\n\n *  Fix: Don't crash on Java 8u252 which introduces an API previously found only on Java 9 and\n    above. See [Jetty's overview][jetty_8_252] of the API change and its consequences.\n\n\n## Version 3.12.10\n\n_2020-02-29_\n\n *  Fix: Don't crash on Android 4.1 when detecting methods that became restricted in Android 11.\n    Supporting a full decade of Android releases on our 3.12.x branch is tricky!\n\n\n## Version 3.12.9\n\n_2020-02-24_\n\n *  Fix: Don't crash on Android 11 due to use of restricted methods. This prevents a crash with the\n    exception, \"Expected Android API level 21+ but was 29\".\n\n\n## Version 3.12.8\n\n_2020-01-11_\n\n *  Fix: Don't crash if the connection is closed when sending a degraded ping. This fixes a\n    regression that was introduced in OkHttp 3.12.7.\n\n\n## Version 3.12.7\n\n_2020-01-03_\n\n *  Fix: Degrade HTTP/2 connections after a timeout. When an HTTP/2 stream times out it may impact\n    the stream only or the entire connection. With this fix OkHttp will now send HTTP/2 pings after\n    a stream timeout to determine whether the connection should remain eligible for pooling.\n\n\n## Version 3.12.6\n\n_2019-09-29_\n\n *  Fix: Cancel calls that fail due to unexpected exceptions. We had a bug where an enqueued call\n    would never call back if it crashed with an unchecked throwable, such as a\n    `NullPointerException` or `OutOfMemoryError`. We now call `Callback.onFailure()` with an\n    `IOException` that reports the call as canceled. The triggering exception is still delivered to\n    the thread's `UncaughtExceptionHandler`.\n *  Fix: Don't evict incomplete entries when iterating the cache. We had a bug where iterating\n    `Cache.urls()` would prevent in-flight entries from being written.\n\n\n## Version 3.12.5\n\n_2019-09-10_\n\n *  Fix: Don't lose HTTP/2 flow control bytes when incoming data races with a stream close. If this\n    happened enough then eventually the connection would stall.\n\n *  Fix: Acknowledge and apply inbound HTTP/2 settings atomically. Previously we had a race where we\n    could use new flow control capacity before acknowledging it, causing strict HTTP/2 servers to\n    fail the call.\n\n\n## Version 3.12.4\n\n_2019-09-04_\n\n *  Fix: Don't crash looking up an absent class on certain buggy Android 4.x devices.\n\n\n## Version 3.12.3\n\n_2019-05-07_\n\n *  Fix: Permit multipart file names to contain non-ASCII characters.\n *  Fix: Retain the `Route` when a connection is reused on a redirect or other follow-up. This was\n    causing some `Authenticator` calls to see a null route when non-null was expected.\n\n\n## Version 3.12.2\n\n_2019-03-14_\n\n *  Fix: Don't crash if the HTTPS server returns no certificates in the TLS handshake.\n *  Fix: Don't leak a connection when a call is canceled immediately preceding the `onFailure()`\n    callback.\n\n\n## Version 3.12.1\n\n_2018-12-23_\n\n *  Fix: Remove overlapping `package-info.java`. This caused issues with some build tools.\n\n\n## Version 3.12.0\n\n_2018-11-16_\n\n *  **OkHttp now supports TLS 1.3.** This requires either Conscrypt or Java 11+.\n\n *  **Proxy authenticators are now asked for preemptive authentication.** OkHttp will now request\n    authentication credentials before creating TLS tunnels through HTTP proxies (HTTP `CONNECT`).\n    Authenticators should identify preemptive authentications by the presence of a challenge whose\n    scheme is \"OkHttp-Preemptive\".\n\n *  **OkHttp now offers full-operation timeouts.** This sets a limit on how long the entire call may\n    take and covers resolving DNS, connecting, writing the request body, server processing, and\n    reading the full response body. If a call requires redirects or retries all must complete within\n    one timeout period.\n\n    Use `OkHttpClient.Builder.callTimeout()` to specify the default duration and `Call.timeout()` to\n    specify the timeout of an individual call.\n\n *  New: Return values and fields are now non-null unless otherwise annotated.\n *  New: `LoggingEventListener` makes it easy to get basic visibility into a call's performance.\n    This class is in the `logging-interceptor` artifact.\n *  New: `Headers.Builder.addUnsafeNonAscii()` allows non-ASCII values to be added without an\n    immediate exception.\n *  New: Headers can be redacted in `HttpLoggingInterceptor`.\n *  New: `Headers.Builder` now accepts dates.\n *  New: OkHttp now accepts `java.time.Duration` for timeouts on Java 8+ and Android 26+.\n *  New: `Challenge` includes all authentication parameters.\n *  New: Upgrade to BouncyCastle 1.60, Conscrypt 1.4.0, and Okio 1.15.0. We don't yet require\n    Kotlin-friendly Okio 2.x but OkHttp works fine with that series.\n\n    ```kotlin\n    implementation(\"org.bouncycastle:bcprov-jdk15on:1.60\")\n    implementation(\"org.conscrypt:conscrypt-openjdk-uber:1.4.0\")\n    implementation(\"com.squareup.okio:okio:1.15.0\")\n    ```\n\n *  Fix: Handle dispatcher executor shutdowns gracefully. When there aren't any threads to carry a\n    call its callback now gets a `RejectedExecutionException`.\n *  Fix: Don't permanently cache responses with `Cache-Control: immutable`. We misunderstood the\n    original `immutable` proposal!\n *  Fix: Change `Authenticator`'s `Route` parameter to be nullable. This was marked as non-null but\n    could be called with null in some cases.\n *  Fix: Don't create malformed URLs when `MockWebServer` is reached via an IPv6 address.\n *  Fix: Don't crash if the system default authenticator is null.\n *  Fix: Don't crash generating elliptic curve certificates on Android.\n *  Fix: Don't crash doing platform detection on RoboVM.\n *  Fix: Don't leak socket connections when web socket upgrades fail.\n\n\n## Version 3.11.0\n\n_2018-07-12_\n\n *  **OkHttp's new okhttp-tls submodule tames HTTPS and TLS.**\n\n    `HeldCertificate` is a TLS certificate and its private key. Generate a certificate with its\n    builder then use it to sign another certificate or perform a TLS handshake. The\n    `certificatePem()` method encodes the certificate in the familiar PEM format\n    (`--- BEGIN CERTIFICATE ---`); the `privateKeyPkcs8Pem()` does likewise for the private key.\n\n    `HandshakeCertificates` holds the TLS certificates required for a TLS handshake. On the server\n    it keeps your `HeldCertificate` and its chain. On the client it keeps the root certificates\n    that are trusted to sign a server's certificate chain. `HandshakeCertificates` also works with\n    mutual TLS where these roles are reversed.\n\n    These classes make it possible to enable HTTPS in MockWebServer in [just a few lines of\n    code][https_server_sample].\n\n *  **OkHttp now supports prior knowledge cleartext HTTP/2.** Enable this by setting\n    `Protocol.H2_PRIOR_KNOWLEDGE` as the lone protocol on an `OkHttpClient.Builder`. This mode\n    only supports `http:` URLs and is best suited in closed environments where HTTPS is\n    inappropriate.\n\n *  New: `HttpUrl.get(String)` is an alternative to `HttpUrl.parse(String)` that throws an exception\n    when the URL is malformed instead of returning null. Use this to avoid checking for null in\n    situations where the input is known to be well-formed. We've also added `MediaType.get(String)`\n    which is an exception-throwing alternative to `MediaType.parse(String)`.\n *  New: The `EventListener` API previewed in OkHttp 3.9 has graduated to a stable API. Use this\n    interface to track metrics and monitor HTTP requests' size and duration.\n *  New: `okhttp-dnsoverhttps` is an experimental API for doing DNS queries over HTTPS. Using HTTPS\n    for DNS offers better security and potentially better performance. This feature is a preview:\n    the API is subject to change.\n *  New: `okhttp-sse` is an early preview of Server-Sent Events (SSE). This feature is incomplete\n    and is only suitable for experimental use.\n *  New: MockWebServer now supports client authentication (mutual TLS). Call `requestClientAuth()`\n    to permit an optional client certificate or `requireClientAuth()` to require one.\n *  New: `RecordedRequest.getHandshake()` returns the HTTPS handshake of a request sent to\n    `MockWebServer`.\n *  Fix: Honor the `MockResponse` header delay in MockWebServer.\n *  Fix: Don't release HTTP/2 connections that have multiple canceled calls. We had a bug where\n    canceling calls would cause the shared HTTP/2 connection to be unnecessarily released. This\n    harmed connection reuse.\n *  Fix: Ensure canceled and discarded HTTP/2 data is not permanently counted against the limited\n    flow control window. We had a few bugs where window size accounting was broken when streams\n    were canceled or reset.\n *  Fix: Recover gracefully if the TLS session returns an unexpected version (`NONE`) or cipher\n    suite (`SSL_NULL_WITH_NULL_NULL`).\n *  Fix: Don't change Conscrypt configuration globally. We migrated from a process-wide setting to\n    configuring only OkHttp's TLS sockets.\n *  Fix: Prefer TLSv1.2 where it is available. On certain older platforms it is necessary to opt-in\n    to TLSv1.2.\n *  New: `Request.tag()` permits multiple tags. Use a `Class<?>` as a key to identify tags. Note\n    that `tag()` now returns null if the request has no tag. Previously this would return the\n    request itself.\n *  New: `Headers.Builder.addAll(Headers)`.\n *  New: `ResponseBody.create(MediaType, ByteString)`.\n *  New: Embed R8/ProGuard rules in the jar. These will be applied automatically by R8.\n *  Fix: Release the connection if `Authenticator` throws an exception.\n *  Fix: Change the declaration of `OkHttpClient.cache()` to return a `@Nullable Cache`. The return\n    value has always been nullable but it wasn't declared properly.\n *  Fix: Reverse suppression of connect exceptions. When both a call and its retry fail, we now\n    throw the initial exception which is most likely to be actionable.\n *  Fix: Retain interrupted state when throwing `InterruptedIOException`. A single interrupt should\n    now be sufficient to break out an in-flight OkHttp call.\n *  Fix: Don't drop a call to `EventListener.callEnd()` when the response body is consumed inside an\n    interceptor.\n\n\n## Version 3.10.0\n\n_2018-02-24_\n\n *  **The pingInterval() feature now aggressively checks connectivity for web\n    sockets and HTTP/2 connections.**\n\n    Previously if you configured a ping interval that would cause OkHttp to send\n    pings, but it did not track whether the reply pongs were received. With this\n    update OkHttp requires that every ping receive a response: if it does not\n    the connection will be closed and the listener's `onFailure()` method will\n    be called.\n\n    Web sockets have always been had pings, but pings on HTTP/2 connections is\n    new in this release. Pings are used for connections that are busy carrying\n    calls and for idle connections in the connection pool. (Pings do not impact\n    when pooled connections are evicted).\n\n    If you have a configured ping interval, you should confirm that it is long\n    enough for a roundtrip from client to server. If your ping interval is too\n    short, slow connections may be misinterpreted as failed connections. A ping\n    interval of 30 seconds is reasonable for most use cases.\n\n *  **OkHttp now supports [Conscrypt][conscrypt].** Conscrypt is a Java Security\n    Provider that integrates BoringSSL into the Java platform. Conscrypt\n    supports more cipher suites than the JVM’s default provider and may also\n    execute more efficiently.\n\n    To use it, first register a [Conscrypt dependency][conscrypt_dependency] in\n    your build system.\n\n    OkHttp will use Conscrypt if you set the `okhttp.platform` system property\n    to `conscrypt`.\n\n    Alternatively, OkHttp will also use Conscrypt if you install it as your\n    preferred security provider. To do so, add the following code to execute\n    before you create your `OkHttpClient`.\n\n    ```\n    Security.insertProviderAt(\n        new org.conscrypt.OpenSSLProvider(), 1);\n    ```\n\n    Conscrypt is the bundled security provider on Android so it is not necessary\n    to configure it on that platform.\n\n *  New: `HttpUrl.addQueryParameter()` percent-escapes more characters.\n    Previously several ASCII punctuation characters were not percent-escaped\n    when used with this method. This does not impact already-encoded query\n    parameters in APIs like `HttpUrl.parse()` and\n    `HttpUrl.Builder.addEncodedQueryParameter()`.\n *  New: CBC-mode ECDSA cipher suites have been removed from OkHttp's default\n    configuration: `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA` and\n    `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`. This tracks a [Chromium\n    change][remove_cbc_ecdsa] to remove these cipher suites because they are\n    fragile and rarely-used.\n *  New: Don't fall back to common name (CN) verification for hostnames. This\n    behavior was deprecated with RFC 2818 in May 2000 and was recently dropped\n    from major web browsers.\n *  New: Honor the `Retry-After` response header. HTTP 503 (Unavailable)\n    responses are retried automatically if this header is present and its delay\n    is 0 seconds. HTTP 408 (Client Timeout) responses are retried automatically\n    if the header is absent or its delay is 0 seconds.\n *  New: Allow request bodies for all HTTP methods except GET and HEAD.\n *  New: Automatic module name of `okhttp3` for use with the Java Platform\n    Module System.\n *  New: Log gzipped bodies when `HttpLoggingInterceptor` is used as a network\n    interceptor.\n *  New: `Protocol.QUIC` constant. This protocol is not supported but this\n    constant is included for completeness.\n *  New: Upgrade to Okio 1.14.0.\n\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okio</groupId>\n       <artifactId>okio</artifactId>\n       <version>1.14.0</version>\n     </dependency>\n\n     com.squareup.okio:okio:1.14.0\n     ```\n\n *  Fix: Handle `HTTP/1.1 100 Continue` status lines, even on requests that did\n    not send the `Expect: continue` request header.\n *  Fix: Do not count web sockets toward the dispatcher's per-host connection\n    limit.\n *  Fix: Avoid using invalid HTTPS sessions. This prevents OkHttp from crashing\n    with the error, `Unexpected TLS version: NONE`.\n *  Fix: Don't corrupt the response cache when a 304 (Not Modified) response\n    overrides the stored \"Content-Encoding\" header.\n *  Fix: Gracefully shut down the HTTP/2 connection before it exhausts the\n    namespace of stream IDs (~536 million streams).\n *  Fix: Never pass a null `Route` to `Authenticator`. There was a bug where\n    routes were omitted for eagerly-closed connections.\n\n## Version 3.9.1\n\n_2017-11-18_\n\n *  New: Recover gracefully when Android's DNS crashes with an unexpected\n    `NullPointerException`.\n *  New: Recover gracefully when Android's socket connections crash with an\n    unexpected `ClassCastException`.\n *  Fix: Don't include the URL's fragment in `encodedQuery()` when the query\n    itself is empty.\n\n## Version 3.9.0\n\n_2017-09-03_\n\n *  **Interceptors are more capable.** The `Chain` interface now offers access\n    to the call and can adjust all call timeouts. Note that this change is\n    source-incompatible for code that implements the `Chain` interface.\n    We don't expect this to be a problem in practice!\n\n *  **OkHttp has an experimental new API for tracking metrics.** The new\n    `EventListener` API is designed to help developers monitor HTTP requests'\n    size and duration. This feature is an unstable preview: the API is subject\n    to change, and the implementation is incomplete. This is a big new API we\n    are eager for feedback.\n\n *  New: Support ALPN via Google Play Services' Dynamic Security Provider. This\n    expands HTTP/2 support to older Android devices that have Google Play\n    Services.\n *  New: Consider all routes when looking for candidate coalesced connections.\n    This increases the likelihood that HTTP/2 connections will be shared.\n *  New: Authentication challenges and credentials now use a charset. Use this in\n    your authenticator to support user names and passwords with non-ASCII\n    characters.\n *  New: Accept a charset in `FormBody.Builder`. Previously form bodies were\n    always UTF-8.\n *  New: Support the `immutable` cache-control directive.\n *  Fix: Don't crash when an HTTP/2 call is redirected while the connection is\n    being shut down.\n *  Fix: Don't drop headers of healthy streams that raced with `GOAWAY` frames.\n    This bug would cause HTTP/2 streams to occasional hang when the connection\n    was shutting down.\n *  Fix: Honor `OkHttpClient.retryOnConnectionFailure()` when the response is a\n    HTTP 408 Request Timeout. If retries are enabled, OkHttp will retry exactly\n    once in response to a 408.\n *  Fix: Don't crash when reading the empty `HEAD` response body if it specifies\n    a `Content-Length`.\n *  Fix: Don't crash if the thread is interrupted while reading the public\n    suffix database.\n *  Fix: Use relative resource path when loading the public suffix database.\n    Loading the resource using a path relative to the class prevents conflicts\n    when the OkHttp classes are relocated (shaded) by allowing multiple private\n    copies of the database.\n *  Fix: Accept cookies for URLs that have an IPv6 address for a host.\n *  Fix: Don't log the protocol (HTTP/1.1, h2) in HttpLoggingInterceptor if the\n    protocol isn't negotiated yet! Previously we'd log HTTP/1.1 by default, and\n    this was confusing.\n *  Fix: Omit the message from MockWebServer's HTTP/2 `:status` header.\n *  Fix: Handle 'Expect: 100 Continue' properly in MockWebServer.\n\n\n## Version 3.8.1\n\n_2017-06-18_\n\n *  Fix: Recover gracefully from stale coalesced connections. We had a bug where\n    connection coalescing (introduced in OkHttp 3.7.0) and stale connection\n    recovery could interact to cause a `NoSuchElementException` crash in the\n    `RouteSelector`.\n\n\n## Version 3.8.0\n\n_2017-05-13_\n\n\n *  **OkHttp now uses `@Nullable` to annotate all possibly-null values.** We've\n    added a compile-time dependency on the JSR 305 annotations. This is a\n    [provided][maven_provided] dependency and does not need to be included in\n    your build configuration, `.jar` file, or `.apk`. We use\n    `@ParametersAreNonnullByDefault` and all parameters and return types are\n    never null unless explicitly annotated `@Nullable`.\n\n *  **Warning: this release is source-incompatible for Kotlin users.**\n    Nullability was previously ambiguous and lenient but now the compiler will\n    enforce strict null checks.\n\n *  New: The response message is now non-null. This is the \"Not Found\" in the\n    status line \"HTTP 404 Not Found\". If you are building responses\n    programmatically (with `new Response.Builder()`) you must now always supply\n    a message. An empty string `\"\"` is permitted. This value was never null on\n    responses returned by OkHttp itself, and it was an old mistake to permit\n    application code to omit a message.\n\n *  The challenge's scheme and realm are now non-null. If you are calling\n    `new Challenge(scheme, realm)` you must provide non-null values. These were\n    never null in challenges created by OkHttp, but could have been null in\n    application code that creates challenges.\n\n *  New: The `TlsVersion` of a `Handshake` is now non-null. If you are calling\n    `Handshake.get()` with a null TLS version, you must instead now provide a\n    non-null `TlsVersion`. Cache responses persisted prior to OkHttp 3.0 did not\n    store a TLS version; for these unknown values the handshake is defaulted to\n    `TlsVersion.SSL_3_0`.\n\n *  New: Upgrade to Okio 1.13.0.\n\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okio</groupId>\n       <artifactId>okio</artifactId>\n       <version>1.13.0</version>\n     </dependency>\n\n     com.squareup.okio:okio:1.13.0\n     ```\n\n *  Fix: gracefully recover when Android 7.0's sockets throw an unexpected\n    `NullPointerException`.\n\n## Version 3.7.0\n\n_2017-04-15_\n\n *  **OkHttp no longer recovers from TLS handshake failures by attempting a TLSv1 connection.**\n    The fallback was necessary for servers that implemented version negotiation incorrectly. Now\n    that 99.99% of servers do it right this fallback is obsolete.\n *  Fix: Do not honor cookies set on a public domain. Previously a malicious site could inject\n    cookies on top-level domains like `co.uk` because our cookie parser didn't honor the [public\n    suffix][public_suffix] list. Alongside this fix is a new API, `HttpUrl.topPrivateDomain()`,\n    which returns the privately domain name if the URL has one.\n *  Fix: Change `MediaType.charset()` to return null for unexpected charsets.\n *  Fix: Don't skip cache invalidation if the invalidating response has no body.\n *  Fix: Don't use a cryptographic random number generator for web sockets. Some Android devices\n    implement `SecureRandom` incorrectly!\n *  Fix: Correctly canonicalize IPv6 addresses in `HttpUrl`. This prevented OkHttp from trusting\n    HTTPS certificates issued to certain IPv6 addresses.\n *  Fix: Don't reuse connections after an unsuccessful `Expect: 100-continue`.\n *  Fix: Handle either `TLS_` or `SSL_` prefixes for cipher suite names. This is necessary for\n    IBM JVMs that use the `SSL_` prefix exclusively.\n *  Fix: Reject HTTP/2 data frames if the stream ID is 0.\n *  New: Upgrade to Okio 1.12.0.\n\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okio</groupId>\n       <artifactId>okio</artifactId>\n       <version>1.12.0</version>\n     </dependency>\n\n     com.squareup.okio:okio:1.12.0\n     ```\n\n *  New: Connection coalescing. OkHttp may reuse HTTP/2 connections across calls that share an IP\n    address and HTTPS certificate, even if their domain names are different.\n *  New: MockWebServer's `RecordedRequest` exposes the requested `HttpUrl` with `getRequestUrl()`.\n\n\n## Version 3.6.0\n\n_2017-01-29_\n\n *  Fix: Don't crash with a \"cache is closed\" error when there is an error initializing the cache.\n *  Fix: Calling `disconnect()` on a connecting `HttpUrlConnection` could cause it to retry in an\n    infinite loop! This regression was introduced in OkHttp 2.7.0.\n *  Fix: Drop cookies that contain ASCII NULL and other bad characters. Previously such cookies\n    would cause OkHttp to crash when they were included in a request.\n *  Fix: Release duplicated multiplexed connections. If we concurrently establish connections to an\n    HTTP/2 server, close all but the first connection.\n *  Fix: Fail the HTTP/2 connection if first frame isn't `SETTINGS`.\n *  Fix: Forbid spaces in header names.\n *  Fix: Don't offer to do gzip if the request is partial.\n *  Fix: MockWebServer is now usable with JUnit 5. That update [broke the rules][junit_5_rules].\n *  New: Support `Expect: 100-continue` as a request header. Callers can use this header to\n    pessimistically hold off on transmitting a request body until a server gives the go-ahead.\n *  New: Permit network interceptors to rewrite the host header for HTTP/2. This makes it possible\n    to do domain fronting.\n *  New: charset support for `Credentials.basic()`.\n\n\n## Version 3.5.0\n\n_2016-11-30_\n\n *  **Web Sockets are now a stable feature of OkHttp.** Since being introduced as a beta feature in\n    OkHttp 2.3 our web socket client has matured. Connect to a server's web socket with\n    `OkHttpClient.newWebSocket()`, send messages with `send()`, and receive messages with the\n    `WebSocketListener`.\n\n    The `okhttp-ws` submodule is no longer available and `okhttp-ws` artifacts from previous\n    releases of OkHttp are not compatible with OkHttp 3.5. When upgrading to the new package\n    please note that the `WebSocket` and `WebSocketCall` classes have been merged. Sending messages\n    is now asynchronous and they may be enqueued before the web socket is connected.\n\n *  **OkHttp no longer attempts a direct connection if the system's HTTP proxy fails.** This\n    behavior was surprising because OkHttp was disregarding the user's specified configuration. If\n    you need to customize proxy fallback behavior, implement your own `java.net.ProxySelector`.\n\n *  Fix: Support TLSv1.3 on devices that support it.\n\n *  Fix: Share pooled connections across equivalent `OkHttpClient` instances. Previous releases had\n    a bug where a shared connection pool did not guarantee shared connections in some cases.\n *  Fix: Prefer the server's response body on all conditional cache misses. Previously we would\n    return the cached response's body if it had a newer `Last-Modified` date.\n *  Fix: Update the stored timestamp on conditional cache hits.\n *  New: Optimized HTTP/2 request header encoding. More headers are HPACK-encoded and string\n    literals are now Huffman-encoded.\n *  New: Expose `Part` headers and body in `Multipart`.\n *  New: Make `ResponseBody.string()` and `ResponseBody.charStream()` BOM-aware. If your HTTP\n    response body begins with a [byte order mark][bom] it will be consumed and used to select a\n    charset for the remaining bytes. Most applications should not need a byte order mark.\n\n *  New: Upgrade to Okio 1.11.0.\n\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okio</groupId>\n       <artifactId>okio</artifactId>\n       <version>1.11.0</version>\n     </dependency>\n\n     com.squareup.okio:okio:1.11.0\n     ```\n\n *  Fix: Avoid sending empty HTTP/2 data frames when there is no request body.\n *  Fix: Add a leading `.` for better domain matching in `JavaNetCookieJar`.\n *  Fix: Gracefully recover from HTTP/2 connection shutdowns at start of request.\n *  Fix: Be lenient if a `MediaType`'s character set is `'single-quoted'`.\n *  Fix: Allow horizontal tab characters in header values.\n *  Fix: When parsing HTTP authentication headers permit challenge parameters in any order.\n\n\n## Version 3.4.2\n\n_2016-11-03_\n\n *  Fix: Recover gracefully when an HTTP/2 connection is shutdown. We had a\n    bug where shutdown HTTP/2 connections were considered usable. This caused\n    infinite loops when calls attempted to recover.\n\n\n## Version 3.4.1\n\n_2016-07-10_\n\n *  **Fix a major bug in encoding HTTP headers.** In 3.4.0 and 3.4.0-RC1 OkHttp\n    had an off-by-one bug in our HPACK encoder. This bug could have caused the\n    wrong headers to be emitted after a sequence of HTTP/2 requests! Everyone\n    who is using OkHttp 3.4.0 or 3.4.0-RC1 should upgrade for this bug fix.\n\n\n## Version 3.4.0\n\n_2016-07-08_\n\n *  New: Support dynamic table size changes to HPACK Encoder.\n *  Fix: Use `TreeMap` in `Headers.toMultimap()`. This makes string lookups on\n    the returned map case-insensitive.\n *  Fix: Don't share the OkHttpClient's `Dispatcher` in `HttpURLConnection`.\n\n\n## Version 3.4.0-RC1\n\n_2016-07-02_\n\n *  **We’ve rewritten HttpURLConnection and HttpsURLConnection.** Previously we\n    shared a single HTTP engine between two frontend APIs: `HttpURLConnection`\n    and `Call`. With this release we’ve rearranged things so that the\n    `HttpURLConnection` frontend now delegates to the `Call` APIs internally.\n    This has enabled substantial simplifications and optimizations in the OkHttp\n    core for both frontends.\n\n    For most HTTP requests the consequences of this change will be negligible.\n    If your application uses `HttpURLConnection.connect()`,\n    `setFixedLengthStreamingMode()`, or `setChunkedStreamingMode()`, OkHttp will\n    now use a async dispatcher thread to establish the HTTP connection.\n\n    We don’t expect this change to have any behavior or performance\n    consequences. Regardless, please exercise your `OkUrlFactory` and\n    `HttpURLConnection` code when applying this update.\n\n *  **Cipher suites may now have arbitrary names.** Previously `CipherSuite` was\n    a Java enum and it was impossible to define new cipher suites without first\n    upgrading OkHttp. With this change it is now a regular Java class with\n    enum-like constants. Application code that uses enum methods on cipher\n    suites (`ordinal()`, `name()`, etc.) will break with this change.\n\n *  Fix: `CertificatePinner` now matches canonicalized hostnames. Previously\n    this was case sensitive. This change should also make it easier to configure\n    certificate pinning for internationalized domain names.\n *  Fix: Don’t crash on non-ASCII `ETag` headers. Previously OkHttp would reject\n    these headers when validating a cached response.\n *  Fix: Don’t allow remote peer to arbitrarily size the HPACK decoder dynamic\n    table.\n *  Fix: Honor per-host configuration in Android’s network security config.\n    Previously disabling cleartext for any host would disable cleartext for all\n    hosts. Note that this setting is only available on Android 24+.\n *  New: HPACK compression is now dynamic. This should improve performance when\n    transmitting request headers over HTTP/2.\n *  New: `Dispatcher.setIdleCallback()` can be used to signal when there are no\n    calls in flight. This is useful for [testing with\n    Espresso][okhttp_idling_resource].\n *  New: Upgrade to Okio 1.9.0.\n\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okio</groupId>\n       <artifactId>okio</artifactId>\n       <version>1.9.0</version>\n     </dependency>\n     ```\n\n\n## Version 3.3.1\n\n_2016-05-28_\n\n *  Fix: The plaintext check in HttpLoggingInterceptor incorrectly classified\n    newline characters as control characters. This is fixed.\n *  Fix: Don't crash reading non-ASCII characters in HTTP/2 headers or in cached\n    HTTP headers.\n *  Fix: Retain the response body when an attempt to open a web socket returns a\n    non-101 response code.\n\n\n## Version 3.3.0\n\n_2016-05-24_\n\n *  New: `Response.sentRequestAtMillis()` and `receivedResponseAtMillis()`\n    methods track the system's local time when network calls are made. These\n    replace the `OkHttp-Sent-Millis` and `OkHttp-Received-Millis` headers that were\n    present in earlier versions of OkHttp.\n *  New: Accept user-provided trust managers in `OkHttpClient.Builder`. This\n    allows OkHttp to satisfy its TLS requirements directly. Otherwise OkHttp\n    will use reflection to extract the `TrustManager` from the\n    `SSLSocketFactory`.\n *  New: Support prerelease Java 9. This gets ALPN from the platform rather than\n    relying on the alpn-boot bootclasspath override.\n *  New: `HttpLoggingInterceptor` now logs connection failures.\n *  New: Upgrade to Okio 1.8.0.\n\n     ```xml\n     <dependency>\n       <groupId>com.squareup.okio</groupId>\n       <artifactId>okio</artifactId>\n       <version>1.8.0</version>\n     </dependency>\n     ```\n\n *  Fix: Gracefully recover from a failure to rebuild the cache journal.\n *  Fix: Don't corrupt cache entries when a cache entry is evicted while it is\n    being updated.\n *  Fix: Make logging more consistent throughout OkHttp.\n *  Fix: Log plaintext bodies only. This uses simple heuristics to differentiate\n    text from other data.\n *  Fix: Recover from `REFUSED_STREAM` errors in HTTP/2. This should improve\n    interoperability with Nginx 1.10.0, which [refuses][nginx_959] streams\n    created before HTTP/2 settings have been acknowledged.\n *  Fix: Improve recovery from failed routes.\n *  Fix: Accommodate tunneling proxies that close the connection after an auth\n    challenge.\n *  Fix: Use the proxy authenticator when authenticating HTTP proxies. This\n    regression was introduced in OkHttp 3.0.\n *  Fix: Fail fast if network interceptors transform the response body such that\n    closing it doesn't also close the underlying stream. We had a bug where\n    OkHttp would attempt to reuse a connection but couldn't because it was still\n    held by a prior request.\n *  Fix: Ensure network interceptors always have access to the underlying\n    connection.\n *  Fix: Use `X509TrustManagerExtensions` on Android 17+.\n *  Fix: Unblock waiting dispatchers on MockWebServer shutdown.\n\n\n## Version 3.2.0\n\n_2016-02-25_\n\n *  Fix: Change the certificate pinner to always build full chains. This\n    prevents a potential crash when using certificate pinning with the Google\n    Play Services security provider.\n *  Fix: Make IPv6 request lines consistent with Firefox and Chrome.\n *  Fix: Recover gracefully when trimming the response cache fails.\n *  New: Add multiple path segments using a single string in `HttpUrl.Builder`.\n *  New: Support SHA-256 pins in certificate pinner.\n\n\n## Version 3.1.2\n\n_2016-02-10_\n\n *  Fix: Don’t crash when finding the trust manager on Robolectric. We attempted\n    to detect the host platform and got confused because Robolectric looks like\n    Android but isn’t!\n *  Fix: Change `CertificatePinner` to skip sanitizing the certificate chain\n    when no certificates were pinned. This avoids an SSL failure in insecure\n    “trust everyone” configurations, such as when talking to a development\n    HTTPS server that has a self-signed certificate.\n\n\n## Version 3.1.1\n\n_2016-02-07_\n\n *  Fix: Don't crash when finding the trust manager if the Play Services (GMS)\n    security provider is installed.\n *  Fix: The previous release introduced a performance regression on Android,\n    caused by looking up CA certificates. This is now fixed.\n\n\n## Version 3.1.0\n\n_2016-02-06_\n\n *  New: WebSockets now defer some writes. This should improve performance for\n    some applications.\n *  New: Override `equals()` and `hashCode()` in our new cookie class. This\n    class now defines equality by value rather than by reference.\n *  New: Handle 408 responses by retrying the request. This allows servers to\n    direct clients to retry rather than failing permanently.\n *  New: Expose the framed protocol in `Connection`. Previously this would\n    return the application-layer protocol (HTTP/1.1 or HTTP/1.0); now it always\n    returns the wire-layer protocol (HTTP/2, SPDY/3.1, or HTTP/1.1).\n *  Fix: Permit the trusted CA root to be pinned by `CertificatePinner`.\n *  Fix: Silently ignore unknown HTTP/2 settings. Previously this would cause\n    the entire connection to fail.\n *  Fix: Don’t crash on unexpected charsets in the logging interceptor.\n *  Fix: `OkHttpClient` is now non-final for the benefit of mocking frameworks.\n    Mocking sophisticated classes like `OkHttpClient` is fragile and you\n    shouldn’t do it. But if that’s how you want to live your life we won’t stand\n    in your way!\n\n\n## Version 3.0.1\n\n_2016-01-14_\n\n *  Rollback OSGi support. This was causing library jars to include more classes\n    than expected, which interfered with Gradle builds.\n\n\n## Version 3.0.0\n\n_2016-01-13_\n\nThis release commits to a stable 3.0 API. Read the 3.0.0-RC1 changes for advice\non upgrading from 2.x to 3.x.\n\n *  **The `Callback` interface now takes a `Call`**. This makes it easier to\n    check if the call was canceled from within the callback. When migrating\n    async calls to this new API, `Call` is now the first parameter for both\n    `onResponse()` and `onFailure()`.\n *  Fix: handle multiple cookies in `JavaNetCookieJar` on Android.\n *  Fix: improve the default HTTP message in MockWebServer responses.\n *  Fix: don't leak file handles when a conditional GET throws.\n *  Fix: Use charset specified by the request body content type in OkHttp's\n    logging interceptor.\n *  Fix: Don't eagerly release pools on cache hits.\n *  New: Make OkHttp OSGi ready.\n *  New: Add already-implemented interfaces Closeable and Flushable to the cache.\n\n\n## Version 3.0.0-RC1\n\n_2016-01-02_\n\nOkHttp 3 is a major release focused on API simplicity and consistency. The API\nchanges are numerous but most are cosmetic. Applications should be able to\nupgrade from the 2.x API to the 3.x API mechanically and without risk.\n\nBecause the release includes breaking API changes, we're changing the project's\npackage name from `com.squareup.okhttp` to `okhttp3`. This should make it\npossible for large applications to migrate incrementally. The Maven group ID\nis now `com.squareup.okhttp3`. For an explanation of this strategy, see Jake\nWharton's post, [Java Interoperability Policy for Major Version\nUpdates][major_versions].\n\nThis release obsoletes OkHttp 2.x, and all code that uses OkHttp's\n`com.squareup.okhttp` package should upgrade to the `okhttp3` package. Libraries\nthat depend on OkHttp should upgrade quickly to prevent applications from being\nstuck on the old version.\n\n *  **There is no longer a global singleton connection pool.** In OkHttp 2.x,\n    all `OkHttpClient` instances shared a common connection pool by default.\n    In OkHttp 3.x, each new `OkHttpClient` gets its own private connection pool.\n    Applications should avoid creating many connection pools as doing so\n    prevents connection reuse. Each connection pool holds its own set of\n    connections alive so applications that have many pools also risk exhausting\n    memory!\n\n    The best practice in OkHttp 3 is to create a single OkHttpClient instance\n    and share it throughout the application. Requests that needs a customized\n    client should call `OkHttpClient.newBuilder()` on that shared instance.\n    This allows customization without the drawbacks of separate connection\n    pools.\n\n *  **OkHttpClient is now stateless.** In the 2.x API `OkHttpClient` had getters\n    and setters. Internally each request was forced to make its own complete\n    snapshot of the `OkHttpClient` instance to defend against racy configuration\n    changes. In 3.x, `OkHttpClient` is now stateless and has a builder. Note\n    that this class is not strictly immutable as it has stateful members like\n    the connection pool and cache.\n\n *  **Get and Set prefixes are now avoided.** With ubiquitous builders\n    throughout OkHttp these accessor prefixes aren't necessary. Previously\n    OkHttp used _get_ and _set_ prefixes sporadically which make the API\n    inconsistent and awkward to explore.\n\n *  **OkHttpClient now implements the new `Call.Factory` interface.** This\n    interface will make your code easier to test. When you test code that makes\n    HTTP requests, you can use this interface to replace the real `OkHttpClient`\n    with your own mocks or fakes.\n\n    The interface will also let you use OkHttp's API with another HTTP client's\n    implementation. This is useful in sandboxed environments like Google App\n    Engine.\n\n *  **OkHttp now does cookies.** We've replaced `java.net.CookieHandler` with\n    a new interface, `CookieJar` and added our own `Cookie` model class. This\n    new cookie follows the latest RFC and supports the same cookie attributes\n    as modern web browsers.\n\n *  **Form and Multipart bodies are now modeled.** We've replaced the opaque\n    `FormEncodingBuilder` with the more powerful `FormBody` and\n    `FormBody.Builder` combo. Similarly we've upgraded `MultipartBuilder` into\n    `MultipartBody`, `MultipartBody.Part`, and `MultipartBody.Builder`.\n\n *  **The Apache HTTP client and HttpURLConnection APIs are deprecated.** They\n    continue to work as they always have, but we're moving everything to the new\n    OkHttp 3 API. The `okhttp-apache` and `okhttp-urlconnection` modules should\n    be only be used to accelerate a transition to OkHttp's request/response API.\n    These deprecated modules will be dropped in an upcoming OkHttp 3.x release.\n\n *  **Canceling batches of calls is now the application's responsibility.**\n    The API to cancel calls by tag has been removed and replaced with a more\n    general mechanism. The dispatcher now exposes all in-flight calls via its\n    `runningCalls()` and `queuedCalls()` methods. You can write code that\n    selects calls by tag, host, or whatever, and invokes `Call.cancel()` on the\n    ones that are no longer necessary.\n\n *  **OkHttp no longer uses the global `java.net.Authenticator` by default.**\n    We've changed our `Authenticator` interface to authenticate web and proxy\n    authentication failures through a single method. An adapter for the old\n    authenticator is available in the `okhttp-urlconnection` module.\n\n *  Fix: Don't throw `IOException` on `ResponseBody.contentLength()` or `close()`.\n *  Fix: Never throw converting an `HttpUrl` to a `java.net.URI`. This changes\n    the `uri()` method to handle malformed percent-escapes and characters\n    forbidden by `URI`.\n *  Fix: When a connect times out, attempt an alternate route. Previously route\n    selection was less efficient when differentiating failures.\n *  New: `Response.peekBody()` lets you access the response body without\n    consuming it. This may be handy for interceptors!\n *  New: `HttpUrl.newBuilder()` resolves a link to a builder.\n *  New: Add the TLS version to the `Handshake`.\n *  New: Drop `Request.uri()` and `Request#urlString()`. Just use\n    `Request.url().uri()` and `Request.url().toString()`.\n *  New: Add URL to HTTP response logging.\n *  New: Make `HttpUrl` the blessed URL method of `Request`.\n\n\n## Version 2.x\n\n[Change log](changelog_2x.md)\n\n\n [bom]: https://en.wikipedia.org/wiki/Byte_order_mark\n [conscrypt]: https://github.com/google/conscrypt/\n [conscrypt_dependency]: https://github.com/google/conscrypt/#download\n [grpc_http2]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md\n [https_server_sample]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/HttpsServer.java\n [jetty_8_252]: https://webtide.com/jetty-alpn-java-8u252/\n [junit_5_rules]: https://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4-rulesupport\n [major_versions]: https://jakewharton.com/java-interoperability-policy-for-major-version-updates/\n [maven_provided]: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html\n [nginx_959]: https://trac.nginx.org/nginx/ticket/959\n [obsolete_apache_client]: https://gist.github.com/swankjesse/09721f72039e3a46cf50f94323deb82d\n [obsolete_url_factory]: https://gist.github.com/swankjesse/dd91c0a8854e1559b00f5fc9c7bfae70\n [okhttp_idling_resource]: https://github.com/JakeWharton/okhttp-idling-resource\n [public_suffix]: https://publicsuffix.org/\n [remove_cbc_ecdsa]: https://developers.google.com/web/updates/2016/12/chrome-56-deprecations#remove_cbc-mode_ecdsa_ciphers_in_tls\n [require_android_5]: https://code.cash.app/okhttp-3-13-requires-android-5\n [tls_configuration_history]: https://square.github.io/okhttp/tls_configuration_history/\n [upgrading_to_okhttp_4]: https://square.github.io/okhttp/upgrading_to_okhttp_4/\n"
  },
  {
    "path": "docs/changelogs/changelog_4x.md",
    "content": "OkHttp 4.x Change Log\n=====================\n\n## Version 4.12.0\n\n_2023-10-16_\n\n *  Fix: Don't hang taking headers for HTTP 103 responses.\n\n *  Fix: Recover gracefully when a cache entry's certificate is corrupted.\n\n *  Fix: Fail permanently when there's a failure loading the bundled public suffix database.\n    This is the dataset that powers `HttpUrl.topPrivateDomain()`.\n\n *  Fix: Immediately update the connection's flow control window instead of waiting for the\n    receiving stream to process it.\n\n    This change may increase OkHttp's memory use for applications that make many concurrent HTTP\n    calls and that can receive data faster than they can process it. Previously, OkHttp limited\n    HTTP/2 to 16 MiB of unacknowledged data per connection. With this fix there is a limit of 16 MiB\n    of unacknowledged data per stream and no per-connection limit.\n\n *  Fix: Don't operate on a connection after it's been returned to the pool. This race occurred\n    on failed web socket connection attempts.\n\n *  Upgrade: [Okio 3.6.0][okio_3_6_0].\n\n *  Upgrade: [Kotlin 1.8.21][kotlin_1_8_21].\n\n\n## Version 4.11.0\n\n_2023-04-22_\n\n *  Fix: Don't fail the call when the response code is ‘HTTP 102 Processing’ or\n    ‘HTTP 103 Early Hints’.\n *  Fix: Read the response even if writing the request fails. This means you'll get a proper HTTP\n    response even if the server rejects your request body.\n *  Fix: Use literal IP addresses directly rather than passing them to `DnsOverHttps`.\n *  Fix: Embed Proguard rules to prevent warnings from tools like DexGuard and R8. These warnings\n    were triggered by OkHttp’s feature detection for TLS packages like `org.conscrypt`,\n    `org.bouncycastle`, and `org.openjsse`.\n *  Upgrade: Explicitly depend on `kotlin-stdlib-jdk8`. This fixes a problem with dependency\n    locking. That's a potential security vulnerability, tracked as [CVE-2022-24329].\n *  Upgrade: [publicsuffix.org data][public_suffix]. This powers `HttpUrl.topPrivateDomain()`.\n    It's also how OkHttp knows which domains can share cookies with one another.\n *  Upgrade: [Okio 3.2.0][okio_3_2_0].\n\n\n## Version 4.10.0\n\n_2022-06-12_\n\n *  Upgrade: [Kotlin 1.6.20][kotlin_1_6_20].\n *  Upgrade: [Okio 3.0.0][okio_3_0_0].\n *  Fix: Recover gracefully when Android's `NativeCrypto` crashes with `\"ssl == null\"`. This occurs\n    when OkHttp retrieves ALPN state on a closed connection.\n\n\n## Version 4.9.3\n\n_2021-11-21_\n\n *  Fix: Don't fail HTTP/2 responses if they complete before a `RST_STREAM` is sent.\n\n\n## Version 4.9.2\n\n_2021-09-30_\n\n *  Fix: Don't include potentially-sensitive header values in `Headers.toString()` or exceptions.\n    This applies to `Authorization`, `Cookie`, `Proxy-Authorization`, and `Set-Cookie` headers.\n *  Fix: Don't crash with an `InaccessibleObjectException` when running on JDK17+ with strong\n    encapsulation enabled.\n *  Fix: Strictly verify hostnames used with OkHttp's `HostnameVerifier`. Programs that make direct\n    manual calls to `HostnameVerifier` could be defeated if the hostnames they pass in are not\n    strictly ASCII. This issue is tracked as [CVE-2021-0341].\n\n\n## Version 4.9.1\n\n_2021-01-30_\n\n *  Fix: Work around a crash in Android 10 and 11 that may be triggered when two threads\n    concurrently close an SSL socket. This would have appeared in crash logs as\n    `NullPointerException: bio == null`.\n\n\n## Version 4.9.0\n\n_2020-09-11_\n\n**With this release, `okhttp-tls` no longer depends on Bouncy Castle and doesn't install the\nBouncy Castle security provider.** If you still need it, you can do it yourself:\n\n```\nSecurity.addProvider(BouncyCastleProvider())\n```\n\nYou will also need to configure this dependency:\n\n```\ndependencies {\n  implementation \"org.bouncycastle:bcprov-jdk15on:1.65\"\n}\n```\n\n *  Upgrade: [Kotlin 1.4.10][kotlin_1_4_10]. We now use Kotlin 1.4.x [functional\n    interfaces][fun_interface] for `Authenticator`, `Interceptor`, and others.\n *  Upgrade: Build with Conscrypt 2.5.1.\n\n\n## Version 4.8.1\n\n_2020-08-06_\n\n *  Fix: Don't crash in `HeldCertificate.Builder` when creating certificates on older versions of\n    Android, including Android 6. We were using a feature of `SimpleDateFormat` that wasn't\n    available in those versions!\n\n\n## Version 4.8.0\n\n_2020-07-11_\n\n *  New: Change `HeldCertificate.Builder` to use its own ASN.1 certificate encoder. This is part\n    of our effort to remove the okhttp-tls module's dependency on Bouncy Castle. We think Bouncy\n    Castle is great! But it's a large dependency (6.5 MiB) and its security provider feature\n    impacts VM-wide behavior.\n\n *  New: Reduce contention for applications that make a very high number of concurrent requests.\n    Previously OkHttp used its connection pool as a lock when making changes to connections and\n    calls. With this change each connection is locked independently.\n\n *  Upgrade: [Okio 2.7.0][okio_2_7_0].\n\n    ```kotlin\n    implementation(\"com.squareup.okio:okio:2.7.0\")\n    ```\n\n *  Fix: Avoid log messages like \"Didn't find class org.conscrypt.ConscryptHostnameVerifier\" when\n    detecting the TLS capabilities of the host platform.\n\n *  Fix: Don't crash in `HttpUrl.topPrivateDomain()` when the hostname is malformed.\n\n *  Fix: Don't attempt Brotli decompression if the response body is empty.\n\n\n## Version 4.7.2\n\n_2020-05-20_\n\n *  Fix: Don't crash inspecting whether the host platform is JVM or Android. With 4.7.0 and 4.7.1 we\n    had a crash `IllegalArgumentException: Not a Conscrypt trust manager` because we depended on\n    initialization order of companion objects.\n\n\n## Version 4.7.1\n\n_2020-05-18_\n\n *  Fix: Pass the right arguments in the trust manager created for `addInsecureHost()`. Without the\n    fix insecure hosts crash with an `IllegalArgumentException` on Android.\n\n\n## Version 4.7.0\n\n_2020-05-17_\n\n *  New: `HandshakeCertificates.Builder.addInsecureHost()` makes it easy to turn off security in\n    private development environments that only carry test data. Prefer this over creating an\n    all-trusting `TrustManager` because only hosts on the allowlist are insecure. From\n    [our DevServer sample][dev_server]:\n\n    ```kotlin\n    val clientCertificates = HandshakeCertificates.Builder()\n        .addPlatformTrustedCertificates()\n        .addInsecureHost(\"localhost\")\n        .build()\n\n    val client = OkHttpClient.Builder()\n        .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n        .build()\n    ```\n\n *  New: Add `cacheHit`, `cacheMiss`, and `cacheConditionalHit()` events to `EventListener`. Use\n    these in logs, metrics, and even test cases to confirm your cache headers are configured as\n    expected.\n\n *  New: Constant string `okhttp3.VERSION`. This is a string like \"4.5.0-RC1\", \"4.5.0\", or\n    \"4.6.0-SNAPSHOT\" indicating the version of OkHttp in the current runtime. Use this to include\n    the OkHttp version in custom `User-Agent` headers.\n\n *  Fix: Don't crash when running as a plugin in Android Studio Canary 4.1. To enable\n    platform-specific TLS features OkHttp must detect whether it's running in a JVM or in Android.\n    The upcoming Android Studio runs in a JVM but has classes from Android and that confused OkHttp!\n\n *  Fix: Include the header `Accept: text/event-stream` for SSE calls. This header is not added if\n    the request already contains an `Accept` header.\n\n *  Fix: Don't crash with a `NullPointerException` if a server sends a close while we're sending a\n    ping. OkHttp had a race condition bug.\n\n\n## Version 4.6.0\n\n_2020-04-28_\n\n *  Fix: Follow HTTP 307 and 308 redirects on methods other than GET and POST. We're reluctant to\n    change OkHttp's behavior in handling common HTTP status codes, but this fix is overdue! The new\n    behavior is now consistent with [RFC 7231][rfc_7231_647], which is newer than OkHttp itself.\n    If you want this update with the old behavior use [this interceptor][legacy_interceptor].\n\n *  Fix: Don't crash decompressing web sockets messages. We had a bug where we assumed deflated\n    bytes in would always yield deflated bytes out and this isn't always the case!\n\n *  Fix: Reliably update and invalidate the disk cache on windows. As originally designed our\n    internal `DiskLruCache` assumes an inode-like file system, where it's fine to delete files that\n    are currently being read or written. On Windows the file system forbids this so we must be more\n    careful when deleting and renaming files.\n\n *  Fix: Don't crash on Java 8u252 which introduces an API previously found only on Java 9 and\n    above. See [Jetty's overview][jetty_8_252] of the API change and its consequences.\n\n *  New: `MultipartReader` is a streaming decoder for [MIME multipart (RFC 2045)][rfc_2045]\n    messages. It complements `MultipartBody` which is our streaming encoder.\n\n    ```kotlin\n    val response: Response = call.execute()\n    val multipartReader = MultipartReader(response.body!!)\n\n    multipartReader.use {\n      while (true) {\n        val part = multipartReader.nextPart() ?: break\n        process(part.headers, part.body)\n      }\n    }\n    ```\n\n *  New: `MediaType.parameter()` gets a parameter like `boundary` from a media type like\n    `multipart/mixed; boundary=\"abc\"`.\n\n *  New: `Authenticator.JAVA_NET_AUTHENTICATOR` forwards authentication requests to\n    `java.net.Authenticator`. This obsoletes `JavaNetAuthenticator` in the `okhttp-urlconnection`\n    module.\n\n *  New: `CertificatePinner` now offers an API for inspecting the configured pins.\n\n *  Upgrade: [Okio 2.6.0][okio_2_6_0].\n\n    ```kotlin\n    implementation(\"com.squareup.okio:okio:2.6.0\")\n    ```\n\n *  Upgrade: [publicsuffix.org data][public_suffix]. This powers `HttpUrl.topPrivateDomain()`.\n    It's also how OkHttp knows which domains can share cookies with one another.\n\n *  Upgrade: [Bouncy Castle 1.65][bouncy_castle_releases]. This dependency is required by the\n    `okhttp-tls` module.\n\n *  Upgrade: [Kotlin 1.3.71][kotlin_1_3_71].\n\n\n## Version 4.5.0\n\n_2020-04-06_\n\n**This release fixes a severe bug where OkHttp incorrectly detected and recovered from unhealthy\nconnections.** Stale or canceled connections were incorrectly attempted when they shouldn't have\nbeen, leading to rare cases of infinite retries. Please upgrade to this release!\n\n *  Fix: don't return stale DNS entries in `DnsOverHttps`. We were caching DNS results indefinitely\n    rather than the duration specified in the response's cache-control header.\n *  Fix: Verify certificate IP addresses in canonical form. When a server presents a TLS certificate\n    containing an IP address we must match that address against the URL's IP address, even when the\n    two addresses are encoded differently, such as `192.168.1.1` and `0::0:0:FFFF:C0A8:101`. Note\n    that OkHttp incorrectly rejected valid certificates resulting in a failure to connect; at no\n    point were invalid certificates accepted.\n *  New: `OkHttpClient.Builder.minWebSocketMessageToCompress()` configures a threshold for\n    compressing outbound web socket messages. Configure this with 0L to always compress outbound\n    messages and `Long.MAX_VALUE` to never compress outbound messages. The default is 1024L which\n    compresses messages of size 1 KiB and larger. (Inbound messages are compressed or not based on\n    the web socket server's configuration.)\n *  New: Defer constructing `Inflater` and `Deflater` instances until they are needed. This saves\n    memory if web socket compression is negotiated but not used.\n\n\n## Version 4.5.0-RC1\n\n_2020-03-17_\n\n**This release candidate turns on web socket compression.**\n\nThe [spec][rfc_7692] includes a sophisticated mechanism for client and server to negotiate\ncompression features. We strive to offer great performance in our default configuration and so we're\nmaking compression the default for everyone starting with this release candidate.\n\nPlease be considerate of your servers and their operators as you roll out this release. Compression\nsaves bandwidth but it costs CPU and memory! If you run into a problem you may need to adjust or\ndisable the `permessage-deflate` compression settings on your server.\n\nNote that OkHttp won't use compression when sending messages smaller than 1 KiB.\n\n *  Fix: Don't crash when the URL hostname contains an underscore on Android.\n *  Fix: Change HTTP/2 to use a daemon thread for its socket reader. If you've ever seen a command\n    line application hang after all of the work is done, it may be due to a non-daemon thread like\n    this one.\n *  New: Include suppressed exceptions when all routes to a target service fail.\n\n\n## Version 4.4.1\n\n_2020-03-08_\n\n *  Fix: Don't reuse a connection on redirect if certs match but DNS does not. For better\n    locality and performance OkHttp attempts to use the same pooled connection across redirects and\n    follow-ups. It independently shares connections when the IP addresses and certificates match,\n    even if the host names do not. In 4.4.0 we introduced a regression where we shared a connection\n    when certificates matched but the DNS addresses did not. This would only occur when following a\n    redirect from one hostname to another, and where both hosts had common certificates.\n\n *  Fix: Don't fail on a redirect when a client has configured a 'trust everything' trust manager.\n    Typically this would cause certain redirects to fail in debug and development configurations.\n\n\n## Version 4.4.0\n\n_2020-02-17_\n\n *  New: Support `canceled()` as an event that can be observed by `EventListener`. This should be\n    useful for splitting out canceled calls in metrics.\n\n *  New: Publish a [bill of materials (BOM)][bom] for OkHttp. Depend on this from Gradle or Maven to\n    keep all of your OkHttp artifacts on the same version, even if they're declared via transitive\n    dependencies. You can even omit versions when declaring other OkHttp dependencies.\n\n    ```kotlin\n    dependencies {\n       api(platform(\"com.squareup.okhttp3:okhttp-bom:4.4.0\"))\n       api(\"com.squareup.okhttp3:okhttp\")              // No version!\n       api(\"com.squareup.okhttp3:logging-interceptor\") // No version!\n    }\n    ```\n\n *  New: Upgrade to Okio 2.4.3.\n\n    ```kotlin\n    implementation(\"com.squareup.okio:okio:2.4.3\")\n    ```\n\n *  Fix: Limit retry attempts for HTTP/2 `REFUSED_STREAM` and `CANCEL` failures.\n *  Fix: Retry automatically when incorrectly sharing a connection among multiple hostnames. OkHttp\n    shares connections when hosts share both IP addresses and certificates, such as `squareup.com`\n    and `www.squareup.com`. If a server refuses such sharing it will return HTTP 421 and OkHttp will\n    automatically retry on an unshared connection.\n *  Fix: Don't crash if a TLS tunnel's response body is truncated.\n *  Fix: Don't track unusable routes beyond their usefulness. We had a bug where we could track\n    certain bad routes indefinitely; now we only track the ones that could be necessary.\n *  Fix: Defer proxy selection until a proxy is required. This saves calls to `ProxySelector` on\n    calls that use a pooled connection.\n\n\n## Version 4.3.1\n\n_2020-01-07_\n\n *  Fix: Don't crash with a `NullPointerException` when a web socket is closed before it connects.\n    This regression was introduced in OkHttp 4.3.0.\n *  Fix: Don't crash with an `IllegalArgumentException` when using custom trust managers on\n    Android 10. Android uses reflection to look up a magic `checkServerTrusted()` method and we\n    didn't have it.\n *  Fix: Explicitly specify the remote server name when making HTTPS connections on Android 5. In\n    4.3.0 we introduced a regression where server name indication (SNI) was broken on Android 5.\n\n\n## Version 4.3.0\n\n_2019-12-31_\n\n *  Fix: Degrade HTTP/2 connections after a timeout. When an HTTP/2 stream times out it may impact\n    the stream only or the entire connection. With this fix OkHttp will now send HTTP/2 pings after\n    a stream timeout to determine whether the connection should remain eligible for pooling.\n\n *  Fix: Don't call `EventListener.responseHeadersStart()` or `responseBodyStart()` until bytes have\n    been received. Previously these events were incorrectly sent too early, when OkHttp was ready to\n    read the response headers or body, which mislead tracing tools. Note that the `responseFailed()`\n    event always used to follow one of these events; now it may be sent without them.\n\n *  New: Upgrade to Kotlin 1.3.61.\n\n *  New: Match any number of subdomains with two asterisks in `CertificatePinner`. For example,\n    `**.squareup.com` matches `us-west.www.squareup.com`, `www.squareup.com` and `squareup.com`.\n\n *  New: Share threads more aggressively between OkHttp's HTTP/2 connections, connection pool,\n    web sockets, and cache. OkHttp has a new internal task runner abstraction for managed task\n    scheduling. In your debugger you will see new thread names and more use of daemon threads.\n\n *  Fix: Don't drop callbacks on unexpected exceptions. When an interceptor throws an unchecked\n    exception the callback is now notified that the call was canceled. The exception is still sent\n    to the uncaught exception handler for reporting and recovery.\n\n *  Fix: Un-deprecate `MockResponse.setHeaders()` and other setters. These were deprecated in OkHttp\n    4.0 but that broke method chaining for Java callers.\n\n *  Fix: Don't crash on HTTP/2 HEAD requests when the `Content-Length` header is present but is not\n    consistent with the length of the response body.\n\n *  Fix: Don't crash when converting a `HttpUrl` instance with an unresolvable hostname to a URI.\n    The new behavior strips invalid characters like `\"` and `{` from the hostname before converting.\n\n *  Fix: Undo a performance regression introduced in OkHttp 4.0 caused by differences in behavior\n    between Kotlin's `assert()` and Java's `assert()`. (Kotlin always evaluates the argument; Java\n    only does when assertions are enabled.)\n\n *  Fix: Honor `RequestBody.isOneShot()` in `HttpLoggingInterceptor`.\n\n\n## Version 4.2.2\n\n_2019-10-06_\n\n *  Fix: When closing a canceled HTTP/2 stream, don't send the `END_STREAM` flag. This could cause\n    the server to incorrectly interpret the stream as having completed normally. This is most useful\n    when a request body needs to cancel its own call.\n\n\n## Version 4.2.1\n\n_2019-10-02_\n\n *  Fix: In 4.1.0 we introduced a performance regression that prevented connections from being\n    pooled in certain situations. We have good test coverage for connection pooling but we missed\n    this because it only occurs if you have proxy configured and you share a connection pool among\n    multiple `OkHttpClient` instances.\n\n    This particularly-subtle bug was caused by us assigning each `OkHttpClient` instance its own\n    `NullProxySelector` when an explicit proxy is configured. But we don't share connections when\n    the proxy selectors are different. Ugh!\n\n\n## Version 4.2.0\n\n_2019-09-10_\n\n *  New: API to decode a certificate and private key to create a `HeldCertificate`. This accepts a\n    string containing both a certificate and PKCS #8-encoded private key.\n\n    ```kotlin\n    val heldCertificate = HeldCertificate.decode(\"\"\"\n        |-----BEGIN CERTIFICATE-----\n        |MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl\n        |cmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAwMTAx\n        |MDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2FzaC5h\n        |cHAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASda8ChkQXxGELnrV/oBnIAx3dD\n        |ocUOJfdz4pOJTP6dVQB9U3UBiW5uSX/MoOD0LL5zG3bVyL3Y6pDwKuYvfLNhoyAw\n        |HjAcBgNVHREBAf8EEjAQhwQBAQEBgghjYXNoLmFwcDAKBggqhkjOPQQDAgNIADBF\n        |AiAyHHg1N6YDDQiY920+cnI5XSZwEGhAtb9PYWO8bLmkcQIhAI2CfEZf3V/obmdT\n        |yyaoEufLKVXhrTQhRfodTeigi4RX\n        |-----END CERTIFICATE-----\n        |-----BEGIN PRIVATE KEY-----\n        |MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCA7ODT0xhGSNn4ESj6J\n        |lu/GJQZoU9lDrCPeUcQ28tzOWw==\n        |-----END PRIVATE KEY-----\n        \"\"\".trimMargin())\n    val handshakeCertificates = HandshakeCertificates.Builder()\n        .heldCertificate(heldCertificate)\n        .build()\n    val server = MockWebServer()\n    server.useHttps(handshakeCertificates.sslSocketFactory(), false)\n    ```\n\n    Get these strings with `HeldCertificate.certificatePem()` and `privateKeyPkcs8Pem()`.\n\n *  Fix: Handshake now returns peer certificates in canonical order: each certificate is signed by\n    the certificate that follows and the last certificate is signed by a trusted root.\n\n *  Fix: Don't lose HTTP/2 flow control bytes when incoming data races with a stream close. If this\n    happened enough then eventually the connection would stall.\n\n *  Fix: Acknowledge and apply inbound HTTP/2 settings atomically. Previously we had a race where we\n    could use new flow control capacity before acknowledging it, causing strict HTTP/2 servers to\n    fail the call.\n\n\n## Version 4.1.1\n\n_2019-09-05_\n\n *  Fix: Don't drop repeated headers when validating cached responses. In our Kotlin upgrade we\n    introduced a regression where we iterated the number of unique header names rather than then\n    number of unique headers. If you're using OkHttp's response cache this may impact you.\n\n\n## Version 4.1.0\n\n_2019-08-12_\n\n [brotli]: https://github.com/google/brotli\n\n *  **OkHttp's new okhttp-brotli module implements Brotli compression.** Install the interceptor to\n    enable [Brotli compression][brotli], which compresses 5-20% smaller than gzip.\n\n    ```\n    val client = OkHttpClient.Builder()\n        .addInterceptor(BrotliInterceptor)\n        .build()\n    ```\n\n    This artifact has a dependency on Google's Brotli decoder (95 KiB).\n\n *  New: `EventListener.proxySelectStart()`, `proxySelectEnd()` events give visibility into the\n    proxy selection process.\n *  New: `Response.byteString()` reads the entire response into memory as a byte string.\n *  New: `OkHttpClient.x509TrustManager` accessor.\n *  New: Permit [new WebSocket response codes][iana_websocket]: 1012 (Service Restart), 1013 (Try\n    Again Later), and 1014 (invalid response from the upstream).\n *  New: Build with Kotlin 1.3.41, BouncyCastle 1.62, and Conscrypt 2.2.1.\n *  Fix: Recover gracefully when a coalesced connection immediately goes unhealthy.\n *  Fix: Defer the `SecurityException` when looking up the default proxy selector.\n *  Fix: Don't use brackets formatting IPv6 host names in MockWebServer.\n *  Fix: Don't permit cache iterators to remove entries that are being written.\n\n\n## Version 4.0.1\n\n_2019-07-10_\n\n *  Fix: Tolerate null-hostile lists in public API. Lists created with `List.of(...)` don't like it\n    when you call `contains(null)` on them!\n *  Fix: Retain binary-compatibility in `okhttp3.internal.HttpHeaders.hasBody()`. Some unscrupulous\n    coders call this and we don't want their users to suffer.\n\n\n## Version 4.0.0\n\n_2019-06-26_\n\n**This release upgrades OkHttp to Kotlin.** We tried our best to make fast and safe to upgrade\nfrom OkHttp 3.x. We wrote an [upgrade guide][upgrading_to_okhttp_4] to help with the migration and a\n[blog post][okhttp4_blog_post] to explain it.\n\n *  Fix: Target Java 8 bytecode for Java and Kotlin.\n\n\n## Version 4.0.0-RC3\n\n_2019-06-24_\n\n *  Fix: Retain binary-compatibility in `okhttp3.internal.HttpMethod`. Naughty third party SDKs\n    import this and we want to ease upgrades for their users.\n\n\n## Version 4.0.0-RC2\n\n_2019-06-21_\n\n *  New: Require Kotlin 1.3.40.\n *  New: Change the Kotlin API from `File.toRequestBody()` to `File.asRequestBody()` and\n    `BufferedSource.toResponseBody()` to `BufferedSource.asResponseBody()`. If the returned value\n    is a view of what created it, we use _as_.\n *  Fix: Permit response codes of zero for compatibility with OkHttp 3.x.\n *  Fix: Change the return type of `MockWebServer.takeRequest()` to be nullable.\n *  Fix: Make `Call.clone()` public to Kotlin callers.\n\n\n## Version 4.0.0-RC1\n\n_2019-06-03_\n\n *  First stable preview of OkHttp 4.\n\n\n## Version 3.x\n\n[Change log](https://square.github.io/okhttp/changelog_3x/)\n\n\n [bom]: https://docs.gradle.org/6.2/userguide/platforms.html#sub:bom_import\n [bouncy_castle_releases]: https://www.bouncycastle.org/releasenotes.html\n [CVE-2021-0341]: https://nvd.nist.gov/vuln/detail/CVE-2021-0341\n [CVE-2022-24329]: https://nvd.nist.gov/vuln/detail/CVE-2022-24329\n [dev_server]: https://github.com/square/okhttp/blob/482f88300f78c3419b04379fc26c3683c10d6a9d/samples/guide/src/main/java/okhttp3/recipes/kt/DevServer.kt\n [fun_interface]: https://kotlinlang.org/docs/reference/fun-interfaces.html\n [iana_websocket]: https://www.iana.org/assignments/websocket/websocket.txt\n [jetty_8_252]: https://webtide.com/jetty-alpn-java-8u252/\n [kotlin_1_3_71]: https://github.com/JetBrains/kotlin/releases/tag/v1.3.71\n [kotlin_1_4_10]: https://github.com/JetBrains/kotlin/releases/tag/v1.4.10\n [kotlin_1_6_20]: https://github.com/JetBrains/kotlin/releases/tag/v1.6.20\n [kotlin_1_8_21]: https://github.com/JetBrains/kotlin/releases/tag/v1.8.21\n [legacy_interceptor]: https://gist.github.com/swankjesse/80135f4e03629527e723ab3bcf64be0b\n [okhttp4_blog_post]: https://cashapp.github.io/2019-06-26/okhttp-4-goes-kotlin\n [okio.FileSystem]: https://square.github.io/okio/file_system/\n [okio_2_6_0]: https://square.github.io/okio/changelog/#version-260\n [okio_2_7_0]: https://square.github.io/okio/changelog/#version-270\n [okio_3_0_0]: https://square.github.io/okio/changelog/#version-300\n [okio_3_2_0]: https://square.github.io/okio/changelog/#version-320\n [okio_3_6_0]: https://square.github.io/okio/changelog/#version-360\n [public_suffix]: https://publicsuffix.org/\n [rfc_2045]: https://tools.ietf.org/html/rfc2045\n [rfc_7231_647]: https://tools.ietf.org/html/rfc7231#section-6.4.7\n [rfc_7692]: https://tools.ietf.org/html/rfc7692\n [semver]: https://semver.org/\n [upgrading_to_okhttp_4]: https://square.github.io/okhttp/upgrading_to_okhttp_4/\n"
  },
  {
    "path": "docs/changelogs/upgrading_to_okhttp_4.md",
    "content": "Upgrading to OkHttp 4\n=====================\n\nOkHttp 4.x upgrades our implementation language from Java to Kotlin and keeps everything else the\nsame. We’ve chosen Kotlin because it gives us powerful new capabilities while integrating closely\nwith Java.\n\nWe spent a lot of time and energy on retaining strict compatibility with OkHttp 3.x. We’re even\nkeeping the package name the same: `okhttp3`!\n\nThere are three kinds of compatibility we’re tracking:\n\n * **Binary compatibility** is the ability to compile a program against OkHttp 3.x, and then to run\n   it against OkHttp 4.x. We’re using the excellent [japicmp][japicmp] library via its\n   [Gradle plugin][japicmp_gradle] to enforce binary compatibility.\n\n * **Java source compatibility** is the ability to upgrade Java uses of OkHttp 3.x to 4.x without\n   changing `.java` files.\n\n * **Kotlin source compatibility** is the ability to upgrade Kotlin uses of OkHttp 3.x to 4.x\n   without changing `.kt` files.\n\nWith a few small exceptions (below), OkHttp 4.x is both binary- and Java source-compatible with\nOkHttp 3.x. You can use an OkHttp 4.x .jar file with applications or libraries built for OkHttp 3.x.\n\nOkHttp is **not** source-compatible for Kotlin callers, but upgrading should be automatic thanks to\nKotlin’s powerful deprecation features. Most developers should be able to use IntelliJ’s _Code\nCleanup_ for a safe and fast upgrade.\n\n\nBackwards-Incompatible Changes\n------------------------------\n\n#### OkHttpClient final methods\n\n`OkHttpClient` has 26 accessors like `interceptors()` and `writeTimeoutMillis()` that were non-final\nin OkHttp 3.x and are final in 4.x. These were made non-final for use with mocking frameworks like\n[Mockito][mockito]. We believe subtyping `OkHttpClient` is the wrong way to test with OkHttp. If\nyou must, mock `Call.Factory` which is the interface that `OkHttpClient` implements.\n\n#### Internal API changes\n\nThe `okhttp3.internal` package is not a published API and we change it frequently without warning.\nDepending on code in this package is bad and will cause you problems with any upgrade! But the 4.x\nwill be particularly painful to naughty developers that import from this package! We changed a lot\nto take advantage of sweet Kotlin features.\n\n#### Credentials.basic()\n\nThe username and password parameters to `Credentials.basic()` are now non-null strings. In OkHttp\n3.x, null would yield a username or password of \"null\".\n\n#### HttpUrl.queryParameterValues()\n\nThe return type of `HttpUrl.queryParameterValues()` is `List<String?>`. Lists that may contain null\nare uncommon and Kotlin callers may have incorrectly assigned the result to `List<String>`.\n\n\nCode Cleanup\n------------\n\nIntelliJ and Android Studio offer a **Code Cleanup** feature that will automatically update\ndeprecated APIs with their replacements. Access this feature from the _Search Anywhere_ dialog\n(double-press shift) or under the _Analyze_ menu.\n\nWe’ve included deprecated APIs in OkHttp 4.0 because they make migration easy. We will remove them\nin a future release! If you’re skipping releases, it’ll be much easier if you upgrade to OkHttp 4.0\nas an intermediate step.\n\n#### Vars and Vals\n\nJava doesn’t have language support for properties so developers make do with getters and setters.\nKotlin does have properties and we take advantage of them in OkHttp.\n\n * **Address**: certificatePinner, connectionSpecs, dns, hostnameVerifier, protocols, proxy,\n   proxyAuthenticator, proxySelector, socketFactory, sslSocketFactory, url\n * **Cache**: directory\n * **CacheControl**: immutable, maxAgeSeconds, maxStaleSeconds, minFreshSeconds, mustRevalidate,\n   noCache, noStore, noTransform, onlyIfCached, sMaxAgeSeconds\n * **Challenge**: authParams, charset, realm, scheme\n * **CipherSuite**: javaName\n * **ConnectionSpec**: cipherSuites, supportsTlsExtensions, tlsVersions\n * **Cookie**: domain, expiresAt, hostOnly, httpOnly, name, path, persistent, value\n * **Dispatcher**: executorService\n * **FormBody**: size\n * **Handshake**: cipherSuite, localCertificates, localPrincipal, peerCertificates, peerPrincipal,\n   tlsVersion\n * **HandshakeCertificates**: keyManager, trustManager\n * **Headers**: size\n * **HeldCertificate**: certificate, keyPair\n * **HttpLoggingInterceptor**: level\n * **HttpUrl**: encodedFragment, encodedPassword, encodedPath, encodedPathSegments, encodedQuery,\n   encodedUsername, fragment, host, password, pathSegments, pathSize, port, query,\n   queryParameterNames, querySize, scheme, username\n * **MockResponse**: headers, http2ErrorCode, socketPolicy, status, trailers\n * **MockWebServer**: bodyLimit, port, protocolNegotiationEnabled, protocols, requestCount,\n   serverSocketFactory\n * **MultipartBody.Part**: body, headers\n * **MultipartBody.**: boundary, parts, size, type\n * **OkHttpClient**: authenticator, cache, callTimeoutMillis, certificatePinner,\n   connectTimeoutMillis, connectionPool, connectionSpecs, cookieJar, dispatcher, dns,\n   eventListenerFactory, followRedirects, followSslRedirects, hostnameVerifier, interceptors,\n   networkInterceptors, pingIntervalMillis, protocols, proxy, proxyAuthenticator, proxySelector,\n   readTimeoutMillis, retryOnConnectionFailure, socketFactory, sslSocketFactory, writeTimeoutMillis\n * **PushPromise**: headers, method, path, response\n * **Request**: body, cacheControl, headers, method, url\n * **Response**: body, cacheControl, cacheResponse, code, handshake, headers, message,\n   networkResponse, priorResponse, protocol, receivedResponseAtMillis, request, sentRequestAtMillis\n * **Route**: address, proxy, socketAddress\n * **TlsVersion**: javaName\n\n#### Renamed Functions\n\n* **Headers.of()**: for symmetry with `listOf()`, `setOf()`, etc., we’ve replaced\n  `Headers.of(String...)` with `headersOf(vararg String)`.\n\n#### Extension Functions\n\nWe’ve migrated from static functions to extension functions where we think they fit.\n\n| Java                                | Kotlin                          |\n| :---------------------------------- | :------------------------------ |\n| Handshake.get(SSLSession)           | SSLSession.handshake()          |\n| Headers.of(Map<String, String>)     | Map<String, String>.toHeaders() |\n| HttpUrl.get(String)                 | String.toHttpUrl()              |\n| HttpUrl.get(URI)                    | URI.toHttpUrlOrNull()           |\n| HttpUrl.get(URL)                    | URL.toHttpUrlOrNull()           |\n| HttpUrl.parse(String)               | String.toHttpUrlOrNull()        |\n| HttpUrl.uri()                       | HttpUrl.toUri()                 |\n| HttpUrl.url()                       | HttpUrl.toUrl()                 |\n| MediaType.get(String)               | String.toMediaType()            |\n| MediaType.parse(String)             | String.toMediaTypeOrNull()      |\n| RequestBody.create(ByteArray)       | ByteArray.toRequestBody()       |\n| RequestBody.create(ByteString)      | ByteString.toRequestBody()      |\n| RequestBody.create(File)            | File.asRequestBody()            |\n| RequestBody.create(String)          | String.toRequestBody()          |\n| ResponseBody.create(BufferedSource) | BufferedSource.asResponseBody() |\n| ResponseBody.create(ByteArray)      | ByteArray.toResponseBody()      |\n| ResponseBody.create(ByteString)     | ByteString.toResponseBody()     |\n| ResponseBody.create(String)         | String.toResponseBody()         |\n\n\nSAM Conversions\n---------------\n\nWhen you use Java APIs from Kotlin you can operate on Java interfaces as if they were Kotlin\nlambdas. The [feature][java_sams] is available for interfaces that define a Single Abstract Method\n(SAM).\n\nBut when you use Kotlin APIs from Kotlin there’s no automatic conversion. Code that used SAM lambdas\nwith OkHttp 3.x: must use `object :` with OkHttp 4.x:\n\nKotlin calling OkHttp 3.x:\n\n```kotlin\nval client = OkHttpClient.Builder()\n    .dns { hostname -> InetAddress.getAllByName(hostname).toList() }\n    .build()\n```\n\nKotlin calling OkHttp 4.x:\n\n```kotlin\nval client = OkHttpClient.Builder()\n    .dns(object : Dns {\n      override fun lookup(hostname: String) =\n          InetAddress.getAllByName(hostname).toList()\n    })\n    .build()\n```\n\nSAM conversion impacts these APIs:\n\n * Authenticator\n * Dispatcher.setIdleCallback(Runnable)\n * Dns\n * EventListener.Factory\n * HttpLoggingInterceptor.Logger\n * LoggingEventListener.Factory\n * OkHttpClient.Builder.hostnameVerifier(HostnameVerifier)\n\nJetBrains [is working on][kotlin_sams] SAM conversions of Kotlin interfaces. Expect it in a future\nrelease of the Kotlin language.\n\n\nCompanion Imports\n-----------------\n\nThe equivalent of static methods in Java is companion object functions in Kotlin. The bytecode is\nthe same but `.kt` files now need `Companion` in the import.\n\nThis works with OkHttp 3.x:\n\n```kotlin\nimport okhttp3.CipherSuite.forJavaName\n```\n\nBut OkHttp 4.x needs a `Companion`:\n\n```kotlin\nimport okhttp3.CipherSuite.Companion.forJavaName\n```\n\nIn the unlikely event that you have a lot of these, run this:\n\n```bash\nsed -i \"\" \\\n  's/^\\(import okhttp3\\.[^.]*\\)\\.\\([a-z][a-zA-Z]*\\)$/\\1.Companion.\\2/g' \\\n  `find . -name \"*.kt\"`\n```\n\n\nAdvanced Profiling\n------------------\n\nAndroid Studio’s Advanced Profiling feature rewrites OkHttp bytecode for instrumentation.\nUnfortunately it crashes on OkHttp 4.x’s bytecode. Until [Google’s bug][advanced_profiling_bug] is\nfixed you must disable advanced profiling in Android Studio.\n\n![Disable Advanced Profiling](../assets/images/disable_advanced_profiling@2x.png)\n\n\nR8 / ProGuard\n-------------\n\nR8 and ProGuard are both code optimizers for `.class` files.\n\nR8 is the [default optimizer][r8] in Android Studio 3.4 and newer. It works well with all\nreleases of OkHttp.\n\nProGuard was the previous default. We’re [tracking problems][proguard_problems] with interactions\nbetween ProGuard, OkHttp 4.x, and Kotlin-originated `.class` files. Make sure you’re on the latest\nrelease if you’re using ProGuard,\n\n\nGradle\n------\n\nOkHttp 4’s minimum requirements are Java 8+ and Android 5+. These requirements were\n[first introduced][require_android_5] with OkHttp 3.13.\n\nHere’s what you need in `build.gradle` to target Java 8 byte code for Kotlin, Java, and Android\nplugins respectively.\n\n```groovy\ncompileKotlin {\n  kotlinOptions {\n    jvmTarget = \"1.8\"\n  }\n}\ncompileTestKotlin {\n  kotlinOptions {\n    jvmTarget = \"1.8\"\n  }\n}\n\ncompileJava {\n  sourceCompatibility = JavaVersion.VERSION_1_8\n  targetCompatibility = JavaVersion.VERSION_1_8\n}\n\nandroid {\n  compileOptions {\n    sourceCompatibility JavaVersion.VERSION_1_8\n    targetCompatibility JavaVersion.VERSION_1_8\n  }\n}\n```\n\n\n [advanced_profiling_bug]: https://issuetracker.google.com/issues/135141615\n [japicmp]: https://github.com/siom79/japicmp\n [japicmp_gradle]: https://github.com/melix/japicmp-gradle-plugin\n [java_sams]: https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions\n [kotlin_sams]: https://youtrack.jetbrains.com/issue/KT-11129\n [mockito]: https://site.mockito.org/\n [proguard_problems]: https://github.com/square/okhttp/issues/5167\n [require_android_5]: https://code.cash.app/okhttp-3-13-requires-android-5\n [r8]: https://developer.android.com/studio/releases#r8-default\n"
  },
  {
    "path": "docs/contribute/code_of_conduct.md",
    "content": "Open Source Code of Conduct\n===========================\n\nAt Square, we are committed to contributing to the open source community and simplifying the process\nof releasing and managing open source software. We’ve seen incredible support and enthusiasm from\nthousands of people who have already contributed to our projects — and we want to ensure our community\ncontinues to be truly open for everyone.\n\nThis code of conduct outlines our expectations for participants, as well as steps to reporting\nunacceptable behavior. We are committed to providing a welcoming and inspiring community for all and\nexpect our code of conduct to be honored.\n\nSquare’s open source community strives to:\n\n * **Be open**: We invite anyone to participate in any aspect of our projects. Our community is\n   open, and any responsibility can be carried by a contributor who demonstrates the required\n   capacity and competence.\n\n * **Be considerate**: People use our work, and we depend on the work of others. Consider users and\n   colleagues before taking action. For example, changes to code, infrastructure, policy, and\n   documentation may negatively impact others.\n\n * **Be respectful**: We expect people to work together to resolve conflict, assume good intentions,\n   and act with empathy. Do not turn disagreements into personal attacks.\n\n * **Be collaborative**: Collaboration reduces redundancy and improves the quality of our work. We\n   strive for transparency within our open source community, and we work closely with upstream\n   developers and others in the free software community to coordinate our efforts.\n\n * **Be pragmatic**: Questions are encouraged and should be asked early in the process to avoid\n   problems later. Be thoughtful and considerate when seeking out the appropriate forum for your\n   questions. Those who are asked should be responsive and helpful.\n\n * **Step down considerately**: Members of every project come and go. When somebody leaves or\n   disengages from the project, they should make it known and take the proper steps to ensure that\n   others can pick up where they left off.\n\nThis code is not exhaustive or complete. It serves to distill our common understanding of a\ncollaborative, shared environment, and goals. We expect it to be followed in spirit as much as in\nthe letter.\n\nDiversity Statement\n-------------------\n\nWe encourage everyone to participate and are committed to building a community for all. Although we\nmay not be able to satisfy everyone, we all agree that everyone is equal.\n\nWhenever a participant has made a mistake, we expect them to take responsibility for it. If someone\nhas been harmed or offended, it is our responsibility to listen carefully and respectfully, and do\nour best to right the wrong.\n\nAlthough this list cannot be exhaustive, we explicitly honor diversity in age, culture, ethnicity,\ngender identity or expression, language, national origin, political beliefs, profession, race,\nreligion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate\ndiscrimination based on any of the protected characteristics above, including participants with\ndisabilities.\n\nReporting Issues\n----------------\n\nIf you experience or witness unacceptable behavior — or have any other concerns — please report it by\nemailing [codeofconduct@squareup.com][codeofconduct_at]. For more details, please see our Reporting\nGuidelines below.\n\nThanks\n------\n\nSome of the ideas and wording for the statements and guidelines above were based on work by the\n[Twitter][twitter_coc], [Ubuntu][ubuntu_coc], [GDC][gdc_coc], and [Django][django_coc] communities.\nWe are thankful for their work.\n\nReporting Guide\n---------------\n\nIf you experience or witness unacceptable behavior — or have any other concerns — please report it by\nemailing [codeofconduct@squareup.com][codeofconduct_at]. All reports will be handled with\ndiscretion.\n\nIn your report please include:\n\n * Your contact information.\n * Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional\n   witnesses, please include them as well.\n * Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly\n   available record (e.g. a mailing list archive or a public IRC logger), please include a link.\n * Any additional information that may be helpful.\n\nAfter filing a report, a representative from the Square Code of Conduct committee will contact you\npersonally. The committee will then review the incident, follow up with any additional questions,\nand make a decision as to how to respond.\n\nAnyone asked to stop unacceptable behavior is expected to comply immediately. If an individual\nengages in unacceptable behavior, the Square Code of Conduct committee may take any action they deem\nappropriate, up to and including a permanent ban from all of Square spaces without warning.\n\n\n[codeofconduct_at]: mailto:codeofconduct@squareup.com\n[twitter_coc]: https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md\n[ubuntu_coc]: https://ubuntu.com/community/code-of-conduct\n[gdc_coc]: https://www.gdconf.com/code-of-conduct\n[django_coc]: https://www.djangoproject.com/conduct/reporting/\n\n"
  },
  {
    "path": "docs/contribute/concurrency.md",
    "content": "Concurrency\n===========\n\nThis document describes the concurrency considerations for http/2 connections and the connection pool within OkHttp.\n\n## HTTP/2 Connections\n\nThe HttpURLConnection API is a blocking API. You make a blocking write to send a request, and a blocking read to receive the response.\n\n#### Blocking APIs\n\nBlocking APIs are convenient because you get top-to-bottom procedural code without indirection. Network calls work like regular method calls: ask for data and it is returned. If the request fails, you get a stacktrace right where the call was made.\n\nBlocking APIs may be inefficient because you hold a thread idle while waiting on the network. Threads are expensive because they have both a memory overhead and a context-switching overhead.\n\n#### Framed protocols\n\nFramed protocols like http/2 don't lend themselves to blocking APIs. Each application-layer thread wants to do blocking I/O for a specific stream, but the streams are multiplexed on the socket. You can't just talk to the socket, you need to cooperate with the other application-layer threads that you're sharing it with.\n\nFraming rules make it impractical to implement http/2 correctly on a single blocking thread. The flow-control features introduce feedback between reads and writes, requiring writes to acknowledge reads and reads to throttle writes.\n\nIn OkHttp we expose a blocking API over a framed protocol. This document explains the code and policy that makes that work.\n\n### Threads\n\n#### Application's calling thread\n\nThe application-layer must block on writing I/O. We can't return from a write until we've pushed its bytes onto the socket. Otherwise, if the write fails we are unable to deliver its IOException to the application. We would have told the application layer that the write succeeded, but it didn't!\n\nThe application-layer can also do blocking reads. If the application asks to read and there's nothing available, we need to hold that thread until either the bytes arrive, the stream is closed, or a timeout elapses. If we get bytes but there's nobody asking for them, we buffer them. We don't consider bytes as delivered for flow control until they're consumed by the application.\n\nConsider an application streaming a video over http/2. Perhaps the user pauses the video and the application stops reading bytes from this stream. The buffer will fill up, and flow control prevents the server from sending more data on this stream. When the user unpauses her video the buffer drains, the read is acknowledged, and the server proceeds to stream data.\n\n#### Shared reader thread\n\nWe can't rely on application threads to read data from the socket. Application threads are transient: sometimes they're reading and writing and sometimes they're off doing application-layer things. But the socket is permanent, and it needs constant attention: we dispatch all incoming frames so the connection is good-to-go when the application layer needs it.\n\nSo we have a dedicated thread for every socket that just reads frames and dispatches them.\n\nThe reader thread must never run application-layer code. Otherwise one slow stream can hold up the entire connection.\n\nSimilarly, the reader thread must never block on writing because this can deadlock the connection. Consider a client and server that both violate this rule. If you get unlucky, they could fill up their TCP buffers (so that writes block) and then use their reader threads to write a frame. Nobody is reading on either end, and the buffers are never drained.\n\n#### Do-stuff-later pool\n\nSometimes there's an action required like calling the application layer or responding to a ping, and the thread discovering the action is not the thread that should do the work. We enqueue a runnable on this executor and it gets handled by one of the executor's threads.\n\n### Locks\n\nWe have 3 different things that we synchronize on.\n\n#### Http2Connection\n\nThis lock guards internal state of each connection. This lock is never held for blocking operations. That means that we acquire the lock, read or write a few fields and release the lock. No I/O and no application-layer callbacks.\n\n#### Http2Stream\n\nThis lock guards the internal state of each stream. As above, it is never held for blocking operations. When we need to hold an application thread to block a read, we use wait/notify on this lock. This works because the lock is released while `wait()` is waiting.\n\n#### Http2Writer\n\nSocket writes are guarded by the Http2Writer. Only one stream can write at a time so that messages are not interleaved. Writes are either made by application-layer threads or the do-stuff-later pool.\n\n### Holding multiple locks\n\nYou're allowed to take the Http2Connection lock while holding the Http2Writer lock. But not vice-versa. Because taking the Http2Writer lock can block.\n\nThis is necessary for bookkeeping when creating new streams. Correct framing requires that stream IDs are sequential on the socket, so we need to bundle assigning the ID with sending the `SYN_STREAM` frame.\n\n## Connection Pool\n\nA primary responsibility for any HTTP client is to efficiently manage network connections. Creating and establishing new connections require a fair amount of overhead and added latency. OkHttp will make every effort to reuse existing connections to avoid this overhead and added latency.\n\nEvery OkHttpClient uses a connection pool. Its job is to maintain a reference to all open connections. When an HTTP request is started, OkHttp will attempt to reuse an existing connection from the pool. If there are no existing connections, a new one is created and put into the connection pool. For HTTP/2, the connection can be reused immediately. For HTTP/1, the request must be completed before it can be reused.\n\nSince HTTP requests frequently happen in parallel, connection pooling must be thread-safe.\n\nThese are the primary classes involved with establishing, sharing, and terminating connections:\n\n * **RealConnectionPool** manages reuse of HTTP and HTTP/2 connections for reduced latency. Every OkHttpClient has one, and its lifetime spans the lifetime of the OkHttpClient.\n\n * **RealConnection** is the socket and streams of an HTTP/1 or HTTP/2 connection. These are created on demand to fulfill HTTP requests. They may be reused for many HTTP request/response exchanges. Their lifetime is typically shorter than a connection pool.\n\n * **Exchange** carries a single HTTP request/response pair.\n\n * **ExchangeFinder** chooses which connection carries each exchange. Where possible it will use the same connection for all exchanges in a single call. It prefers reusing pooled connections over establishing new connections.      \n\n#### Per-Connection Locks\n\nEach connection has its own lock. The connections in the pool are all in a `ConcurrentLinkedQueue`. Due to data races, iterators of this queue may return removed connections. Callers must check the connection's `noNewExchanges` property before using connections from the pool.\n\nThe connection lock is never held while doing I/O (even closing a socket) to prevent contention.\n\nA lock-per-connection is used to maximize concurrency.\n"
  },
  {
    "path": "docs/contribute/contributing.md",
    "content": "Contributing\n============\n\nKeeping the project small and stable limits our ability to accept new contributors. We are not\nseeking new committers at this time, but some small contributions are welcome.\n\nIf you've found a security problem, please follow our [bug bounty][security] program.\n\nIf you've found a bug, please contribute a failing test case so we can study and fix it.\n\nIf you have a new feature idea, please build it in an external library. There are\n[many libraries][works_with_okhttp] that sit on top or hook in via existing APIs. If you build\nsomething that integrates with OkHttp, tell us so that we can link it!\n\nBefore code can be accepted all contributors must complete our\n[Individual Contributor License Agreement (CLA)][cla].\n\n\nCode Contributions\n------------------\n\nGet working code on a personal branch with tests passing before you submit a PR:\n\n```\n./gradlew clean check\n```\n\nPlease make every effort to follow existing conventions and style in order to keep the code as\nreadable as possible.\n\nContribute code changes through GitHub by forking the repository and sending a pull request. We\nsquash all pull requests on merge.\n\n\nGradle Setup\n------------\n\n```\n$ cat local.properties\nsdk.dir=PATH_TO_ANDROID_HOME/sdk\norg.gradle.caching=true\n```\n\nRunning Android Tests\n---------------------\n\n$ ANDROID_SDK_ROOT=PATH_TO_ANDROID_HOME/sdk ./gradlew :android-test:connectedCheck -PandroidBuild=true\n\nCommitter's Guides\n------------------\n\n * [Concurrency][concurrency]\n * [Debug Logging][debug_logging]\n * [Releasing][releasing]\n\n [cla]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1\n [concurrency]: https://square.github.io/okhttp/concurrency/\n [debug_logging]: https://square.github.io/okhttp/debug_logging/\n [releasing]: https://square.github.io/okhttp/releasing/\n [security]: https://square.github.io/okhttp/security/\n [works_with_okhttp]: https://square.github.io/okhttp/works_with_okhttp/\n [okhttp_build]: https://github.com/square/okhttp/blob/master/okhttp/build.gradle\n"
  },
  {
    "path": "docs/contribute/debug_logging.md",
    "content": "Debug Logging\n=============\n\nOkHttp has internal APIs to enable debug logging. It uses the `java.util.logging` API which can be\ntricky to configure. As a shortcut, you can paste [OkHttpDebugLogging.kt]. Then enable debug logging\nfor whichever features you need:\n\n```\nOkHttpDebugLogging.enableHttp2()\nOkHttpDebugLogging.enableTaskRunner()\n```\n\n### Activating on Android\n\n```\n$ adb shell setprop log.tag.okhttp.Http2 DEBUG\n$ adb shell setprop log.tag.okhttp.TaskRunner DEBUG\n$ adb logcat '*:E' 'okhttp.Http2:D' 'okhttp.TaskRunner:D'\n```\n\n### HTTP/2 Frame Logging\n\nThis logs inbound (`<<`) and outbound (`>>`) frames for HTTP/2 connections.\n\n```\n[2020-01-01 00:00:00] >> CONNECTION 505249202a20485454502f322e300d0a0d0a534d0d0a0d0a\n[2020-01-01 00:00:00] >> 0x00000000     6 SETTINGS\n[2020-01-01 00:00:00] >> 0x00000000     4 WINDOW_UPDATE\n[2020-01-01 00:00:00] >> 0x00000003    47 HEADERS       END_STREAM|END_HEADERS\n[2020-01-01 00:00:00] << 0x00000000     6 SETTINGS\n[2020-01-01 00:00:00] << 0x00000000     0 SETTINGS      ACK\n[2020-01-01 00:00:00] << 0x00000000     4 WINDOW_UPDATE\n[2020-01-01 00:00:00] >> 0x00000000     0 SETTINGS      ACK\n[2020-01-01 00:00:00] << 0x00000003   322 HEADERS       END_HEADERS\n[2020-01-01 00:00:00] << 0x00000003   288 DATA\n[2020-01-01 00:00:00] << 0x00000003     0 DATA          END_STREAM\n[2020-01-01 00:00:00] << 0x00000000     8 GOAWAY\n[2020-01-01 00:00:05] << 0x00000000     8 GOAWAY\n```\n\n### Task Runner Logging \n\nThis logs task enqueues, starts, and finishes.\n\n```\n[2020-01-01 00:00:00] Q10000 scheduled after   0 µs: OkHttp ConnectionPool\n[2020-01-01 00:00:00] Q10000 starting              : OkHttp ConnectionPool\n[2020-01-01 00:00:00] Q10000 run again after 300 s : OkHttp ConnectionPool\n[2020-01-01 00:00:00] Q10000 finished run in   1 ms: OkHttp ConnectionPool\n[2020-01-01 00:00:00] Q10001 scheduled after   0 µs: OkHttp squareup.com applyAndAckSettings\n[2020-01-01 00:00:00] Q10001 starting              : OkHttp squareup.com applyAndAckSettings\n[2020-01-01 00:00:00] Q10003 scheduled after   0 µs: OkHttp squareup.com onSettings\n[2020-01-01 00:00:00] Q10003 starting              : OkHttp squareup.com onSettings\n[2020-01-01 00:00:00] Q10001 finished run in   3 ms: OkHttp squareup.com applyAndAckSettings\n[2020-01-01 00:00:00] Q10003 finished run in 528 µs: OkHttp squareup.com onSettings\n[2020-01-01 00:00:00] Q10000 scheduled after   0 µs: OkHttp ConnectionPool\n[2020-01-01 00:00:00] Q10000 starting              : OkHttp ConnectionPool\n[2020-01-01 00:00:00] Q10000 run again after 300 s : OkHttp ConnectionPool\n[2020-01-01 00:00:00] Q10000 finished run in 739 µs: OkHttp ConnectionPool\n```\n\n[OkHttpDebugLogging.kt]: https://github.com/square/okhttp/blob/master/okhttp-testing-support/src/main/kotlin/okhttp3/OkHttpDebugLogging.kt\n"
  },
  {
    "path": "docs/features/caching.md",
    "content": "Caching\n=======\n\nOkHttp implements an optional, off by default, Cache. OkHttp aims for RFC correct and\npragmatic caching behaviour, following common real-world browser like Firefox/Chrome and\nserver behaviour when ambiguous.\n\n# Basic Usage\n\n```kotlin\n  private val client: OkHttpClient = OkHttpClient.Builder()\n      .cache(Cache(\n          directory = File(application.cacheDir, \"http_cache\"),\n          // $0.05 worth of phone storage in 2020\n          maxSize = 50L * 1024L * 1024L // 50 MiB\n      ))\n      .build()\n```\n\n## EventListener events\n\nCache Events are exposed via the EventListener API.  Typical scenarios are below.\n\n### Cache Hit\n\nIn the ideal scenario the cache can fulfill the request without any conditional call to the network.\nThis will skip the normal events such as DNS, connecting to the network, and downloading the response body.\n\nAs recommended by the HTTP RFC the max age of a document is defaulted to 10% of the\ndocument's age at the time it was served based on \"Last-Modified\". Default expiration dates aren't used for URIs\ncontaining a query.\n\n - CallStart\n - **CacheHit**\n - CallEnd\n\n### Cache Miss\n\nUnder a cache miss the normal request events are seen but an additional event shows the presence of the cache.\nCache Miss will be typical if the item has not been read from the network, is uncacheable, or is past it's\nlifetime based on Response cache headers.\n\n - CallStart\n - **CacheMiss**\n - ProxySelectStart\n - ... Standard Events ...\n - CallEnd\n\n### Conditional Cache Hit\n\nWhen cache flags require checking the cache results are still valid an early cacheConditionalHit event is\nreceived followed by a cache hit or miss.  Critically in the cache hit scenario the server won’t send the response body.\n\nThe response will have non-null `cacheResponse` and `networkResponse`. The cacheResponse will be used as the top level\nresponse only if the response code is HTTP/1.1 304 Not Modified.\n\n - CallStart\n - **CacheConditionalHit**\n - ConnectionAcquired\n - ... Standard Events...\n - ResponseBodyEnd _(0 bytes)_\n - **CacheHit**\n - ConnectionReleased\n - CallEnd\n\n## Cache directory\n\nThe cache directory must be exclusively owned by a single instance.\n\nDeleting the cache when it is no longer needed can be done.  However this may delete the purpose of the cache\nwhich is designed to persist between app restarts.\n\n```kotlin\ncache.delete()\n```\n\n## Pruning the Cache\n\nPruning the entire Cache to clear space temporarily can be done using evictAll.\n\n```kotlin\ncache.evictAll()\n```\n\nRemoving individual items can be done using the urls iterator.\nThis would be typical after a user initiates a force refresh by a pull to refresh type action.\n\n```java\n    val urlIterator = cache.urls()\n    while (urlIterator.hasNext()) {\n      if (urlIterator.next().startsWith(\"https://www.google.com/\")) {\n        urlIterator.remove()\n      }\n    }\n```\n\n### Troubleshooting\n\n1. Valid cacheable responses are not being cached\n\nMake sure you are reading responses fully as unless they are read fully, cancelled or stalled Responses will not be cached.\n\n### Overriding normal cache behaviour\n\nSee [`Cache`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-cache/) documentation.\n"
  },
  {
    "path": "docs/features/calls.md",
    "content": "# Calls\n\nThe HTTP client’s job is to accept your request and produce its response. This is simple in theory but it gets tricky in practice.\n\n## [Requests](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-request/)\n\nEach HTTP request contains a URL, a method (like `GET` or `POST`), and a list of headers. Requests may also contain a body: a data stream of a specific content type.\n\n## [Responses](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-response/)\n\nThe response answers the request with a code (like 200 for success or 404 for not found), headers, and its own optional body.\n\n## Rewriting Requests\n\nWhen you provide OkHttp with an HTTP request, you’re describing the request at a high-level: _“fetch me this URL with these headers.”_ For correctness and efficiency, OkHttp rewrites your request before transmitting it.\n\nOkHttp may add headers that are absent from the original request, including `Content-Length`, `Transfer-Encoding`, `User-Agent`, `Host`, `Connection`, and `Content-Type`. It will add an `Accept-Encoding` header for transparent response compression unless the header is already present. If you’ve got cookies, OkHttp will add a `Cookie` header with them.\n\nSome requests will have a cached response. When this cached response isn’t fresh, OkHttp can do a _conditional GET_ to download an updated response if it’s newer than what’s cached. This requires headers like `If-Modified-Since` and `If-None-Match` to be added.\n\n## Rewriting Responses\n\nIf transparent compression was used, OkHttp will drop the corresponding response headers `Content-Encoding` and `Content-Length` because they don’t apply to the decompressed response body.\n\nIf a conditional GET was successful, responses from the network and cache are merged as directed by the spec.\n\n## Follow-up Requests\n\nWhen your requested URL has moved, the webserver will return a response code like `302` to indicate the document’s new URL. OkHttp will follow the redirect to retrieve a final response.\n\nIf the response issues an authorization challenge, OkHttp will ask the [`Authenticator`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-authenticator/) (if one is configured) to satisfy the challenge. If the authenticator supplies a credential, the request is retried with that credential included.\n\n## Retrying Requests\n\nSometimes connections fail: either a pooled connection was stale and disconnected, or the webserver itself couldn’t be reached. OkHttp will retry the request with a different route if one is available.\n\n## [Calls](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-call/)\n\nWith rewrites, redirects, follow-ups and retries, your simple request may yield many requests and responses. OkHttp uses `Call` to model the task of satisfying your request through however many intermediate requests and responses are necessary. Typically this isn’t many! But it’s comforting to know that your code will continue to work if your URLs are redirected or if you failover to an alternate IP address.\n\nCalls are executed in one of two ways:\n\n * **Synchronous:** your thread blocks until the response is readable.\n * **Asynchronous:** you enqueue the request on any thread, and get [called back](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-callback/) on another thread when the response is readable.\n\nCalls can be canceled from any thread. This will fail the call if it hasn’t yet completed! Code that is writing the request body or reading the response body will suffer an `IOException` when its call is canceled.\n\n## Dispatch\n\nFor synchronous calls, you bring your own thread and are responsible for managing how many simultaneous requests you make. Too many simultaneous connections wastes resources; too few harms latency.\n\nFor asynchronous calls, [`Dispatcher`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-dispatcher/) implements policy for maximum simultaneous requests. You can set maximums per-webserver (default is 5), and overall (default is 64).\n"
  },
  {
    "path": "docs/features/connections.md",
    "content": "Connections\n===========\n\nAlthough you provide only the URL, OkHttp plans its connection to your webserver using three types: URL, Address, and Route.\n\n### [URLs](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-http-url/)\n\nURLs (like `https://github.com/square/okhttp`) are fundamental to HTTP and the Internet. In addition to being a universal, decentralized naming scheme for everything on the web, they also specify how to access web resources.\n\nURLs are abstract:\n\n * They specify that the call may be plaintext (`http`) or encrypted (`https`), but not which cryptographic algorithms should be used. Nor do they specify how to verify the peer's certificates (the [HostnameVerifier](https://developer.android.com/reference/javax/net/ssl/HostnameVerifier.html)) or which certificates can be trusted (the [SSLSocketFactory](https://developer.android.com/reference/org/apache/http/conn/ssl/SSLSocketFactory.html)).\n * They don't specify whether a specific proxy server should be used or how to authenticate with that proxy server.\n\nThey're also concrete: each URL identifies a specific path (like `/square/okhttp`) and query (like `?q=sharks&lang=en`). Each webserver hosts many URLs.\n\n### [Addresses](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-address/)\n\nAddresses specify a webserver (like `github.com`) and all of the **static** configuration necessary to connect to that server: the port number, HTTPS settings, and preferred network protocols (like HTTP/2).\n\nURLs that share the same address may also share the same underlying TCP socket connection. Sharing a connection has substantial performance benefits: lower latency, higher throughput (due to [TCP slow start](https://www.igvita.com/2011/10/20/faster-web-vs-tcp-slow-start/)) and conserved battery. OkHttp uses a [ConnectionPool](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-connection-pool/) that automatically reuses HTTP/1.x connections and multiplexes HTTP/2 connections.\n\nIn OkHttp some fields of the address come from the URL (scheme, hostname, port) and the rest come from the [OkHttpClient](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/).\n\n### [Routes](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-route/)\n\nRoutes supply the **dynamic** information necessary to actually connect to a webserver. This is the specific IP address to attempt (as discovered by a DNS query), the exact proxy server to use (if a [ProxySelector](https://developer.android.com/reference/java/net/ProxySelector.html) is in use), and which version of TLS to negotiate (for HTTPS connections).\n\nThere may be many routes for a single address. For example, a webserver that is hosted in multiple datacenters may yield multiple IP addresses in its DNS response.\n\nIn limited situations OkHttp will retry a route if connecting fails:\n\n * When making an HTTPS connection through an HTTP proxy, the proxy may issue an authentication challenge. OkHttp will call the proxy [authenticator](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-authenticator/) and try again.\n * When making TLS connections with multiple [connection specs](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-connection-spec/), these are attempted in sequence until the TLS handshake succeeds.\n\n### [Connections](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-connection/)\n\nWhen you request a URL with OkHttp, here's what it does:\n\n 1. It uses the URL and configured OkHttpClient to create an **address**. This address specifies how we'll connect to the webserver.\n 2. It attempts to retrieve a connection with that address from the **connection pool**.\n 3. If it doesn't find a connection in the pool, it selects a **route** to attempt. This usually means making a DNS request to get the server's IP addresses. It then selects a TLS version and proxy server if necessary.\n 4. If it's a new route, it connects by building either a direct socket connection, a TLS tunnel (for HTTPS over an HTTP proxy), or a direct TLS connection. It does TLS handshakes as necessary. This step may be retried for tunnel challenges and TLS handshake failures.\n 5. It sends the HTTP request and reads the response.\n\nIf there's a problem with the connection, OkHttp will select another route and try again. This allows OkHttp to recover when a subset of a server's addresses are unreachable. It's also useful when a pooled connection is stale or if the attempted TLS version is unsupported.\n\nOnce the response has been received, the connection will be returned to the pool so it can be reused for a future request. Connections are evicted from the pool after a period of inactivity.\n\n### [Fast Fallback](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/fast-fallback)\n\nSince version 5.0, `OkHttpClient` supports fast fallback, which is our implementation of Happy Eyeballs [RFC 6555](https://datatracker.ietf.org/doc/html/rfc6555).\n\nWith fast fallback, OkHttp attempts to connect to multiple web servers concurrently. It keeps whichever route connects first and cancels all of the others. Its rules are:\n\n * Prefer to alternate IP addresses from different address families, (IPv6 / IPv4), starting with IPv6.\n * Don't start a new attempt until 250 ms after the most recent attempt was started.\n * Keep whichever TCP connection succeeds first and cancel all the others.\n * Race TCP only. Only attempt a TLS handshake on the winning TCP connection.\n\nIf the winner of the TCP handshake race fails to succeed in a TLS handshake, the process is restarted with the remaining routes.\n\n"
  },
  {
    "path": "docs/features/events.md",
    "content": "Events\n======\n\nEvents allow you to capture metrics on your application’s HTTP calls. Use events to monitor:\n\n * The size and frequency of the HTTP calls your application makes. If you’re making too many calls, or your calls are too large, you should know about it!\n * The performance of these calls on the underlying network. If the network’s performance isn’t sufficient, you need to either improve the network or use less of it.\n\n### EventListener\n\nSubclass [EventListener](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-event-listener/) and override methods for the events you are interested in. In a successful HTTP call with no redirects or retries the sequence of events is described by this flow.\n\n![Events Diagram](../assets/images/events@2x.png)\n\nHere’s a [sample event listener](https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PrintEventsNonConcurrent.java) that prints each event with a timestamp.\n\n```java\nclass PrintingEventListener extends EventListener {\n  private long callStartNanos;\n\n  private void printEvent(String name) {\n    long nowNanos = System.nanoTime();\n    if (name.equals(\"callStart\")) {\n      callStartNanos = nowNanos;\n    }\n    long elapsedNanos = nowNanos - callStartNanos;\n    System.out.printf(\"%.3f %s%n\", elapsedNanos / 1000000000d, name);\n  }\n\n  @Override public void callStart(Call call) {\n    printEvent(\"callStart\");\n  }\n\n  @Override public void callEnd(Call call) {\n    printEvent(\"callEnd\");\n  }\n\n  @Override public void dnsStart(Call call, String domainName) {\n    printEvent(\"dnsStart\");\n  }\n\n  @Override public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {\n    printEvent(\"dnsEnd\");\n  }\n\n  ...\n}\n```\n\nWe make a couple calls:\n\n```java\nRequest request = new Request.Builder()\n    .url(\"https://publicobject.com/helloworld.txt\")\n    .build();\n\nSystem.out.println(\"REQUEST 1 (new connection)\");\ntry (Response response = client.newCall(request).execute()) {\n  // Consume and discard the response body.\n  response.body().source().readByteString();\n}\n\nSystem.out.println(\"REQUEST 2 (pooled connection)\");\ntry (Response response = client.newCall(request).execute()) {\n  // Consume and discard the response body.\n  response.body().source().readByteString();\n}\n```\n\nAnd the listener prints the corresponding events:\n\n```\nREQUEST 1 (new connection)\n0.000 callStart\n0.010 dnsStart\n0.017 dnsEnd\n0.025 connectStart\n0.117 secureConnectStart\n0.586 secureConnectEnd\n0.586 connectEnd\n0.587 connectionAcquired\n0.588 requestHeadersStart\n0.590 requestHeadersEnd\n0.591 responseHeadersStart\n0.675 responseHeadersEnd\n0.676 responseBodyStart\n0.679 responseBodyEnd\n0.679 connectionReleased\n0.680 callEnd\nREQUEST 2 (pooled connection)\n0.000 callStart\n0.001 connectionAcquired\n0.001 requestHeadersStart\n0.001 requestHeadersEnd\n0.002 responseHeadersStart\n0.082 responseHeadersEnd\n0.082 responseBodyStart\n0.082 responseBodyEnd\n0.083 connectionReleased\n0.083 callEnd\n```\n\nNotice how no connect events are fired for the second call. It reused the connection from the first request for dramatically better performance.\n\n### EventListener.Factory\n\nIn the preceding example we used a field, `callStartNanos`, to track the elapsed time of each event. This is handy, but it won’t work if multiple calls are executing concurrently. To accommodate this, use a `Factory` to create a new `EventListener` instance for each `Call`. This allows each listener to keep call-specific state.\n\nThis [sample factory](https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PrintEvents.java) creates a unique ID for each call and uses that ID to differentiate calls in log messages.\n\n```java\nclass PrintingEventListener extends EventListener {\n  public static final Factory FACTORY = new Factory() {\n    final AtomicLong nextCallId = new AtomicLong(1L);\n\n    @Override public EventListener create(Call call) {\n      long callId = nextCallId.getAndIncrement();\n      System.out.printf(\"%04d %s%n\", callId, call.request().url());\n      return new PrintingEventListener(callId, System.nanoTime());\n    }\n  };\n\n  final long callId;\n  final long callStartNanos;\n\n  public PrintingEventListener(long callId, long callStartNanos) {\n    this.callId = callId;\n    this.callStartNanos = callStartNanos;\n  }\n\n  private void printEvent(String name) {\n    long elapsedNanos = System.nanoTime() - callStartNanos;\n    System.out.printf(\"%04d %.3f %s%n\", callId, elapsedNanos / 1000000000d, name);\n  }\n\n  @Override public void callStart(Call call) {\n    printEvent(\"callStart\");\n  }\n\n  @Override public void callEnd(Call call) {\n    printEvent(\"callEnd\");\n  }\n\n  ...\n}\n```\n\nWe can use this listener to race a pair of concurrent HTTP requests:\n\n```java\nRequest washingtonPostRequest = new Request.Builder()\n    .url(\"https://www.washingtonpost.com/\")\n    .build();\nclient.newCall(washingtonPostRequest).enqueue(new Callback() {\n  ...\n});\n\nRequest newYorkTimesRequest = new Request.Builder()\n    .url(\"https://www.nytimes.com/\")\n    .build();\nclient.newCall(newYorkTimesRequest).enqueue(new Callback() {\n  ...\n});\n```\n\nRunning this race over home WiFi shows the Times (`0002`) completes just slightly sooner than the Post (`0001`):\n\n```\n0001 https://www.washingtonpost.com/\n0001 0.000 callStart\n0002 https://www.nytimes.com/\n0002 0.000 callStart\n0002 0.010 dnsStart\n0001 0.013 dnsStart\n0001 0.022 dnsEnd\n0002 0.019 dnsEnd\n0001 0.028 connectStart\n0002 0.025 connectStart\n0002 0.072 secureConnectStart\n0001 0.075 secureConnectStart\n0001 0.386 secureConnectEnd\n0002 0.390 secureConnectEnd\n0002 0.400 connectEnd\n0001 0.403 connectEnd\n0002 0.401 connectionAcquired\n0001 0.404 connectionAcquired\n0001 0.406 requestHeadersStart\n0002 0.403 requestHeadersStart\n0001 0.414 requestHeadersEnd\n0002 0.411 requestHeadersEnd\n0002 0.412 responseHeadersStart\n0001 0.415 responseHeadersStart\n0002 0.474 responseHeadersEnd\n0002 0.475 responseBodyStart\n0001 0.554 responseHeadersEnd\n0001 0.555 responseBodyStart\n0002 0.554 responseBodyEnd\n0002 0.554 connectionReleased\n0002 0.554 callEnd\n0001 0.624 responseBodyEnd\n0001 0.624 connectionReleased\n0001 0.624 callEnd\n```\n\nThe `EventListener.Factory` also makes it possible to limit metrics to a subset of calls. This one captures metrics on a random 10%:\n\n```java\nclass MetricsEventListener extends EventListener {\n  private static final Factory FACTORY = new Factory() {\n    @Override public EventListener create(Call call) {\n      if (Math.random() < 0.10) {\n        return new MetricsEventListener(call);\n      } else {\n        return EventListener.NONE;\n      }\n    }\n  };\n\n  ...\n}\n```\n\n### Events with Failures\n\nWhen an operation fails, a failure method is called. This is `connectFailed()` for failures while building a connection to the server, and `callFailed()` when the HTTP call fails permanently. When a failure happens it is possible that a `start` event won’t have a corresponding `end` event.\n\n![Events Diagram](../assets/images/events_with_failures@2x.png)\n\n### Events with Retries and Follow-Ups\n\nOkHttp is resilient and can automatically recover from some connectivity failures. In this case, the `connectFailed()` event is not terminal and not followed by `callFailed()`. Event listeners will receive multiple events of the same type when retries are attempted.\n\nA single HTTP call may require follow-up requests to be made to handle authentication challenges, redirects, and HTTP-layer timeouts. In such cases multiple connections, requests, and responses may be attempted. Follow-ups are another reason a single call may trigger multiple events of the same type.\n\n![Events Diagram](../assets/images/events_with_failures_and_retries@2x.png)\n\n### Availability\n\nEvents is available as a public API in OkHttp 3.11. Future releases may introduce new event types; you will need to override the corresponding methods to handle them.\n"
  },
  {
    "path": "docs/features/https.md",
    "content": "HTTPS\n=====\n\nOkHttp attempts to balance two competing concerns:\n\n * **Connectivity** to as many hosts as possible. That includes advanced hosts that run the latest versions of [boringssl](https://boringssl.googlesource.com/boringssl/) and less out of date hosts running older versions of [OpenSSL](https://www.openssl.org/).\n * **Security** of the connection. This includes verification of the remote webserver with certificates and the privacy of data exchanged with strong ciphers.\n\nWhen negotiating a connection to an HTTPS server, OkHttp needs to know which [TLS versions](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-tls-version/) and [cipher suites](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-cipher-suite/) to offer. A client that wants to maximize connectivity would include obsolete TLS versions and weak-by-design cipher suites. A strict client that wants to maximize security would be limited to only the latest TLS version and strongest cipher suites.\n\nSpecific security vs. connectivity decisions are implemented by [ConnectionSpec](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-connection-spec/). OkHttp includes four built-in connection specs:\n\n * `RESTRICTED_TLS` is a secure configuration, intended to meet stricter compliance requirements.\n * `MODERN_TLS` is a secure configuration that connects to modern HTTPS servers.\n * `COMPATIBLE_TLS` is a secure configuration that connects to secure–but not current–HTTPS servers.\n * `CLEARTEXT` is an insecure configuration that is used for `http://` URLs.\n\nThese loosely follow the model set in [Google Cloud Policies](https://cloud.google.com/load-balancing/docs/ssl-policies-concepts). We [track changes](../security/tls_configuration_history.md) to this policy.\n\nBy default, OkHttp will attempt a `MODERN_TLS` connection.  However by configuring the client connectionSpecs you can allow a fall back to `COMPATIBLE_TLS` connection if the modern configuration fails.\n\n```java\nOkHttpClient client = new OkHttpClient.Builder()\n    .connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))\n    .build();\n```\n\nThe TLS versions and cipher suites in each spec can change with each release. For example, in OkHttp 2.2 we dropped support for SSL 3.0 in response to the [POODLE](https://googleonlinesecurity.blogspot.ca/2014/10/this-poodle-bites-exploiting-ssl-30.html) attack. And in OkHttp 2.3 we dropped support for [RC4](https://en.wikipedia.org/wiki/RC4#Security). As with your desktop web browser, staying up-to-date with OkHttp is the best way to stay secure.\n\nYou can build your own connection spec with a custom set of TLS versions and cipher suites. For example, this configuration is limited to three highly-regarded cipher suites. Its drawback is that it requires Android 5.0+ and a similarly current webserver.\n\n```java\nConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)\n    .tlsVersions(TlsVersion.TLS_1_2)\n    .cipherSuites(\n          CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n          CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\n          CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)\n    .build();\n\nOkHttpClient client = new OkHttpClient.Builder()\n    .connectionSpecs(Collections.singletonList(spec))\n    .build();\n```\n\n### Debugging TLS Handshake Failures\n\nThe TLS handshake requires clients and servers to share a common TLS version and cipher suite. This\ndepends on the JVM or Android version, OkHttp version, and web server configuration. If there is no\ncommon cipher suite and TLS version, your call will fail like this:\n\n```\nCaused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x7f2719a89e80:\n    Failure in SSL library, usually a protocol error\n        error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake\n        failure (external/openssl/ssl/s23_clnt.c:770 0x7f2728a53ea0:0x00000000)\n    at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)\n```\n\nYou can check a web server's configuration using [Qualys SSL Labs][qualys]. OkHttp's TLS\nconfiguration history is [tracked here](../security/tls_configuration_history.md).\n\nApplications expected to be installed on older Android devices should consider adopting the\n[Google Play Services’ ProviderInstaller][provider_installer]. This will increase security for users\nand increase connectivity with web servers.\n\n### Certificate Pinning ([.kt][CertificatePinningKotlin], [.java][CertificatePinningJava])\n\nBy default, OkHttp trusts the certificate authorities of the host platform. This strategy maximizes connectivity, but it is subject to certificate authority attacks such as the [2011 DigiNotar attack](https://www.computerworld.com/article/2510951/cybercrime-hacking/hackers-spied-on-300-000-iranians-using-fake-google-certificate.html). It also assumes your HTTPS servers’ certificates are signed by a certificate authority.\n\nUse [CertificatePinner](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-certificate-pinner/) to restrict which certificates and certificate authorities are trusted. Certificate pinning increases security, but limits your server team’s abilities to update their TLS certificates. **Do not use certificate pinning without the blessing of your server’s TLS administrator!**\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient.Builder()\n          .certificatePinner(\n              CertificatePinner.Builder()\n                  .add(\"publicobject.com\", \"sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=\")\n                  .build())\n          .build()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"https://publicobject.com/robots.txt\")\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          for (certificate in response.handshake!!.peerCertificates) {\n            println(CertificatePinner.pin(certificate))\n          }\n        }\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client = new OkHttpClient.Builder()\n          .certificatePinner(\n              new CertificatePinner.Builder()\n                  .add(\"publicobject.com\", \"sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=\")\n                  .build())\n          .build();\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"https://publicobject.com/robots.txt\")\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          for (Certificate certificate : response.handshake().peerCertificates()) {\n            System.out.println(CertificatePinner.pin(certificate));\n          }\n        }\n      }\n    ```\n\n### Customizing Trusted Certificates ([.kt][CustomTrustKotlin], [.java][CustomTrustJava])\n\nThe full code sample shows how to replace the host platform’s certificate authorities with your own set. As above, **do not use custom certificates without the blessing of your server’s TLS administrator!**\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client: OkHttpClient\n\n      init {\n        val trustManager = trustManagerForCertificates(trustedCertificatesInputStream())\n        val sslContext = SSLContext.getInstance(\"TLS\")\n        sslContext.init(null, arrayOf<TrustManager>(trustManager), null)\n        val sslSocketFactory = sslContext.socketFactory\n\n        client = OkHttpClient.Builder()\n            .sslSocketFactory(sslSocketFactory, trustManager)\n            .build()\n      }\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"https://publicobject.com/helloworld.txt\")\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          for ((name, value) in response.headers) {\n            println(\"$name: $value\")\n          }\n\n          println(response.body!!.string())\n        }\n      }\n\n      /**\n       * Returns an input stream containing one or more certificate PEM files. This implementation just\n       * embeds the PEM files in Java strings; most applications will instead read this from a resource\n       * file that gets bundled with the application.\n       */\n      private fun trustedCertificatesInputStream(): InputStream {\n        ... // Full source omitted. See sample.\n      }\n\n      private fun trustManagerForCertificates(inputStream: InputStream): X509TrustManager {\n        ... // Full source omitted. See sample.\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client;\n\n      public CustomTrust() {\n        X509TrustManager trustManager;\n        SSLSocketFactory sslSocketFactory;\n        try {\n          trustManager = trustManagerForCertificates(trustedCertificatesInputStream());\n          SSLContext sslContext = SSLContext.getInstance(\"TLS\");\n          sslContext.init(null, new TrustManager[] { trustManager }, null);\n          sslSocketFactory = sslContext.getSocketFactory();\n        } catch (GeneralSecurityException e) {\n          throw new RuntimeException(e);\n        }\n\n        client = new OkHttpClient.Builder()\n            .sslSocketFactory(sslSocketFactory, trustManager)\n            .build();\n      }\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"https://publicobject.com/helloworld.txt\")\n            .build();\n\n        Response response = client.newCall(request).execute();\n        System.out.println(response.body().string());\n      }\n\n      private InputStream trustedCertificatesInputStream() {\n        ... // Full source omitted. See sample.\n      }\n\n      public SSLContext sslContextForTrustedCertificates(InputStream in) {\n        ... // Full source omitted. See sample.\n      }\n    ```\n\n [CustomTrustJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java\n [CustomTrustKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/CustomTrust.kt\n [CertificatePinningJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CertificatePinning.java\n [CertificatePinningKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/CertificatePinning.kt\n [provider_installer]: https://developer.android.com/training/articles/security-gms-provider\n [qualys]: https://www.ssllabs.com/ssltest/\n"
  },
  {
    "path": "docs/features/interceptors.md",
    "content": "Interceptors\n============\n\nInterceptors are a powerful mechanism that can monitor, rewrite, and retry calls. Here's a simple interceptor that logs the outgoing request and the incoming response.\n\n```java\nclass LoggingInterceptor implements Interceptor {\n  @Override public Response intercept(Interceptor.Chain chain) throws IOException {\n    Request request = chain.request();\n\n    long t1 = System.nanoTime();\n    logger.info(String.format(\"Sending request %s on %s%n%s\",\n        request.url(), chain.connection(), request.headers()));\n\n    Response response = chain.proceed(request);\n\n    long t2 = System.nanoTime();\n    logger.info(String.format(\"Received response for %s in %.1fms%n%s\",\n        response.request().url(), (t2 - t1) / 1e6d, response.headers()));\n\n    return response;\n  }\n}\n```\n\nA call to `chain.proceed(request)` is a critical part of each interceptor’s implementation. This simple-looking method is where all the HTTP work happens, producing a response to satisfy the request. If `chain.proceed(request)` is being called more than once previous response bodies must be closed.\n\nInterceptors can be chained. Suppose you have both a compressing interceptor and a checksumming interceptor: you'll need to decide whether data is compressed and then checksummed, or checksummed and then compressed. OkHttp uses lists to track interceptors, and interceptors are called in order.\n\n![Interceptors Diagram](../assets/images/interceptors@2x.png)\n\n### Application Interceptors\n\nInterceptors are registered as either _application_ or _network_ interceptors. We'll use the `LoggingInterceptor` defined above to show the difference.\n\nRegister an _application_ interceptor by calling `addInterceptor()` on `OkHttpClient.Builder`:\n\n```java\nOkHttpClient client = new OkHttpClient.Builder()\n    .addInterceptor(new LoggingInterceptor())\n    .build();\n\nRequest request = new Request.Builder()\n    .url(\"http://www.publicobject.com/helloworld.txt\")\n    .header(\"User-Agent\", \"OkHttp Example\")\n    .build();\n\nResponse response = client.newCall(request).execute();\nresponse.body().close();\n```\n\nThe URL `http://www.publicobject.com/helloworld.txt` redirects to `https://publicobject.com/helloworld.txt`, and OkHttp follows this redirect automatically. Our application interceptor is called **once** and the response returned from `chain.proceed()` has the redirected response:\n\n```\nINFO: Sending request http://www.publicobject.com/helloworld.txt on null\nUser-Agent: OkHttp Example\n\nINFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms\nServer: nginx/1.4.6 (Ubuntu)\nContent-Type: text/plain\nContent-Length: 1759\nConnection: keep-alive\n```\n\nWe can see that we were redirected because `response.request().url()` is different from `request.url()`. The two log statements log two different URLs.\n\n### Network Interceptors\n\nRegistering a network interceptor is quite similar. Call `addNetworkInterceptor()` instead of `addInterceptor()`:\n\n```java\nOkHttpClient client = new OkHttpClient.Builder()\n    .addNetworkInterceptor(new LoggingInterceptor())\n    .build();\n\nRequest request = new Request.Builder()\n    .url(\"http://www.publicobject.com/helloworld.txt\")\n    .header(\"User-Agent\", \"OkHttp Example\")\n    .build();\n\nResponse response = client.newCall(request).execute();\nresponse.body().close();\n```\n\nWhen we run this code, the interceptor runs twice. Once for the initial request to `http://www.publicobject.com/helloworld.txt`, and another for the redirect to `https://publicobject.com/helloworld.txt`.\n\n```\nINFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=none protocol=http/1.1}\nUser-Agent: OkHttp Example\nHost: www.publicobject.com\nConnection: Keep-Alive\nAccept-Encoding: gzip\n\nINFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms\nServer: nginx/1.4.6 (Ubuntu)\nContent-Type: text/html\nContent-Length: 193\nConnection: keep-alive\nLocation: https://publicobject.com/helloworld.txt\n\nINFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1}\nUser-Agent: OkHttp Example\nHost: publicobject.com\nConnection: Keep-Alive\nAccept-Encoding: gzip\n\nINFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms\nServer: nginx/1.4.6 (Ubuntu)\nContent-Type: text/plain\nContent-Length: 1759\nConnection: keep-alive\n```\n\nThe network requests also contain more data, such as the `Accept-Encoding: gzip` header added by OkHttp to advertise support for response compression. The network interceptor's `Chain` has a non-null `Connection` that can be used to interrogate the IP address and TLS configuration that were used to connect to the webserver.\n\n### Choosing between application and network interceptors\n\nEach interceptor chain has relative merits.\n\n**Application interceptors**\n\n * Don't need to worry about intermediate responses like redirects and retries.\n * Are always invoked once, even if the HTTP response is served from the cache.\n * Observe the application's original intent. Unconcerned with OkHttp-injected headers like `If-None-Match`.\n * Permitted to short-circuit and not call `Chain.proceed()`.\n * Permitted to retry and make multiple calls to `Chain.proceed()`.\n * Can adjust Call timeouts using withConnectTimeout, withReadTimeout, withWriteTimeout.\n\n**Network Interceptors**\n\n * Able to operate on intermediate responses like redirects and retries.\n * Not invoked for cached responses that short-circuit the network.\n * Observe the data just as it will be transmitted over the network.\n * Access to the `Connection` that carries the request.\n\n### Rewriting Requests\n\nInterceptors can add, remove, or replace request headers. They can also transform the body of those requests that have one. For example, you can use an application interceptor to add request body compression if you're connecting to a webserver known to support it.\n\n```java\n/** This interceptor compresses the HTTP request body. Many webservers can't handle this! */\nfinal class GzipRequestInterceptor implements Interceptor {\n  @Override public Response intercept(Interceptor.Chain chain) throws IOException {\n    Request originalRequest = chain.request();\n    if (originalRequest.body() == null || originalRequest.header(\"Content-Encoding\") != null) {\n      return chain.proceed(originalRequest);\n    }\n\n    Request compressedRequest = originalRequest.newBuilder()\n        .header(\"Content-Encoding\", \"gzip\")\n        .method(originalRequest.method(), gzip(originalRequest.body()))\n        .build();\n    return chain.proceed(compressedRequest);\n  }\n\n  private RequestBody gzip(final RequestBody body) {\n    return new RequestBody() {\n      @Override public MediaType contentType() {\n        return body.contentType();\n      }\n\n      @Override public long contentLength() {\n        return -1; // We don't know the compressed length in advance!\n      }\n\n      @Override public void writeTo(BufferedSink sink) throws IOException {\n        BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));\n        body.writeTo(gzipSink);\n        gzipSink.close();\n      }\n    };\n  }\n}\n```\n\n### Rewriting Responses\n\nSymmetrically, interceptors can rewrite response headers and transform the response body. This is generally more dangerous than rewriting request headers because it may violate the webserver's expectations!\n\nIf you're in a tricky situation and prepared to deal with the consequences, rewriting response headers is a powerful way to work around problems. For example, you can fix a server's misconfigured `Cache-Control` response header to enable better response caching:\n\n```java\n/** Dangerous interceptor that rewrites the server's cache-control header. */\nprivate static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {\n  @Override public Response intercept(Interceptor.Chain chain) throws IOException {\n    Response originalResponse = chain.proceed(chain.request());\n    return originalResponse.newBuilder()\n        .header(\"Cache-Control\", \"max-age=60\")\n        .build();\n  }\n};\n```\n\nTypically this approach works best when it complements a corresponding fix on the webserver!\n"
  },
  {
    "path": "docs/features/r8_proguard.md",
    "content": "\nR8 / ProGuard\n=============\n\nIf you use OkHttp as a dependency in an Android project which uses R8 as a default compiler you\ndon't have to do anything. The specific rules are [already bundled][okhttp3_pro] into the JAR which can be\ninterpreted by R8 automatically.\n\nIf you, however, don't use R8 you have to apply the rules from [this file][okhttp3_pro]. You might\nalso need rules from [Okio][okio] which is a dependency of this library.\n\n [okhttp3_pro]: https://raw.githubusercontent.com/square/okhttp/master/okhttp/okhttp3.pro\n [okio]: https://square.github.io/okio/\n"
  },
  {
    "path": "docs/recipes.md",
    "content": "---\ntitle: Recipes\ndescription: A collection of common/useful code examples for Kotlin and Java\n---\n\n\n# Recipes\n\nWe've written some recipes that demonstrate how to solve common problems with OkHttp. Read through them to learn about how everything works together. Cut-and-paste these examples freely; that's what they're for.\n\n### Synchronous Get ([.kt][SynchronousGetKotlin], [.java][SynchronousGetJava])\n\nDownload a file, print its headers, and print its response body as a string.\n\nThe `string()` method on response body is convenient and efficient for small documents. But if the response body is large (greater than 1 MiB), avoid `string()` because it will load the entire document into memory. In that case, prefer to process the body as a stream.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"https://publicobject.com/helloworld.txt\")\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          for ((name, value) in response.headers) {\n            println(\"$name: $value\")\n          }\n\n          println(response.body!!.string())\n        }\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"https://publicobject.com/helloworld.txt\")\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          Headers responseHeaders = response.headers();\n          for (int i = 0; i < responseHeaders.size(); i++) {\n            System.out.println(responseHeaders.name(i) + \": \" + responseHeaders.value(i));\n          }\n\n          System.out.println(response.body().string());\n        }\n      }\n    ```\n\n### Asynchronous Get ([.kt][AsynchronousGetKotlin], [.java][AsynchronousGetJava])\n\nDownload a file on a worker thread, and get called back when the response is readable. The callback is made after the response headers are ready. Reading the response body may still block. OkHttp doesn't currently offer asynchronous APIs to receive a response body in parts.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"http://publicobject.com/helloworld.txt\")\n            .build()\n\n        client.newCall(request).enqueue(object : Callback {\n          override fun onFailure(call: Call, e: IOException) {\n            e.printStackTrace()\n          }\n\n          override fun onResponse(call: Call, response: Response) {\n            response.use {\n              if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n              for ((name, value) in response.headers) {\n                println(\"$name: $value\")\n              }\n\n              println(response.body!!.string())\n            }\n          }\n        })\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"http://publicobject.com/helloworld.txt\")\n            .build();\n\n        client.newCall(request).enqueue(new Callback() {\n          @Override public void onFailure(Call call, IOException e) {\n            e.printStackTrace();\n          }\n\n          @Override public void onResponse(Call call, Response response) throws IOException {\n            try (ResponseBody responseBody = response.body()) {\n              if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n              Headers responseHeaders = response.headers();\n              for (int i = 0, size = responseHeaders.size(); i < size; i++) {\n                System.out.println(responseHeaders.name(i) + \": \" + responseHeaders.value(i));\n              }\n\n              System.out.println(responseBody.string());\n            }\n          }\n        });\n      }\n    ```\n\n### Accessing Headers ([.kt][AccessHeadersKotlin], [.java][AccessHeadersJava])\n\nTypically HTTP headers work like a `Map<String, String>`: each field has one value or none. But some headers permit multiple values, like Guava's [Multimap](https://guava.dev/releases/23.0/api/docs/com/google/common/collect/Multimap.html). For example, it's legal and common for an HTTP response to supply multiple `Vary` headers. OkHttp's APIs attempt to make both cases comfortable.\n\nWhen writing request headers, use `header(name, value)` to set the only occurrence of `name` to `value`. If there are existing values, they will be removed before the new value is added. Use `addHeader(name, value)` to add a header without removing the headers already present.\n\nWhen reading response a header, use `header(name)` to return the _last_ occurrence of the named value. Usually this is also the only occurrence! If no value is present, `header(name)` will return null. To read all of a field's values as a list, use `headers(name)`.\n\nTo visit all headers, use the `Headers` class which supports access by index.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"https://api.github.com/repos/square/okhttp/issues\")\n            .header(\"User-Agent\", \"OkHttp Headers.java\")\n            .addHeader(\"Accept\", \"application/json; q=0.5\")\n            .addHeader(\"Accept\", \"application/vnd.github.v3+json\")\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          println(\"Server: ${response.header(\"Server\")}\")\n          println(\"Date: ${response.header(\"Date\")}\")\n          println(\"Vary: ${response.headers(\"Vary\")}\")\n        }\n      }\n    ```\n\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"https://api.github.com/repos/square/okhttp/issues\")\n            .header(\"User-Agent\", \"OkHttp Headers.java\")\n            .addHeader(\"Accept\", \"application/json; q=0.5\")\n            .addHeader(\"Accept\", \"application/vnd.github.v3+json\")\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          System.out.println(\"Server: \" + response.header(\"Server\"));\n          System.out.println(\"Date: \" + response.header(\"Date\"));\n          System.out.println(\"Vary: \" + response.headers(\"Vary\"));\n        }\n      }\n    ```\n\n### Posting a String ([.kt][PostStringKotlin], [.java][PostStringJava])\n\nUse an HTTP POST to send a request body to a service. This example posts a markdown document to a web service that renders markdown as HTML. Because the entire request body is in memory simultaneously, avoid posting large (greater than 1 MiB) documents using this API.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        val postBody = \"\"\"\n            |Releases\n            |--------\n            |\n            | * _1.0_ May 6, 2013\n            | * _1.1_ June 15, 2013\n            | * _1.2_ August 11, 2013\n            |\"\"\".trimMargin()\n\n        val request = Request.Builder()\n            .url(\"https://api.github.com/markdown/raw\")\n            .post(postBody.toRequestBody(MEDIA_TYPE_MARKDOWN))\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          println(response.body!!.string())\n        }\n      }\n\n      companion object {\n        val MEDIA_TYPE_MARKDOWN = \"text/x-markdown; charset=utf-8\".toMediaType()\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      public static final MediaType MEDIA_TYPE_MARKDOWN\n          = MediaType.parse(\"text/x-markdown; charset=utf-8\");\n\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        String postBody = \"\"\n            + \"Releases\\n\"\n            + \"--------\\n\"\n            + \"\\n\"\n            + \" * _1.0_ May 6, 2013\\n\"\n            + \" * _1.1_ June 15, 2013\\n\"\n            + \" * _1.2_ August 11, 2013\\n\";\n\n        Request request = new Request.Builder()\n            .url(\"https://api.github.com/markdown/raw\")\n            .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          System.out.println(response.body().string());\n        }\n      }\n    ```\n\n### Post Streaming ([.kt][PostStreamingKotlin], [.java][PostStreamingJava])\n\nHere we `POST` a request body as a stream. The content of this request body is being generated as it's being written. This example streams directly into the [Okio](https://github.com/square/okio) buffered sink. Your programs may prefer an `OutputStream`, which you can get from `BufferedSink.outputStream()`.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        val requestBody = object : RequestBody() {\n          override fun contentType() = MEDIA_TYPE_MARKDOWN\n\n          override fun writeTo(sink: BufferedSink) {\n            sink.writeUtf8(\"Numbers\\n\")\n            sink.writeUtf8(\"-------\\n\")\n            for (i in 2..997) {\n              sink.writeUtf8(String.format(\" * $i = ${factor(i)}\\n\"))\n            }\n          }\n\n          private fun factor(n: Int): String {\n            for (i in 2 until n) {\n              val x = n / i\n              if (x * i == n) return \"${factor(x)} × $i\"\n            }\n            return n.toString()\n          }\n        }\n\n        val request = Request.Builder()\n            .url(\"https://api.github.com/markdown/raw\")\n            .post(requestBody)\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          println(response.body!!.string())\n        }\n      }\n\n      companion object {\n        val MEDIA_TYPE_MARKDOWN = \"text/x-markdown; charset=utf-8\".toMediaType()\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      public static final MediaType MEDIA_TYPE_MARKDOWN\n          = MediaType.parse(\"text/x-markdown; charset=utf-8\");\n\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        RequestBody requestBody = new RequestBody() {\n          @Override public MediaType contentType() {\n            return MEDIA_TYPE_MARKDOWN;\n          }\n\n          @Override public void writeTo(BufferedSink sink) throws IOException {\n            sink.writeUtf8(\"Numbers\\n\");\n            sink.writeUtf8(\"-------\\n\");\n            for (int i = 2; i <= 997; i++) {\n              sink.writeUtf8(String.format(\" * %s = %s\\n\", i, factor(i)));\n            }\n          }\n\n          private String factor(int n) {\n            for (int i = 2; i < n; i++) {\n              int x = n / i;\n              if (x * i == n) return factor(x) + \" × \" + i;\n            }\n            return Integer.toString(n);\n          }\n        };\n\n        Request request = new Request.Builder()\n            .url(\"https://api.github.com/markdown/raw\")\n            .post(requestBody)\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          System.out.println(response.body().string());\n        }\n      }\n    ```\n\n### Posting a File ([.kt][PostFileKotlin], [.java][PostFileJava])\n\nIt's easy to use a file as a request body.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        val file = File(\"README.md\")\n\n        val request = Request.Builder()\n            .url(\"https://api.github.com/markdown/raw\")\n            .post(file.asRequestBody(MEDIA_TYPE_MARKDOWN))\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          println(response.body!!.string())\n        }\n      }\n\n      companion object {\n        val MEDIA_TYPE_MARKDOWN = \"text/x-markdown; charset=utf-8\".toMediaType()\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      public static final MediaType MEDIA_TYPE_MARKDOWN\n          = MediaType.parse(\"text/x-markdown; charset=utf-8\");\n\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        File file = new File(\"README.md\");\n\n        Request request = new Request.Builder()\n            .url(\"https://api.github.com/markdown/raw\")\n            .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          System.out.println(response.body().string());\n        }\n      }\n    ```\n\n### Posting form parameters ([.kt][PostFormKotlin], [.java][PostFormJava])\n\nUse `FormBody.Builder` to build a request body that works like an HTML `<form>` tag. Names and values will be encoded using an HTML-compatible form URL encoding.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        val formBody = FormBody.Builder()\n            .add(\"search\", \"Jurassic Park\")\n            .build()\n        val request = Request.Builder()\n            .url(\"https://en.wikipedia.org/w/index.php\")\n            .post(formBody)\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          println(response.body!!.string())\n        }\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        RequestBody formBody = new FormBody.Builder()\n            .add(\"search\", \"Jurassic Park\")\n            .build();\n        Request request = new Request.Builder()\n            .url(\"https://en.wikipedia.org/w/index.php\")\n            .post(formBody)\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          System.out.println(response.body().string());\n        }\n      }\n    ```\n\n### Posting a multipart request ([.kt][PostMultipartKotlin], [.java][PostMultipartJava])\n\n`MultipartBody.Builder` can build sophisticated request bodies compatible with HTML file upload forms. Each part of a multipart request body is itself a request body, and can define its own headers. If present, these headers should describe the part body, such as its `Content-Disposition`. The `Content-Length` and `Content-Type` headers are added automatically if they're available.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image\n        val requestBody = MultipartBody.Builder()\n            .setType(MultipartBody.FORM)\n            .addFormDataPart(\"title\", \"Square Logo\")\n            .addFormDataPart(\"image\", \"logo-square.png\",\n                File(\"docs/images/logo-square.png\").asRequestBody(MEDIA_TYPE_PNG))\n            .build()\n\n        val request = Request.Builder()\n            .header(\"Authorization\", \"Client-ID $IMGUR_CLIENT_ID\")\n            .url(\"https://api.imgur.com/3/image\")\n            .post(requestBody)\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          println(response.body!!.string())\n        }\n      }\n\n      companion object {\n        /**\n         * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running\n         * these examples, please request your own client ID! https://api.imgur.com/oauth2\n         */\n        private val IMGUR_CLIENT_ID = \"9199fdef135c122\"\n        private val MEDIA_TYPE_PNG = \"image/png\".toMediaType()\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      /**\n       * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running\n       * these examples, please request your own client ID! https://api.imgur.com/oauth2\n       */\n      private static final String IMGUR_CLIENT_ID = \"...\";\n      private static final MediaType MEDIA_TYPE_PNG = MediaType.parse(\"image/png\");\n\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image\n        RequestBody requestBody = new MultipartBody.Builder()\n            .setType(MultipartBody.FORM)\n            .addFormDataPart(\"title\", \"Square Logo\")\n            .addFormDataPart(\"image\", \"logo-square.png\",\n                RequestBody.create(MEDIA_TYPE_PNG, new File(\"website/static/logo-square.png\")))\n            .build();\n\n        Request request = new Request.Builder()\n            .header(\"Authorization\", \"Client-ID \" + IMGUR_CLIENT_ID)\n            .url(\"https://api.imgur.com/3/image\")\n            .post(requestBody)\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          System.out.println(response.body().string());\n        }\n      }\n    ```\n\n### Parse a JSON Response With Moshi ([.kt][ParseResponseWithMoshiKotlin], [.java][ParseResponseWithMoshiJava])\n\n[Moshi](https://github.com/square/moshi) is a handy API for converting between JSON and Java objects. Here we're using it to decode a JSON response from a GitHub API.\n\nNote that `ResponseBody.charStream()` uses the `Content-Type` response header to select which charset to use when decoding the response body. It defaults to `UTF-8` if no charset is specified.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n      private val moshi = Moshi.Builder().build()\n      private val gistJsonAdapter = moshi.adapter(Gist::class.java)\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"https://api.github.com/gists/c2a7c39532239ff261be\")\n            .build()\n        client.newCall(request).execute().use { response ->\n          if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n          val gist = gistJsonAdapter.fromJson(response.body!!.source())\n\n          for ((key, value) in gist!!.files!!) {\n            println(key)\n            println(value.content)\n          }\n        }\n      }\n\n      @JsonClass(generateAdapter = true)\n      data class Gist(var files: Map<String, GistFile>?)\n\n      @JsonClass(generateAdapter = true)\n      data class GistFile(var content: String?)\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client = new OkHttpClient();\n      private final Moshi moshi = new Moshi.Builder().build();\n      private final JsonAdapter<Gist> gistJsonAdapter = moshi.adapter(Gist.class);\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"https://api.github.com/gists/c2a7c39532239ff261be\")\n            .build();\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          Gist gist = gistJsonAdapter.fromJson(response.body().source());\n\n          for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {\n            System.out.println(entry.getKey());\n            System.out.println(entry.getValue().content);\n          }\n        }\n      }\n\n      static class Gist {\n        Map<String, GistFile> files;\n      }\n\n      static class GistFile {\n        String content;\n      }\n    ```\n\n### Response Caching ([.kt][CacheResponseKotlin], [.java][CacheResponseJava])\n\nTo cache responses, you'll need a cache directory that you can read and write to, and a limit on the cache's size. The cache directory should be private, and untrusted applications should not be able to read its contents!\n\nIt is an error to have multiple caches accessing the same cache directory simultaneously. Most applications should call `new OkHttpClient()` exactly once, configure it with their cache, and use that same instance everywhere. Otherwise the two cache instances will stomp on each other, corrupt the response cache, and possibly crash your program.\n\nResponse caching uses HTTP headers for all configuration. You can add request headers like `Cache-Control: max-stale=3600` and OkHttp's cache will honor them. Your webserver configures how long responses are cached with its own response headers, like `Cache-Control: max-age=9600`. There are cache headers to force a cached response, force a network response, or force the network response to be validated with a conditional GET.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client: OkHttpClient = OkHttpClient.Builder()\n          .cache(Cache(\n              directory = cacheDirectory,\n              maxSize = 10L * 1024L * 1024L // 10 MiB\n          ))\n          .build()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"http://publicobject.com/helloworld.txt\")\n            .build()\n\n        val response1Body = client.newCall(request).execute().use {\n          if (!it.isSuccessful) throw IOException(\"Unexpected code $it\")\n\n          println(\"Response 1 response:          $it\")\n          println(\"Response 1 cache response:    ${it.cacheResponse}\")\n          println(\"Response 1 network response:  ${it.networkResponse}\")\n          return@use it.body!!.string()\n        }\n\n        val response2Body = client.newCall(request).execute().use {\n          if (!it.isSuccessful) throw IOException(\"Unexpected code $it\")\n\n          println(\"Response 2 response:          $it\")\n          println(\"Response 2 cache response:    ${it.cacheResponse}\")\n          println(\"Response 2 network response:  ${it.networkResponse}\")\n          return@use it.body!!.string()\n        }\n\n        println(\"Response 2 equals Response 1? \" + (response1Body == response2Body))\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client;\n\n      public CacheResponse(File cacheDirectory) throws Exception {\n        int cacheSize = 10 * 1024 * 1024; // 10 MiB\n        Cache cache = new Cache(cacheDirectory, cacheSize);\n\n        client = new OkHttpClient.Builder()\n            .cache(cache)\n            .build();\n      }\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"http://publicobject.com/helloworld.txt\")\n            .build();\n\n        String response1Body;\n        try (Response response1 = client.newCall(request).execute()) {\n          if (!response1.isSuccessful()) throw new IOException(\"Unexpected code \" + response1);\n\n          response1Body = response1.body().string();\n          System.out.println(\"Response 1 response:          \" + response1);\n          System.out.println(\"Response 1 cache response:    \" + response1.cacheResponse());\n          System.out.println(\"Response 1 network response:  \" + response1.networkResponse());\n        }\n\n        String response2Body;\n        try (Response response2 = client.newCall(request).execute()) {\n          if (!response2.isSuccessful()) throw new IOException(\"Unexpected code \" + response2);\n\n          response2Body = response2.body().string();\n          System.out.println(\"Response 2 response:          \" + response2);\n          System.out.println(\"Response 2 cache response:    \" + response2.cacheResponse());\n          System.out.println(\"Response 2 network response:  \" + response2.networkResponse());\n        }\n\n        System.out.println(\"Response 2 equals Response 1? \" + response1Body.equals(response2Body));\n      }\n    ```\n\nTo prevent a response from using the cache, use [`CacheControl.FORCE_NETWORK`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-cache-control/-companion/-f-o-r-c-e_-n-e-t-w-o-r-k). To prevent it from using the network, use [`CacheControl.FORCE_CACHE`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-cache-control/-companion/-f-o-r-c-e_-c-a-c-h-e). Be warned: if you use `FORCE_CACHE` and the response requires the network, OkHttp will return a `504 Unsatisfiable Request` response.\n\n\n### Canceling a Call ([.kt][CancelCallKotlin], [.java][CancelCallJava])\n\nUse `Call.cancel()` to stop an ongoing call immediately. If a thread is currently writing a request or reading a response, it will receive an `IOException`. Use this to conserve the network when a call is no longer necessary; for example when your user navigates away from an application. Both synchronous and asynchronous calls can be canceled.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val executor = Executors.newScheduledThreadPool(1)\n      private val client = OkHttpClient()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"http://httpbin.org/delay/2\") // This URL is served with a 2 second delay.\n            .build()\n\n        val startNanos = System.nanoTime()\n        val call = client.newCall(request)\n\n        // Schedule a job to cancel the call in 1 second.\n        executor.schedule({\n          System.out.printf(\"%.2f Canceling call.%n\", (System.nanoTime() - startNanos) / 1e9f)\n          call.cancel()\n          System.out.printf(\"%.2f Canceled call.%n\", (System.nanoTime() - startNanos) / 1e9f)\n        }, 1, TimeUnit.SECONDS)\n\n        System.out.printf(\"%.2f Executing call.%n\", (System.nanoTime() - startNanos) / 1e9f)\n        try {\n          call.execute().use { response ->\n            System.out.printf(\"%.2f Call was expected to fail, but completed: %s%n\",\n                (System.nanoTime() - startNanos) / 1e9f, response)\n          }\n        } catch (e: IOException) {\n          System.out.printf(\"%.2f Call failed as expected: %s%n\",\n              (System.nanoTime() - startNanos) / 1e9f, e)\n        }\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"http://httpbin.org/delay/2\") // This URL is served with a 2 second delay.\n            .build();\n\n        final long startNanos = System.nanoTime();\n        final Call call = client.newCall(request);\n\n        // Schedule a job to cancel the call in 1 second.\n        executor.schedule(new Runnable() {\n          @Override public void run() {\n            System.out.printf(\"%.2f Canceling call.%n\", (System.nanoTime() - startNanos) / 1e9f);\n            call.cancel();\n            System.out.printf(\"%.2f Canceled call.%n\", (System.nanoTime() - startNanos) / 1e9f);\n          }\n        }, 1, TimeUnit.SECONDS);\n\n        System.out.printf(\"%.2f Executing call.%n\", (System.nanoTime() - startNanos) / 1e9f);\n        try (Response response = call.execute()) {\n          System.out.printf(\"%.2f Call was expected to fail, but completed: %s%n\",\n              (System.nanoTime() - startNanos) / 1e9f, response);\n        } catch (IOException e) {\n          System.out.printf(\"%.2f Call failed as expected: %s%n\",\n              (System.nanoTime() - startNanos) / 1e9f, e);\n        }\n      }\n    ```\n\n### Timeouts ([.kt][ConfigureTimeoutsKotlin], [.java][ConfigureTimeoutsJava])\n\nUse timeouts to fail a call when its peer is unreachable. Network partitions can be due to client connectivity problems, server availability problems, or anything between. OkHttp supports connect, write, read, and full call timeouts.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client: OkHttpClient = OkHttpClient.Builder()\n          .connectTimeout(5, TimeUnit.SECONDS)\n          .writeTimeout(5, TimeUnit.SECONDS)\n          .readTimeout(5, TimeUnit.SECONDS)\n          .callTimeout(10, TimeUnit.SECONDS)\n          .build()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"http://httpbin.org/delay/2\") // This URL is served with a 2 second delay.\n            .build()\n\n        client.newCall(request).execute().use { response ->\n          println(\"Response completed: $response\")\n        }\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client;\n\n      public ConfigureTimeouts() throws Exception {\n        client = new OkHttpClient.Builder()\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(10, TimeUnit.SECONDS)\n            .readTimeout(30, TimeUnit.SECONDS)\n            .build();\n      }\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"http://httpbin.org/delay/2\") // This URL is served with a 2 second delay.\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          System.out.println(\"Response completed: \" + response);\n        }\n      }\n    ```\n\n### Per-call Configuration ([.kt][PerCallSettingsKotlin], [.java][PerCallSettingsJava])\n\nAll the HTTP client configuration lives in `OkHttpClient` including proxy settings, timeouts, and caches. When you need to change the configuration of a single call, call `OkHttpClient.newBuilder()`. This returns a builder that shares the same connection pool, dispatcher, and configuration with the original client. In the example below, we make one request with a 500 ms timeout and another with a 3000 ms timeout.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"http://httpbin.org/delay/1\") // This URL is served with a 1 second delay.\n            .build()\n\n        // Copy to customize OkHttp for this request.\n        val client1 = client.newBuilder()\n            .readTimeout(500, TimeUnit.MILLISECONDS)\n            .build()\n        try {\n          client1.newCall(request).execute().use { response ->\n            println(\"Response 1 succeeded: $response\")\n          }\n        } catch (e: IOException) {\n          println(\"Response 1 failed: $e\")\n        }\n\n        // Copy to customize OkHttp for this request.\n        val client2 = client.newBuilder()\n            .readTimeout(3000, TimeUnit.MILLISECONDS)\n            .build()\n        try {\n          client2.newCall(request).execute().use { response ->\n            println(\"Response 2 succeeded: $response\")\n          }\n        } catch (e: IOException) {\n          println(\"Response 2 failed: $e\")\n        }\n      }\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client = new OkHttpClient();\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"http://httpbin.org/delay/1\") // This URL is served with a 1 second delay.\n            .build();\n\n        // Copy to customize OkHttp for this request.\n        OkHttpClient client1 = client.newBuilder()\n            .readTimeout(500, TimeUnit.MILLISECONDS)\n            .build();\n        try (Response response = client1.newCall(request).execute()) {\n          System.out.println(\"Response 1 succeeded: \" + response);\n        } catch (IOException e) {\n          System.out.println(\"Response 1 failed: \" + e);\n        }\n\n        // Copy to customize OkHttp for this request.\n        OkHttpClient client2 = client.newBuilder()\n            .readTimeout(3000, TimeUnit.MILLISECONDS)\n            .build();\n        try (Response response = client2.newCall(request).execute()) {\n          System.out.println(\"Response 2 succeeded: \" + response);\n        } catch (IOException e) {\n          System.out.println(\"Response 2 failed: \" + e);\n        }\n      }\n    ```\n\n### Handling authentication ([.kt][AuthenticateKotlin], [.java][AuthenticateJava])\n\nOkHttp can automatically retry unauthenticated requests. When a response is `401 Not Authorized`, an `Authenticator` is asked to supply credentials. Implementations should build a new request that includes the missing credentials. If no credentials are available, return null to skip the retry.\n\nUse `Response.challenges()` to get the schemes and realms of any authentication challenges. When fulfilling a `Basic` challenge, use `Credentials.basic(username, password)` to encode the request header.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      private val client = OkHttpClient.Builder()\n          .authenticator(object : Authenticator {\n            @Throws(IOException::class)\n            override fun authenticate(route: Route?, response: Response): Request? {\n              if (response.request.header(\"Authorization\") != null) {\n                return null // Give up, we've already attempted to authenticate.\n              }\n\n              println(\"Authenticating for response: $response\")\n              println(\"Challenges: ${response.challenges()}\")\n              val credential = Credentials.basic(\"jesse\", \"password1\")\n              return response.request.newBuilder()\n                  .header(\"Authorization\", credential)\n                  .build()\n            }\n          })\n          .build()\n\n      fun run() {\n        val request = Request.Builder()\n            .url(\"http://publicobject.com/secrets/hellosecret.txt\")\n            .build()\n      }\n    ```\n\n    To avoid making many retries when authentication isn't working, you can return null to give up. For example, you may want to skip the retry when these exact credentials have already been attempted:\n\n    ```kotlin\n    if (credential == response.request.header(\"Authorization\")) {\n      return null // If we already failed with these credentials, don't retry.\n     }\n    ```\n\n    You may also skip the retry when you’ve hit an application-defined attempt limit:\n\n    ```kotlin\n    if (response.responseCount >= 3) {\n      return null // If we've failed 3 times, give up.\n    }\n    ```\n\n    This above code relies on this `responseCount` extension val:\n\n    ```kotlin\n    val Response.responseCount: Int\n      get() = generateSequence(this) { it.priorResponse }.count()\n    ```\n=== \":material-language-java: Java\"\n    ```java\n      private final OkHttpClient client;\n\n      public Authenticate() {\n        client = new OkHttpClient.Builder()\n            .authenticator(new Authenticator() {\n              @Override public Request authenticate(Route route, Response response) throws IOException {\n                if (response.request().header(\"Authorization\") != null) {\n                  return null; // Give up, we've already attempted to authenticate.\n                }\n\n                System.out.println(\"Authenticating for response: \" + response);\n                System.out.println(\"Challenges: \" + response.challenges());\n                String credential = Credentials.basic(\"jesse\", \"password1\");\n                return response.request().newBuilder()\n                    .header(\"Authorization\", credential)\n                    .build();\n              }\n            })\n            .build();\n      }\n\n      public void run() throws Exception {\n        Request request = new Request.Builder()\n            .url(\"http://publicobject.com/secrets/hellosecret.txt\")\n            .build();\n\n        try (Response response = client.newCall(request).execute()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          System.out.println(response.body().string());\n        }\n      }\n    ```\n\n    To avoid making many retries when authentication isn't working, you can return null to give up. For example, you may want to skip the retry when these exact credentials have already been attempted:\n\n    ```java\n      if (credential.equals(response.request().header(\"Authorization\"))) {\n        return null; // If we already failed with these credentials, don't retry.\n       }\n    ```\n\n    You may also skip the retry when you’ve hit an application-defined attempt limit:\n\n    ```java\n      if (responseCount(response) >= 3) {\n        return null; // If we've failed 3 times, give up.\n      }\n    ```\n\n    This above code relies on this `responseCount()` method:\n\n    ```java\n      private int responseCount(Response response) {\n        int result = 1;\n        while ((response = response.priorResponse()) != null) {\n          result++;\n        }\n        return result;\n      }\n    ```\n\n### Upload Progress ([.kt][UploadProgressKotlin], [.java][UploadProgressJava])\n\nUpload a file to a server (for example, Imgur) and report progress as the request body is being written. You can implement a ProgressListener to receive updates and wrap the original request body with ProgressRequestBody. This allows you to monitor how many bytes have been uploaded and calculate the percentage of completion.\n\n=== \":material-language-kotlin: Kotlin\"\n    ```kotlin\n      class UploadProgress {\n\n        companion object {\n          private const val IMGUR_CLIENT_ID = \"9199fdef135c122\"\n          private val MEDIA_TYPE_PNG = \"image/png\".toMediaType()\n\n          @JvmStatic\n          fun main(args: Array<String>) {\n            UploadProgress().run()\n          }\n        }\n\n        private val client = OkHttpClient()\n\n        @Throws(Exception::class)\n        fun run() {\n          val progressListener = object : ProgressListener {\n            private var firstUpdate = true\n\n            override fun update(bytesWritten: Long, contentLength: Long, done: Boolean) {\n              if (done) {\n                println(\"completed\")\n              } else {\n                if (firstUpdate) {\n                  firstUpdate = false\n                  if (contentLength == -1L) {\n                    println(\"content-length: unknown\")\n                  } else {\n                    println(\"content-length: $contentLength\")\n                  }\n                }\n                println(bytesWritten)\n                if (contentLength != -1L) {\n                  println(\"${100 * bytesWritten / contentLength}% done\")\n                }\n              }\n            }\n          }\n\n          val file = File(\"docs/images/logo-square.png\")\n          val requestBody: RequestBody = file.asRequestBody(MEDIA_TYPE_PNG)\n\n          val request =\n            Request.Builder().header(\"Authorization\", \"Client-ID $IMGUR_CLIENT_ID\")\n              .url(\"https://api.imgur.com/3/image\")\n              .post(ProgressRequestBody(requestBody, progressListener)).build()\n\n          client.newCall(request).execute().use { response ->\n            if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n            println(response.body.string())\n          }\n        }\n\n        private class ProgressRequestBody(\n          private val delegate: RequestBody, private val progressListener: ProgressListener\n        ) : RequestBody() {\n\n          override fun contentType() = delegate.contentType()\n\n          @Throws(IOException::class)\n          override fun contentLength(): Long = delegate.contentLength()\n\n          @Throws(IOException::class)\n          override fun writeTo(sink: BufferedSink) {\n            val forwardingSink = object : ForwardingSink(sink) {\n              private var totalBytesWritten: Long = 0\n              private var completed = false\n\n              override fun write(source: Buffer, byteCount: Long) {\n                super.write(source, byteCount)\n                totalBytesWritten += byteCount\n                progressListener.update(totalBytesWritten, contentLength(), completed)\n              }\n\n              override fun close() {\n                super.close()\n                if (!completed) {\n                  completed = true\n                  progressListener.update(totalBytesWritten, contentLength(), completed)\n                }\n              }\n            }\n\n            val bufferedSink = forwardingSink.buffer()\n            delegate.writeTo(bufferedSink)\n            bufferedSink.flush()\n          }\n        }\n\n        fun interface ProgressListener {\n          fun update(bytesWritten: Long, contentLength: Long, done: Boolean)\n        }\n      }\n    ```\n\n=== \":material-language-java: Java\"\n    ```java\n      public final class UploadProgress {\n        private static final String IMGUR_CLIENT_ID = \"9199fdef135c122\";\n        private static final MediaType MEDIA_TYPE_PNG = MediaType.get(\"image/png\");\n\n        private final OkHttpClient client = new OkHttpClient();\n\n        public void run() throws Exception {\n          final ProgressListener progressListener = new ProgressListener() {\n            boolean firstUpdate = true;\n\n            @Override public void update(long bytesWritten, long contentLength, boolean done) {\n              if (done) {\n                System.out.println(\"completed\");\n              } else {\n                if (firstUpdate) {\n                  firstUpdate = false;\n                  if (contentLength == -1) {\n                    System.out.println(\"content-length: unknown\");\n                  } else {\n                    System.out.format(\"content-length: %d\\n\", contentLength);\n                  }\n                }\n                System.out.println(bytesWritten);\n                if (contentLength != -1) {\n                  System.out.format(\"%d%% done\\n\", (100 * bytesWritten) / contentLength);\n                }\n              }\n            }\n          };\n\n          RequestBody requestBody = RequestBody.create(\n              new File(\"docs/images/logo-square.png\"),\n              MEDIA_TYPE_PNG);\n\n          Request request = new Request.Builder()\n              .header(\"Authorization\", \"Client-ID \" + IMGUR_CLIENT_ID)\n              .url(\"https://api.imgur.com/3/image\")\n              .post(new ProgressRequestBody(requestBody, progressListener))\n              .build();\n\n          Response response = client.newCall(request).execute();\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          System.out.println(response.body().string());\n        }\n\n        public static void main(String... args) throws Exception {\n          new UploadProgress().run();\n        }\n\n        private static class ProgressRequestBody extends RequestBody {\n          private final ProgressListener progressListener;\n          private final RequestBody delegate;\n\n          public ProgressRequestBody(RequestBody delegate, ProgressListener progressListener) {\n            this.delegate = delegate;\n            this.progressListener = progressListener;\n          }\n\n          @Override public MediaType contentType() {\n            return delegate.contentType();\n          }\n\n          @Override public long contentLength() throws IOException {\n            return delegate.contentLength();\n          }\n\n          @Override public void writeTo(BufferedSink sink) throws IOException {\n            BufferedSink bufferedSink = Okio.buffer(sink(sink));\n            delegate.writeTo(bufferedSink);\n            bufferedSink.flush();\n          }\n\n          public Sink sink(Sink sink) {\n            return new ForwardingSink(sink) {\n              private long totalBytesWritten = 0L;\n              private boolean completed = false;\n\n              @Override public void write(Buffer source, long byteCount) throws IOException {\n                super.write(source, byteCount);\n                totalBytesWritten += byteCount;\n                progressListener.update(totalBytesWritten, contentLength(), completed);\n              }\n\n              @Override public void close() throws IOException {\n                super.close();\n                if (!completed) {\n                  completed = true;\n                  progressListener.update(totalBytesWritten, contentLength(), completed);\n                }\n              }\n            };\n          }\n        }\n\n        interface ProgressListener {\n          void update(long bytesWritten, long contentLength, boolean done);\n        }\n      }\n    ```\n\n [SynchronousGetJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/SynchronousGet.java\n [SynchronousGetKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/SynchronousGet.kt\n [AsynchronousGetJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/AsynchronousGet.java\n [AsynchronousGetKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/AsynchronousGet.kt\n [AccessHeadersJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/AccessHeaders.java\n [AccessHeadersKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/AccessHeaders.kt\n [PostStringJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PostString.java\n [PostStringKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/PostString.kt\n [PostStreamingJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PostStreaming.java\n [PostStreamingKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/PostStreaming.kt\n [PostFileJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PostFile.java\n [PostFileKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/PostFile.kt\n [PostFormJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PostForm.java\n [PostFormKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/PostForm.kt\n [PostMultipartJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PostMultipart.java\n [PostMultipartKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/PostMultipart.kt\n [ParseResponseWithMoshiJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/ParseResponseWithMoshi.java\n [ParseResponseWithMoshiKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/ParseResponseWithMoshi.kt\n [CacheResponseJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CacheResponse.java\n [CacheResponseKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/CacheResponse.kt\n [CancelCallJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CancelCall.java\n [CancelCallKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/CancelCall.kt\n [ConfigureTimeoutsJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/ConfigureTimeouts.java\n [ConfigureTimeoutsKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/ConfigureTimeouts.kt\n [PerCallSettingsJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PerCallSettings.java\n [PerCallSettingsKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/PerCallSettings.kt\n [AuthenticateJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Authenticate.java\n [AuthenticateKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/Authenticate.kt\n [UploadProgressJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/UploadProgress.java\n [UploadProgressKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/UploadProgress.kt\n"
  },
  {
    "path": "docs/releasing.md",
    "content": "Releasing\n=========\n\n1. Update `CHANGELOG.md`.\n\n2. Set versions:\n\n    ```\n    export RELEASE_VERSION=X.Y.Z\n    export NEXT_VERSION=X.Y.Z-SNAPSHOT\n    ```\n\n3. Update versions, tag the release, and prepare for the next release.\n\n    ```\n    sed -i \"\" \\\n      \"s/version = \\\".*\\\"/version = \\\"$RELEASE_VERSION\\\"/g\" \\\n      build.gradle.kts\n    sed -i \"\" \\\n      \"s/\\\"com.squareup.okhttp3:\\([^\\:]*\\):[^\\\"]*\\\"/\\\"com.squareup.okhttp3:\\1:$RELEASE_VERSION\\\"/g\" \\\n      `find . -name \"README.md\"`\n    sed -i \"\" \\\n      \"s/\\/com.squareup.okhttp3\\/\\([^\\:]*\\)\\/[^\\/]*\\//\\/com.squareup.okhttp3\\/\\1\\/$RELEASE_VERSION\\//g\" \\\n      `find . -name \"README.md\"`\n\n    git commit -am \"Prepare for release $RELEASE_VERSION.\"\n    git tag -a parent-$RELEASE_VERSION -m \"Version $RELEASE_VERSION\"\n    git push && git push --tags\n\n   sed -i \"\" \\\n      \"s/version = \\\".*\\\"/version = \\\"$NEXT_VERSION\\\"/g\" \\\n      build.gradle.kts\n    git commit -am \"Prepare next development version.\"\n    git push\n    ```\n\n4. Wait for [GitHub Actions][github_actions] to build and promote the release.\n\n[github_actions]: https://github.com/square/okhttp/actions\n"
  },
  {
    "path": "docs/security/security.md",
    "content": "Security\n========\n\n## Supported Versions\n\n| Version | Supported           | Notes                                        |\n| ------- | ------------------- | -------------------------------------------- |\n| 5.x     | ✅                  | APIs subject to change in alpha releases.    |\n| 4.x     | ✅                  | Android 5.0+ (API level 21+) and on Java 8+. |\n| 3.x     | ❌ Ended 2021-12-31 | Android 2.3+ (API level 9+) and Java 7+.     |\n\n\n## Reporting a Vulnerability\n\nSquare recognizes the important contributions the security research community\ncan make. We therefore encourage reporting security issues with the code\ncontained in this repository.\n\nIf you believe you have discovered a security vulnerability, please follow the\nguidelines at https://bugcrowd.com/squareopensource\n\n\n## Verifying Artifacts\n\nWe sign our artifacts using this [key][signing_key]:\n\n```\npub rsa4096/dbd744ace7ade6aa50dd591f66b50994442d2d40 2021-07-09T14:50:19Z\n\t Hash=a79b48fd6a1f31699c788b50c97d0b98\n\nuid Square Clippy <opensource@squareup.com>\nsig  sig  66b50994442d2d40 2021-07-09T14:50:19Z 2041-07-04T14:50:19Z ____________________ [selfsig]\n```\n\nThe best way to verify artifacts is [automatically with Gradle][gradle_verification].\n\n\n[gradle_verification]: https://docs.gradle.org/current/userguide/dependency_verification.html#sec:signature-verification\n[signing_key]: https://keyserver.ubuntu.com/pks/lookup?op=hget&search=a79b48fd6a1f31699c788b50c97d0b98\n"
  },
  {
    "path": "docs/security/security_providers.md",
    "content": "\nSecurity Providers\n==================\n\n## Provider Status\n\n| Provider         | HTTP/2  | TLSv1.3      | Powered By      | Notes                                                        |\n| :--------------- | :------ | :----------- | :-------------- | :----------------------------------------------------------- |\n| JVM default      | Java 9+ | Java 11+     | [OpenJDK]       |                                                              |\n| Android default  | ✅      | Android 10+  | [BoringSSL]     |                                                              |\n| [GraalVM]        | ✅      |              | [OpenJDK]       | Only actively tested with JDK 11, not with 8 target          |\n| [Bouncy Castle]  | ✅      |              | [Bouncy Castle] | [Tracking bug.][bug5698]                                     |\n| [Conscrypt]      | ✅      | ✅           | [BoringSSL]     | Activated if Conscrypt is first registered provider.         |\n| [OpenJSSE]       |         | ✅           | [OpenJDK]       | OpenJDK backport.                                            |\n| [Corretto]       | ✅      | ✅           | [OpenSSL]       | Amazon's high-performance provider. [Tracking bug.][bug5592] |\n\nAll providers support HTTP/1.1 and TLSv1.2.\n\n\n[BoringSSL]: https://boringssl.googlesource.com/boringssl/\n[Bouncy Castle]: https://www.bouncycastle.org/java.html\n[Conscrypt]: https://www.conscrypt.org/\n[Corretto]: https://github.com/corretto/amazon-corretto-crypto-provider\n[GraalVM]: https://www.graalvm.org/\n[OpenJDK]: https://openjdk.java.net/groups/security/\n[OpenJSSE]: https://github.com/openjsse/openjsse\n[OpenSSL]: https://www.openssl.org/\n[bug5592]: https://github.com/square/okhttp/issues/5592\n[bug5698]: https://github.com/square/okhttp/issues/5698\n"
  },
  {
    "path": "docs/security/tls_configuration_history.md",
    "content": "TLS Configuration History\n=========================\n\nOkHttp tracks the dynamic TLS ecosystem to balance connectivity and security. This page is a log of\nchanges we've made over time to OkHttp's default TLS options.\n\n[OkHttp 3.14][OkHttp314]\n------------------------\n\n_2019-03-14_\n\nRemove 2 TLSv1.3 cipher suites that are neither available on OkHttp’s host platforms nor enabled in releases of Chrome and Firefox.\n\n##### RESTRICTED_TLS cipher suites\n\n * TLS_AES_128_GCM_SHA256[¹][tlsv13_only]\n * TLS_AES_256_GCM_SHA384[¹][tlsv13_only]\n * TLS_CHACHA20_POLY1305_SHA256[¹][tlsv13_only]\n * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n * **REMOVED:** ~~TLS_AES_128_CCM_SHA256[¹][tlsv13_only]~~\n * **REMOVED:** ~~TLS_AES_128_CCM_8_SHA256[¹][tlsv13_only]~~\n\n##### MODERN_TLS / COMPATIBLE_TLS cipher suites\n\n * TLS_AES_128_GCM_SHA256[¹][tlsv13_only]\n * TLS_AES_256_GCM_SHA384[¹][tlsv13_only]\n * TLS_CHACHA20_POLY1305_SHA256[¹][tlsv13_only]\n * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_GCM_SHA256[²][http2_naughty]\n * TLS_RSA_WITH_AES_256_GCM_SHA384[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_3DES_EDE_CBC_SHA[²][http2_naughty]\n * **REMOVED:** ~~TLS_AES_128_CCM_SHA256[¹][tlsv13_only]~~\n * **REMOVED:** ~~TLS_AES_128_CCM_8_SHA256[¹][tlsv13_only]~~\n\n[OkHttp 3.13][OkHttp313]\n------------------------\n\n_2019-02-04_\n\nRemove TLSv1.1 and TLSv1 from MODERN_TLS. Change COMPATIBLE_TLS to support all TLS versions.\n\n##### RESTRICTED_TLS versions\n\n* TLSv1.3\n* TLSv1.2\n\n##### MODERN_TLS versions\n\n* TLSv1.3\n* TLSv1.2\n* **REMOVED:** ~~TLSv1.1~~\n* **REMOVED:** ~~TLSv1~~\n\n##### COMPATIBLE_TLS versions\n\n* **NEW:** TLSv1.3\n* **NEW:** TLSv1.2\n* **NEW:** TLSv1.1\n* TLSv1\n\n[OkHttp 3.12][OkHttp312]\n------------------------\n\n_2018-11-16_\n\nAdded support for TLSv1.3.\n\n##### RESTRICTED_TLS cipher suites\n\n * **NEW:** TLS_AES_128_GCM_SHA256[¹][tlsv13_only]\n * **NEW:** TLS_AES_256_GCM_SHA384[¹][tlsv13_only]\n * **NEW:** TLS_CHACHA20_POLY1305_SHA256[¹][tlsv13_only]\n * **NEW:** TLS_AES_128_CCM_SHA256[¹][tlsv13_only]\n * **NEW:** TLS_AES_128_CCM_8_SHA256[¹][tlsv13_only]\n * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n\n##### MODERN_TLS / COMPATIBLE_TLS cipher suites\n\n * **NEW:** TLS_AES_128_GCM_SHA256[¹][tlsv13_only]\n * **NEW:** TLS_AES_256_GCM_SHA384[¹][tlsv13_only]\n * **NEW:** TLS_CHACHA20_POLY1305_SHA256[¹][tlsv13_only]\n * **NEW:** TLS_AES_128_CCM_SHA256[¹][tlsv13_only]\n * **NEW:** TLS_AES_128_CCM_8_SHA256[¹][tlsv13_only]\n * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_GCM_SHA256[²][http2_naughty]\n * TLS_RSA_WITH_AES_256_GCM_SHA384[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_3DES_EDE_CBC_SHA[²][http2_naughty]\n\n##### RESTRICTED_TLS versions\n\n* **NEW:** TLSv1.3\n* TLSv1.2\n\n##### MODERN_TLS versions\n\n* **NEW:** TLSv1.3\n* TLSv1.2\n* TLSv1.1\n* TLSv1\n\n##### COMPATIBLE_TLS versions\n\n* TLSv1\n\n[OkHttp 3.11][OkHttp311]\n------------------------\n\n_2018-07-12_\n\nAdded a new extra strict RESTRICTED_TLS configuration inspired by [Google Cloud’s similar policy][googlecloud_ssl_policy]. It is appropriate when both the host platform\n(JVM/Conscrypt/Android) and target webserver are current.\n\n##### RESTRICTED_TLS cipher suites\n\n * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n\n##### RESTRICTED_TLS versions\n\n * TLSv1.2\n\n[OkHttp 3.10][OkHttp310]\n------------------------\n\n_2018-02-24_\n\nRemove two rarely-used cipher suites from the default set. This tracks a [Chromium change][chromium_change] to remove these cipher suites because they are fragile and rarely-used.\n\n##### MODERN_TLS / COMPATIBLE_TLS cipher suites\n\n * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n * TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_GCM_SHA256[²][http2_naughty]\n * TLS_RSA_WITH_AES_256_GCM_SHA384[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_3DES_EDE_CBC_SHA[²][http2_naughty]\n * **REMOVED:** ~~TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA~~\n * **REMOVED:** ~~TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA~~\n\n\n[OkHttp 3.5][OkHttp35]\n----------------------\n\n_2016-11-30_\n\nRemove three old cipher suites and add five new ones. This tracks changes in what's available on\nAndroid and Java, and also what cipher suites recent releases of Chrome and Firefox support by\ndefault.\n\n##### MODERN_TLS / COMPATIBLE_TLS cipher suites\n\n * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n * **NEW:** TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n * **NEW:** TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n * **NEW:** TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\n * **NEW:** TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_GCM_SHA256[²][http2_naughty]\n * **NEW:** TLS_RSA_WITH_AES_256_GCM_SHA384[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_3DES_EDE_CBC_SHA[²][http2_naughty]\n * **REMOVED:** ~~TLS_DHE_RSA_WITH_AES_128_CBC_SHA~~\n * **REMOVED:** ~~TLS_DHE_RSA_WITH_AES_128_GCM_SHA256~~\n * **REMOVED:** ~~TLS_DHE_RSA_WITH_AES_256_CBC_SHA~~\n\n[OkHttp 3.0][OkHttp30]\n----------------------\n\n_2016-01-13_\n\n##### MODERN_TLS / COMPATIBLE_TLS cipher suites\n\n * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\n * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_DHE_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_DHE_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_GCM_SHA256[²][http2_naughty]\n * TLS_RSA_WITH_AES_128_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_AES_256_CBC_SHA[²][http2_naughty]\n * TLS_RSA_WITH_3DES_EDE_CBC_SHA[²][http2_naughty]\n\n##### MODERN_TLS versions\n\n * TLSv1.2\n * TLSv1.1\n * TLSv1\n\n##### COMPATIBLE_TLS versions\n\n * TLSv1\n\n---\n\n<a name=\"tlsv13_only\"></a>\n#### ¹ TLSv1.3 Only\n\nCipher suites that are only available with TLSv1.3.\n\n<a name=\"http2_naughty\"></a>\n#### ² HTTP/2 Cipher Suite Denylist\n\nCipher suites that are [discouraged for use][http2_denylist] with HTTP/2. OkHttp includes them because better suites are not commonly available. For example, none of the better cipher suites listed above shipped with Android 4.4 or Java 7.\n\n[OkHttp30]: https://square.github.io/okhttp/changelog_3x/#version-300\n[OkHttp310]: https://square.github.io/okhttp/changelog_3x/#version-310\n[OkHttp311]: https://square.github.io/okhttp/changelog_3x/#version-320\n[OkHttp312]: https://square.github.io/okhttp/changelog_3x/#version-330\n[OkHttp313]: https://square.github.io/okhttp/changelog_3x/#version-340\n[OkHttp314]: https://square.github.io/okhttp/changelog_3x/#version-310\n[OkHttp35]: https://square.github.io/okhttp/changelog_3x/#version-350\n[chromium_change]: https://developers.google.com/web/updates/2016/12/chrome-56-deprecations#remove_cbc-mode_ecdsa_ciphers_in_tls\n[googlecloud_ssl_policy]: https://cloud.google.com/load-balancing/docs/ssl-policies-concepts\n[http2_denylist]: https://tools.ietf.org/html/rfc7540#appendix-A\n[http2_naughty]: #http2_naughty\n[tlsv13_only]: #tlsv13_only\n"
  },
  {
    "path": "docs/works_with_okhttp.md",
    "content": "Works with OkHttp\n=================\n\nHere’s some libraries that work nicely with OkHttp.\n\n * [Chucker](https://github.com/ChuckerTeam/chucker): An in-app HTTP inspector for Android OkHttp clients.\n * [Coil](https://github.com/coil-kt/coil): An image loading library for Android backed by Kotlin Coroutines.\n * [Communicator](https://github.com/Taig/Communicator): An OkHttp wrapper for Scala built with Android in mind.\n * [Cronet Transport for OkHttp](https://github.com/google/cronet-transport-for-okhttp): A HTTP3 ready transport layer for OkHttp on Android, based on Chromium network stack.\n * [CWAC-NetSecurity](https://github.com/commonsguy/cwac-netsecurity): Simplifying Secure Internet Access.\n * [Failsafe](https://failsafe.dev/okhttp/): Fault tolerance and resilience patterns.\n * [Flipper](https://fbflipper.com/): A desktop debugging platform for mobile developers.\n * [Fresco](https://github.com/facebook/fresco): An Android library for managing images and the memory they use.\n * [Glide](https://github.com/bumptech/glide): An image loading and caching library for Android focused on smooth scrolling.\n * [GoogleAppEngineOkHttp](https://github.com/apkelly/GoogleAppEngineOkHttp): An OkHttp Call that works on Google App Engine.\n * [Hunter](https://github.com/Leaking/Hunter): Configure all OkHttpClients centrally.\n * ⬜️ [Moshi](https://github.com/square/moshi): A modern JSON library for Android and Java.\n * [Ok2Curl](https://github.com/mrmike/Ok2Curl): Convert OkHttp requests into curl logs.\n * [OkHttp AWS Signer](https://github.com/babbel/okhttp-aws-signer): AWS V4 signing algorithm for OkHttp requests\n * [okhttp-digest](https://github.com/rburgst/okhttp-digest): A digest authenticator for OkHttp.\n * [OkHttp Idling Resource](https://github.com/JakeWharton/okhttp-idling-resource): An Espresso IdlingResource for OkHttp.\n * [okhttp-client-mock](https://github.com/gmazzo/okhttp-client-mock): A simple OKHttp client mock, using a programmable request interceptor.\n * [OkHttp Profiler](https://plugins.jetbrains.com/plugin/11249-okhttp-profiler): An IntelliJ plugin for monitoring OkHttp calls.\n * [OkReplay](https://github.com/airbnb/okreplay): Record and replay OkHttp network interaction in your tests.\n * [okhttp-signpost](https://github.com/pakerfeldt/okhttp-signpost): OAuth signing with signpost and OkHttp.\n * [okhttp-spring-boot](https://github.com/freefair/okhttp-spring-boot): Spring Boot starters for OkHttp\n * [okhttp-staleiferror-interceptor](https://github.com/PeelTechnologies/okhttp-staleiferror-interceptor/): serve stale responses when the server isn’t reachable.\n * [okhttp-stats](https://github.com/flipkart-incubator/okhttp-stats): Get stats like average network speed.\n * [okhttp-system-keystore](https://github.com/charleskorn/okhttp-system-keystore): Use trusted certificates from the operating system keystore (Keychain on macOS, Certificate Store on Windows).\n * ⬜️ [Okio](https://github.com/square/okio/): A modern I/O API for Java.\n * [OkLog](https://github.com/simonpercic/OkLog): Response logging interceptor for OkHttp. Logs a URL link with URL-encoded response for every OkHttp call.\n * [Okurl](https://github.com/yschimke/okurl/wiki) A curl-like client for social networks and other APIs.\n * [PersistentCookieJar](https://github.com/franmontiel/PersistentCookieJar): A persistent `CookieJar`.\n * ⬜️ [Picasso](https://github.com/square/picasso): A powerful image downloading and caching library for Android.\n * ⬜️ [Retrofit](https://github.com/square/retrofit): Type-safe HTTP client for Android and Java by Square.\n * [ScribeJava](https://github.com/scribejava/scribejava): Simple OAuth library for Java\n * [Stetho](https://github.com/facebook/stetho): Stetho is a debug bridge for Android applications.\n * ⬜️ [Wire](https://github.com/square/wire): Clean, lightweight protocol buffers for Android and Java.\n"
  },
  {
    "path": "fuzzing/fuzzingserver-config.json",
    "content": "{\n  \"url\": \"ws://127.0.0.1:9099\",\n  \"outdir\": \"./target/fuzzingserver-report\",\n  \"cases\": [\"*\"],\n  \"exclude-cases\": [\n    \"6.1.1\",\n    \"6.1.2\",\n    \"6.1.3\",\n    \"6.2.1\",\n    \"6.2.2\",\n    \"6.2.3\",\n    \"6.2.4\",\n    \"6.3.1\",\n    \"6.3.2\",\n    \"6.4.1\",\n    \"6.4.2\",\n    \"6.4.3\",\n    \"6.4.4\",\n    \"6.5.1\",\n    \"6.5.2\",\n    \"6.5.3\",\n    \"6.5.4\",\n    \"6.5.5\",\n    \"6.6.1\",\n    \"6.6.2\",\n    \"6.6.3\",\n    \"6.6.4\",\n    \"6.6.5\",\n    \"6.6.6\",\n    \"6.6.7\",\n    \"6.6.8\",\n    \"6.6.9\",\n    \"6.6.10\",\n    \"6.6.11\",\n    \"6.7.1\",\n    \"6.7.2\",\n    \"6.7.3\",\n    \"6.7.4\",\n    \"6.8.1\",\n    \"6.8.2\",\n    \"6.9.1\",\n    \"6.9.2\",\n    \"6.9.3\",\n    \"6.9.4\",\n    \"6.10.1\",\n    \"6.10.2\",\n    \"6.10.3\",\n    \"6.11.1\",\n    \"6.11.2\",\n    \"6.11.3\",\n    \"6.11.4\",\n    \"6.11.5\",\n    \"6.12.1\",\n    \"6.12.2\",\n    \"6.12.3\",\n    \"6.12.4\",\n    \"6.12.5\",\n    \"6.12.6\",\n    \"6.12.7\",\n    \"6.12.8\",\n    \"6.13.1\",\n    \"6.13.2\",\n    \"6.13.3\",\n    \"6.13.4\",\n    \"6.13.5\",\n    \"6.14.1\",\n    \"6.14.2\",\n    \"6.14.3\",\n    \"6.14.4\",\n    \"6.14.5\",\n    \"6.14.6\",\n    \"6.14.7\",\n    \"6.14.8\",\n    \"6.14.9\",\n    \"6.14.10\",\n    \"6.15.1\",\n    \"6.16.1\",\n    \"6.16.2\",\n    \"6.16.3\",\n    \"6.17.1\",\n    \"6.17.2\",\n    \"6.17.3\",\n    \"6.17.4\",\n    \"6.17.5\",\n    \"6.18.1\",\n    \"6.18.2\",\n    \"6.18.3\",\n    \"6.18.4\",\n    \"6.18.5\",\n    \"6.19.1\",\n    \"6.19.2\",\n    \"6.19.3\",\n    \"6.19.4\",\n    \"6.19.5\",\n    \"6.20.1\",\n    \"6.20.2\",\n    \"6.20.3\",\n    \"6.20.4\",\n    \"6.20.5\",\n    \"6.20.6\",\n    \"6.20.7\",\n    \"6.21.1\",\n    \"6.21.2\",\n    \"6.21.3\",\n    \"6.21.4\",\n    \"6.21.5\",\n    \"6.21.6\",\n    \"6.21.7\",\n    \"6.21.8\",\n    \"6.22.1\",\n    \"6.22.2\",\n    \"6.22.3\",\n    \"6.22.4\",\n    \"6.22.5\",\n    \"6.22.6\",\n    \"6.22.7\",\n    \"6.22.8\",\n    \"6.22.9\",\n    \"6.22.10\",\n    \"6.22.11\",\n    \"6.22.12\",\n    \"6.22.13\",\n    \"6.22.14\",\n    \"6.22.15\",\n    \"6.22.16\",\n    \"6.22.17\",\n    \"6.22.18\",\n    \"6.22.19\",\n    \"6.22.20\",\n    \"6.22.21\",\n    \"6.22.22\",\n    \"6.22.23\",\n    \"6.22.24\",\n    \"6.22.25\",\n    \"6.22.26\",\n    \"6.22.27\",\n    \"6.22.28\",\n    \"6.22.29\",\n    \"6.22.30\",\n    \"6.22.31\",\n    \"6.22.32\",\n    \"6.22.33\",\n    \"6.22.34\",\n    \"6.23.1\",\n    \"6.23.2\",\n    \"6.23.3\",\n    \"6.23.4\",\n    \"6.23.5\",\n    \"6.23.6\",\n    \"6.23.7\"\n  ],\n  \"exclude-agent-cases\": {}\n}\n"
  },
  {
    "path": "fuzzing/fuzzingserver-expected.txt",
    "content": "\"1.1.1 OK\"\n\"1.1.2 OK\"\n\"1.1.3 OK\"\n\"1.1.4 OK\"\n\"1.1.5 OK\"\n\"1.1.6 OK\"\n\"1.1.7 OK\"\n\"1.1.8 OK\"\n\"1.2.1 OK\"\n\"1.2.2 OK\"\n\"1.2.3 OK\"\n\"1.2.4 OK\"\n\"1.2.5 OK\"\n\"1.2.6 OK\"\n\"1.2.7 OK\"\n\"1.2.8 OK\"\n\"10.1.1 OK\"\n\"12.1.1 UNIMPLEMENTED\"\n\"12.1.10 UNIMPLEMENTED\"\n\"12.1.11 UNIMPLEMENTED\"\n\"12.1.12 UNIMPLEMENTED\"\n\"12.1.13 UNIMPLEMENTED\"\n\"12.1.14 UNIMPLEMENTED\"\n\"12.1.15 UNIMPLEMENTED\"\n\"12.1.16 UNIMPLEMENTED\"\n\"12.1.17 UNIMPLEMENTED\"\n\"12.1.18 UNIMPLEMENTED\"\n\"12.1.2 UNIMPLEMENTED\"\n\"12.1.3 UNIMPLEMENTED\"\n\"12.1.4 UNIMPLEMENTED\"\n\"12.1.5 UNIMPLEMENTED\"\n\"12.1.6 UNIMPLEMENTED\"\n\"12.1.7 UNIMPLEMENTED\"\n\"12.1.8 UNIMPLEMENTED\"\n\"12.1.9 UNIMPLEMENTED\"\n\"12.2.1 UNIMPLEMENTED\"\n\"12.2.10 UNIMPLEMENTED\"\n\"12.2.11 UNIMPLEMENTED\"\n\"12.2.12 UNIMPLEMENTED\"\n\"12.2.13 UNIMPLEMENTED\"\n\"12.2.14 UNIMPLEMENTED\"\n\"12.2.15 UNIMPLEMENTED\"\n\"12.2.16 UNIMPLEMENTED\"\n\"12.2.17 UNIMPLEMENTED\"\n\"12.2.18 UNIMPLEMENTED\"\n\"12.2.2 UNIMPLEMENTED\"\n\"12.2.3 UNIMPLEMENTED\"\n\"12.2.4 UNIMPLEMENTED\"\n\"12.2.5 UNIMPLEMENTED\"\n\"12.2.6 UNIMPLEMENTED\"\n\"12.2.7 UNIMPLEMENTED\"\n\"12.2.8 UNIMPLEMENTED\"\n\"12.2.9 UNIMPLEMENTED\"\n\"12.3.1 UNIMPLEMENTED\"\n\"12.3.10 UNIMPLEMENTED\"\n\"12.3.11 UNIMPLEMENTED\"\n\"12.3.12 UNIMPLEMENTED\"\n\"12.3.13 UNIMPLEMENTED\"\n\"12.3.14 UNIMPLEMENTED\"\n\"12.3.15 UNIMPLEMENTED\"\n\"12.3.16 UNIMPLEMENTED\"\n\"12.3.17 UNIMPLEMENTED\"\n\"12.3.18 UNIMPLEMENTED\"\n\"12.3.2 UNIMPLEMENTED\"\n\"12.3.3 UNIMPLEMENTED\"\n\"12.3.4 UNIMPLEMENTED\"\n\"12.3.5 UNIMPLEMENTED\"\n\"12.3.6 UNIMPLEMENTED\"\n\"12.3.7 UNIMPLEMENTED\"\n\"12.3.8 UNIMPLEMENTED\"\n\"12.3.9 UNIMPLEMENTED\"\n\"12.4.1 UNIMPLEMENTED\"\n\"12.4.10 UNIMPLEMENTED\"\n\"12.4.11 UNIMPLEMENTED\"\n\"12.4.12 UNIMPLEMENTED\"\n\"12.4.13 UNIMPLEMENTED\"\n\"12.4.14 UNIMPLEMENTED\"\n\"12.4.15 UNIMPLEMENTED\"\n\"12.4.16 UNIMPLEMENTED\"\n\"12.4.17 UNIMPLEMENTED\"\n\"12.4.18 UNIMPLEMENTED\"\n\"12.4.2 UNIMPLEMENTED\"\n\"12.4.3 UNIMPLEMENTED\"\n\"12.4.4 UNIMPLEMENTED\"\n\"12.4.5 UNIMPLEMENTED\"\n\"12.4.6 UNIMPLEMENTED\"\n\"12.4.7 UNIMPLEMENTED\"\n\"12.4.8 UNIMPLEMENTED\"\n\"12.4.9 UNIMPLEMENTED\"\n\"12.5.1 UNIMPLEMENTED\"\n\"12.5.10 UNIMPLEMENTED\"\n\"12.5.11 UNIMPLEMENTED\"\n\"12.5.12 UNIMPLEMENTED\"\n\"12.5.13 UNIMPLEMENTED\"\n\"12.5.14 UNIMPLEMENTED\"\n\"12.5.15 UNIMPLEMENTED\"\n\"12.5.16 UNIMPLEMENTED\"\n\"12.5.17 UNIMPLEMENTED\"\n\"12.5.18 UNIMPLEMENTED\"\n\"12.5.2 UNIMPLEMENTED\"\n\"12.5.3 UNIMPLEMENTED\"\n\"12.5.4 UNIMPLEMENTED\"\n\"12.5.5 UNIMPLEMENTED\"\n\"12.5.6 UNIMPLEMENTED\"\n\"12.5.7 UNIMPLEMENTED\"\n\"12.5.8 UNIMPLEMENTED\"\n\"12.5.9 UNIMPLEMENTED\"\n\"13.1.1 UNIMPLEMENTED\"\n\"13.1.10 UNIMPLEMENTED\"\n\"13.1.11 UNIMPLEMENTED\"\n\"13.1.12 UNIMPLEMENTED\"\n\"13.1.13 UNIMPLEMENTED\"\n\"13.1.14 UNIMPLEMENTED\"\n\"13.1.15 UNIMPLEMENTED\"\n\"13.1.16 UNIMPLEMENTED\"\n\"13.1.17 UNIMPLEMENTED\"\n\"13.1.18 UNIMPLEMENTED\"\n\"13.1.2 UNIMPLEMENTED\"\n\"13.1.3 UNIMPLEMENTED\"\n\"13.1.4 UNIMPLEMENTED\"\n\"13.1.5 UNIMPLEMENTED\"\n\"13.1.6 UNIMPLEMENTED\"\n\"13.1.7 UNIMPLEMENTED\"\n\"13.1.8 UNIMPLEMENTED\"\n\"13.1.9 UNIMPLEMENTED\"\n\"13.2.1 UNIMPLEMENTED\"\n\"13.2.10 UNIMPLEMENTED\"\n\"13.2.11 UNIMPLEMENTED\"\n\"13.2.12 UNIMPLEMENTED\"\n\"13.2.13 UNIMPLEMENTED\"\n\"13.2.14 UNIMPLEMENTED\"\n\"13.2.15 UNIMPLEMENTED\"\n\"13.2.16 UNIMPLEMENTED\"\n\"13.2.17 UNIMPLEMENTED\"\n\"13.2.18 UNIMPLEMENTED\"\n\"13.2.2 UNIMPLEMENTED\"\n\"13.2.3 UNIMPLEMENTED\"\n\"13.2.4 UNIMPLEMENTED\"\n\"13.2.5 UNIMPLEMENTED\"\n\"13.2.6 UNIMPLEMENTED\"\n\"13.2.7 UNIMPLEMENTED\"\n\"13.2.8 UNIMPLEMENTED\"\n\"13.2.9 UNIMPLEMENTED\"\n\"13.3.1 UNIMPLEMENTED\"\n\"13.3.10 UNIMPLEMENTED\"\n\"13.3.11 UNIMPLEMENTED\"\n\"13.3.12 UNIMPLEMENTED\"\n\"13.3.13 UNIMPLEMENTED\"\n\"13.3.14 UNIMPLEMENTED\"\n\"13.3.15 UNIMPLEMENTED\"\n\"13.3.16 UNIMPLEMENTED\"\n\"13.3.17 UNIMPLEMENTED\"\n\"13.3.18 UNIMPLEMENTED\"\n\"13.3.2 UNIMPLEMENTED\"\n\"13.3.3 UNIMPLEMENTED\"\n\"13.3.4 UNIMPLEMENTED\"\n\"13.3.5 UNIMPLEMENTED\"\n\"13.3.6 UNIMPLEMENTED\"\n\"13.3.7 UNIMPLEMENTED\"\n\"13.3.8 UNIMPLEMENTED\"\n\"13.3.9 UNIMPLEMENTED\"\n\"13.4.1 UNIMPLEMENTED\"\n\"13.4.10 UNIMPLEMENTED\"\n\"13.4.11 UNIMPLEMENTED\"\n\"13.4.12 UNIMPLEMENTED\"\n\"13.4.13 UNIMPLEMENTED\"\n\"13.4.14 UNIMPLEMENTED\"\n\"13.4.15 UNIMPLEMENTED\"\n\"13.4.16 UNIMPLEMENTED\"\n\"13.4.17 UNIMPLEMENTED\"\n\"13.4.18 UNIMPLEMENTED\"\n\"13.4.2 UNIMPLEMENTED\"\n\"13.4.3 UNIMPLEMENTED\"\n\"13.4.4 UNIMPLEMENTED\"\n\"13.4.5 UNIMPLEMENTED\"\n\"13.4.6 UNIMPLEMENTED\"\n\"13.4.7 UNIMPLEMENTED\"\n\"13.4.8 UNIMPLEMENTED\"\n\"13.4.9 UNIMPLEMENTED\"\n\"13.5.1 UNIMPLEMENTED\"\n\"13.5.10 UNIMPLEMENTED\"\n\"13.5.11 UNIMPLEMENTED\"\n\"13.5.12 UNIMPLEMENTED\"\n\"13.5.13 UNIMPLEMENTED\"\n\"13.5.14 UNIMPLEMENTED\"\n\"13.5.15 UNIMPLEMENTED\"\n\"13.5.16 UNIMPLEMENTED\"\n\"13.5.17 UNIMPLEMENTED\"\n\"13.5.18 UNIMPLEMENTED\"\n\"13.5.2 UNIMPLEMENTED\"\n\"13.5.3 UNIMPLEMENTED\"\n\"13.5.4 UNIMPLEMENTED\"\n\"13.5.5 UNIMPLEMENTED\"\n\"13.5.6 UNIMPLEMENTED\"\n\"13.5.7 UNIMPLEMENTED\"\n\"13.5.8 UNIMPLEMENTED\"\n\"13.5.9 UNIMPLEMENTED\"\n\"13.6.1 UNIMPLEMENTED\"\n\"13.6.10 UNIMPLEMENTED\"\n\"13.6.11 UNIMPLEMENTED\"\n\"13.6.12 UNIMPLEMENTED\"\n\"13.6.13 UNIMPLEMENTED\"\n\"13.6.14 UNIMPLEMENTED\"\n\"13.6.15 UNIMPLEMENTED\"\n\"13.6.16 UNIMPLEMENTED\"\n\"13.6.17 UNIMPLEMENTED\"\n\"13.6.18 UNIMPLEMENTED\"\n\"13.6.2 UNIMPLEMENTED\"\n\"13.6.3 UNIMPLEMENTED\"\n\"13.6.4 UNIMPLEMENTED\"\n\"13.6.5 UNIMPLEMENTED\"\n\"13.6.6 UNIMPLEMENTED\"\n\"13.6.7 UNIMPLEMENTED\"\n\"13.6.8 UNIMPLEMENTED\"\n\"13.6.9 UNIMPLEMENTED\"\n\"13.7.1 UNIMPLEMENTED\"\n\"13.7.10 UNIMPLEMENTED\"\n\"13.7.11 UNIMPLEMENTED\"\n\"13.7.12 UNIMPLEMENTED\"\n\"13.7.13 UNIMPLEMENTED\"\n\"13.7.14 UNIMPLEMENTED\"\n\"13.7.15 UNIMPLEMENTED\"\n\"13.7.16 UNIMPLEMENTED\"\n\"13.7.17 UNIMPLEMENTED\"\n\"13.7.18 UNIMPLEMENTED\"\n\"13.7.2 UNIMPLEMENTED\"\n\"13.7.3 UNIMPLEMENTED\"\n\"13.7.4 UNIMPLEMENTED\"\n\"13.7.5 UNIMPLEMENTED\"\n\"13.7.6 UNIMPLEMENTED\"\n\"13.7.7 UNIMPLEMENTED\"\n\"13.7.8 UNIMPLEMENTED\"\n\"13.7.9 UNIMPLEMENTED\"\n\"2.1 OK\"\n\"2.10 OK\"\n\"2.11 OK\"\n\"2.2 OK\"\n\"2.3 OK\"\n\"2.4 OK\"\n\"2.5 OK\"\n\"2.6 OK\"\n\"2.7 OK\"\n\"2.8 OK\"\n\"2.9 OK\"\n\"3.1 OK\"\n\"3.2 NON-STRICT\"\n\"3.3 NON-STRICT\"\n\"3.4 NON-STRICT\"\n\"3.5 OK\"\n\"3.6 OK\"\n\"3.7 OK\"\n\"4.1.1 OK\"\n\"4.1.2 OK\"\n\"4.1.3 NON-STRICT\"\n\"4.1.4 NON-STRICT\"\n\"4.1.5 OK\"\n\"4.2.1 OK\"\n\"4.2.2 OK\"\n\"4.2.3 NON-STRICT\"\n\"4.2.4 OK\"\n\"4.2.5 OK\"\n\"5.1 OK\"\n\"5.10 OK\"\n\"5.11 OK\"\n\"5.12 OK\"\n\"5.13 OK\"\n\"5.14 OK\"\n\"5.15 OK\"\n\"5.16 OK\"\n\"5.17 OK\"\n\"5.18 OK\"\n\"5.19 OK\"\n\"5.2 OK\"\n\"5.20 OK\"\n\"5.3 OK\"\n\"5.4 OK\"\n\"5.5 OK\"\n\"5.6 OK\"\n\"5.7 OK\"\n\"5.8 OK\"\n\"5.9 OK\"\n\"7.1.1 OK\"\n\"7.1.2 OK\"\n\"7.1.3 OK\"\n\"7.1.4 OK\"\n\"7.1.5 OK\"\n\"7.1.6 INFORMATIONAL\"\n\"7.13.1 INFORMATIONAL\"\n\"7.13.2 INFORMATIONAL\"\n\"7.3.1 OK\"\n\"7.3.2 OK\"\n\"7.3.3 OK\"\n\"7.3.4 OK\"\n\"7.3.5 OK\"\n\"7.3.6 OK\"\n\"7.5.1 FAILED\"\n\"7.7.1 OK\"\n\"7.7.10 OK\"\n\"7.7.11 OK\"\n\"7.7.12 OK\"\n\"7.7.13 OK\"\n\"7.7.2 OK\"\n\"7.7.3 OK\"\n\"7.7.4 OK\"\n\"7.7.5 OK\"\n\"7.7.6 OK\"\n\"7.7.7 OK\"\n\"7.7.8 OK\"\n\"7.7.9 OK\"\n\"7.9.1 OK\"\n\"7.9.10 OK\"\n\"7.9.11 OK\"\n\"7.9.12 OK\"\n\"7.9.13 OK\"\n\"7.9.2 OK\"\n\"7.9.3 OK\"\n\"7.9.4 OK\"\n\"7.9.5 OK\"\n\"7.9.6 OK\"\n\"7.9.7 OK\"\n\"7.9.8 OK\"\n\"7.9.9 OK\"\n\"9.1.1 OK\"\n\"9.1.2 OK\"\n\"9.1.3 OK\"\n\"9.1.4 OK\"\n\"9.1.5 OK\"\n\"9.1.6 OK\"\n\"9.2.1 OK\"\n\"9.2.2 OK\"\n\"9.2.3 OK\"\n\"9.2.4 OK\"\n\"9.2.5 OK\"\n\"9.2.6 OK\"\n\"9.3.1 OK\"\n\"9.3.2 OK\"\n\"9.3.3 OK\"\n\"9.3.4 OK\"\n\"9.3.5 OK\"\n\"9.3.6 OK\"\n\"9.3.7 OK\"\n\"9.3.8 OK\"\n\"9.3.9 OK\"\n\"9.4.1 OK\"\n\"9.4.2 OK\"\n\"9.4.3 OK\"\n\"9.4.4 OK\"\n\"9.4.5 OK\"\n\"9.4.6 OK\"\n\"9.4.7 OK\"\n\"9.4.8 OK\"\n\"9.4.9 OK\"\n\"9.5.1 OK\"\n\"9.5.2 OK\"\n\"9.5.3 OK\"\n\"9.5.4 OK\"\n\"9.5.5 OK\"\n\"9.5.6 OK\"\n\"9.6.1 OK\"\n\"9.6.2 OK\"\n\"9.6.3 OK\"\n\"9.6.4 OK\"\n\"9.6.5 OK\"\n\"9.6.6 OK\"\n\"9.7.1 OK\"\n\"9.7.2 OK\"\n\"9.7.3 OK\"\n\"9.7.4 OK\"\n\"9.7.5 OK\"\n\"9.7.6 OK\"\n\"9.8.1 OK\"\n\"9.8.2 OK\"\n\"9.8.3 OK\"\n\"9.8.4 OK\"\n\"9.8.5 OK\"\n\"9.8.6 OK\"\n"
  },
  {
    "path": "fuzzing/fuzzingserver-test.sh",
    "content": "#!/usr/bin/env bash\n\nSCRIPT_DIR=\"$( cd \"$( dirname \"$0\" )\" && pwd )\"\ncd \"$SCRIPT_DIR\"\n\nwhich wstest\nif [ $? != 0 ]; then\n  echo \"Run 'pip install autobahntestsuite', maybe with 'sudo'.\"\n  exit 1\nfi\nwhich jq\nif [ $? != 0 ]; then\n  echo \"Run 'brew install jq'\"\n  exit 1\nfi\n\ntrap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT\n\nset -ex\n\nwstest -m fuzzingserver -s fuzzingserver-config.json &\nsleep 2 # wait for wstest to start\n\njava -jar target/okhttp-tests-*-jar-with-dependencies.jar\n\njq '.[] as $in | $in | keys[] | . + \" \" + $in[.].behavior' target/fuzzingserver-report/index.json > target/fuzzingserver-actual.txt\n\ndiff fuzzingserver-expected.txt target/fuzzingserver-actual.txt\n"
  },
  {
    "path": "fuzzing/fuzzingserver-update-expected.sh",
    "content": "#!/usr/bin/env bash\n\nSCRIPT_DIR=\"$( cd \"$( dirname \"$0\" )\" && pwd )\"\ncd \"$SCRIPT_DIR\"\n\nif [ ! -f target/fuzzingserver-actual.txt ]; then\n  echo \"File not found. Did you run the Autobahn test script?\"\n  exit 1\nfi\n\ncp target/fuzzingserver-actual.txt fuzzingserver-expected.txt\n"
  },
  {
    "path": "gradle/gradle-daemon-jvm.properties",
    "content": "#This file is generated by updateDaemonJvm\ntoolchainVendor=ADOPTIUM\ntoolchainVersion=21\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\nagp = \"9.1.0\"\namazon-corretto = \"2.5.0\"\nandroid-junit5 = \"2.0.1\"\nandroidx-activity = \"1.11.0\"\nandroidx-annotation = \"1.9.1\"\nandroidx-espresso-core = \"3.7.0\"\nandroidx-junit = \"1.3.0\"\nandroidx-test-runner = \"1.7.0\"\nanimalsniffer = \"2.0.1\"\nanimalsniffer-annotations = \"1.27\"\nassertk = \"0.28.1\"\nbinary-compatibility-validator = \"0.18.1\"\nbnd = \"7.2.1\"\nbrotli-dec = \"0.1.2\"\nburst = \"2.10.2\"\ncheckstyle = \"13.3.0\"\nclikt = \"5.1.0\"\nextra-java-module-info = \"1.14\"\ncodehaus-signature-java18 = \"1.0\"\ncoroutines = \"1.10.2\"\nde-mannodermaus-junit5 = \"2.0.1\"\ndokka = \"2.1.0\"\neclipse-osgi = \"3.24.100\"\ngraalvm = \"25.0.2\"\ngraalvm-plugin = \"0.11.5\"\nguava = \"33.5.0-jre\"\nhamcrest-library = \"3.0\"\nhttp-client5 = \"5.6\"\njetty-client = \"9.4.58.v20250814\"\njlink = \"0.7\"\njetbrains-annotations = \"26.1.0\"\njnr-unixsocket = \"0.38.24\"\njsoup = \"1.22.1\"\njunit-pioneer = \"1.9.1\"\njunit-platform = \"1.14.3\"\njunit4 = \"4.13.2\"\nkotlin = \"2.2.21\"\nksp = \"2.3.6\"\nlint-gradle = \"1.0.0-alpha05\"\nmaven-publish = \"0.36.0\"\nmaven-sympathy = \"0.3.0\"\nmockserver-client = \"5.15.0\"\nmrjar = \"0.1.1\"\nopenjsse = \"1.1.14\"\norg-bouncycastle = \"1.83\"\norg-conscrypt = \"2.5.2\"\norg-junit-jupiter = \"5.13.4\"\nplayservices-safetynet = \"18.1.0\"\nrobolectric = \"4.16.1\"\nrobolectric-android = \"16-robolectric-13921718\"\nserialization = \"1.10.0\"\nshadow-plugin = \"9.4.0\"\nsignature-android-apilevel21 = \"5.0.1_r2\"\nsignature-android-apilevel24 = \"7.0_r2\"\nspotless-plugin = \"8.3.0\"\nsquare-kotlin-poet = \"2.2.0\"\nsquare-moshi = \"1.15.2\"\nsquare-okio = \"3.17.0\"\nsquare-retrofit = \"3.0.0\"\nstartup-runtime = \"1.2.0\"\ntestcontainers = \"1.21.4\"\nzstd-kmp-okio = \"0.4.0\"\n\n# Set to lower version than KGP version to maximize compatibility\n# Default to matching okio\nkotlinCoreLibrariesVersion = \"2.1.21\"\n\n[libraries]\namazon-corretto = { module = \"software.amazon.cryptools:AmazonCorrettoCryptoProvider\", version.ref = \"amazon-corretto\" }\nandroidx-activity = { module = \"androidx.activity:activity-ktx\", version.ref = \"androidx-activity\" }\nandroidx-annotation = { module = \"androidx.annotation:annotation\", version.ref = \"androidx-annotation\" }\nandroidx-espresso-core = { module = \"androidx.test.espresso:espresso-core\", version.ref = \"androidx-espresso-core\" }\nandroidx-junit = { module = \"androidx.test.ext:junit\", version.ref = \"androidx-junit\" }\nandroidx-lint-gradle = { module = \"androidx.lint:lint-gradle\", version.ref = \"lint-gradle\" }\nandroidx-startup-runtime = { module = \"androidx.startup:startup-runtime\", version.ref = \"startup-runtime\" }\nandroidx-test-runner = { module = \"androidx.test:runner\", version.ref = \"androidx-test-runner\" }\nanimalsniffer-annotations = { module = \"org.codehaus.mojo:animal-sniffer-annotations\", version.ref = \"animalsniffer-annotations\" }\naqute-bnd = { module = \"biz.aQute.bnd:biz.aQute.bnd\", version.ref = \"bnd\" }\naqute-resolve = { module = \"biz.aQute.bnd:biz.aQute.resolve\", version.ref = \"bnd\" }\nassertk = { module = \"com.willowtreeapps.assertk:assertk\", version.ref = \"assertk\" }\nbouncycastle-bcpkix = { module = \"org.bouncycastle:bcpkix-jdk15to18\", version.ref = \"org-bouncycastle\" }\nbouncycastle-bcprov = { module = \"org.bouncycastle:bcprov-jdk15to18\", version.ref = \"org-bouncycastle\" }\nbouncycastle-bctls = { module = \"org.bouncycastle:bctls-jdk15to18\", version.ref = \"org-bouncycastle\" }\nbrotli-dec = { module = \"org.brotli:dec\", version.ref = \"brotli-dec\" }\ncheckstyle = { module = \"com.puppycrawl.tools:checkstyle\", version.ref = \"checkstyle\" }\nclikt = { module = \"com.github.ajalt.clikt:clikt\", version.ref = \"clikt\" }\ncodehaus-signature-java18 = { module = \"org.codehaus.mojo.signature:java18\", version.ref = \"codehaus-signature-java18\" }\nconscrypt-android = { module = \"org.conscrypt:conscrypt-android\", version.ref = \"org-conscrypt\" }\nconscrypt-openjdk = { module = \"org.conscrypt:conscrypt-openjdk-uber\", version.ref = \"org-conscrypt\" }\nsquare-retrofit-converter-moshi = { module = \"com.squareup.retrofit2:converter-moshi\", version.ref = \"square-retrofit\" }\neclipse-osgi = { module = \"org.eclipse.platform:org.eclipse.osgi\", version.ref = \"eclipse-osgi\" }\nhamcrest-library = { module = \"org.hamcrest:hamcrest-library\", version.ref = \"hamcrest-library\" }\nguava-jre = { module = \"com.google.guava:guava\", version.ref = \"guava\" }\nhttp-client5 = { module = \"org.apache.httpcomponents.client5:httpclient5\", version.ref = \"http-client5\" }\njetty-client = { module = \"org.eclipse.jetty:jetty-client\", version.ref = \"jetty-client\" }\njnr-unixsocket = { module = \"com.github.jnr:jnr-unixsocket\", version.ref = \"jnr-unixsocket\" }\njsoup = { module = \"org.jsoup:jsoup\", version.ref = \"jsoup\" }\njunit = { module = \"junit:junit\", version.ref = \"junit4\" }\njunit-ktx = { module = \"androidx.test.ext:junit-ktx\", version.ref = \"androidx-junit\" }\njetbrains-annotations = { module = \"org.jetbrains:annotations\", version.ref = \"jetbrains-annotations\" }\njunit-jupiter-api = { module = \"org.junit.jupiter:junit-jupiter-api\", version.ref = \"org-junit-jupiter\" }\njunit-jupiter-engine = { module = \"org.junit.jupiter:junit-jupiter-engine\", version.ref = \"org-junit-jupiter\" }\njunit-jupiter-params = { module = \"org.junit.jupiter:junit-jupiter-params\", version.ref = \"org-junit-jupiter\" }\njunit-platform-console = { module = \"org.junit.platform:junit-platform-console\", version.ref = \"junit-platform\" }\njunit-platform-launcher = { module = \"org.junit.platform:junit-platform-launcher\", version.ref = \"junit-platform\" }\njunit-vintage-engine = { module = \"org.junit.vintage:junit-vintage-engine\", version.ref = \"org-junit-jupiter\" }\njunit-pioneer = { module = \"org.junit-pioneer:junit-pioneer\", version.ref = \"junit-pioneer\" }\njunit5android-core = { module = \"de.mannodermaus.junit5:android-test-core\", version.ref = \"de-mannodermaus-junit5\" }\njunit5android-runner = { module = \"de.mannodermaus.junit5:android-test-runner\", version.ref = \"de-mannodermaus-junit5\" }\nkotlin-gradle-plugin-api = { module = \"org.jetbrains.kotlin:kotlin-gradle-plugin-api\", version.ref = \"kotlin\" }\nkotlin-junit5 = { module = \"org.jetbrains.kotlin:kotlin-test-junit5\", version.ref = \"kotlin\" }\nkotlin-reflect = { module = \"org.jetbrains.kotlin:kotlin-reflect\", version.ref = \"kotlin\" }\nkotlin-stdlib = { module = \"org.jetbrains.kotlin:kotlin-stdlib\", version.ref = \"kotlin\" }\nkotlin-stdlib-osgi = { module = \"org.jetbrains.kotlin:kotlin-osgi-bundle\", version.ref = \"kotlin\" }\nkotlin-test-annotations = { module = \"org.jetbrains.kotlin:kotlin-test-annotations-common\", version.ref = \"kotlin\" }\nkotlin-test-common = { module = \"org.jetbrains.kotlin:kotlin-test-common\", version.ref = \"kotlin\" }\nkotlin-test-junit = { module = \"org.jetbrains.kotlin:kotlin-test-junit\", version.ref = \"kotlin\" }\nkotlinx-coroutines-core = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-core\", version.ref = \"coroutines\" }\nkotlinx-coroutines-test = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-test\", version.ref = \"coroutines\" }\nkotlinx-serialization-core = { module = \"org.jetbrains.kotlinx:kotlinx-serialization-core\", version.ref = \"serialization\" }\nkotlinx-serialization-json = { module = \"org.jetbrains.kotlinx:kotlinx-serialization-json\", version.ref = \"serialization\" }\nmockserver = { module = \"org.testcontainers:mockserver\", version.ref = \"testcontainers\" }\nmockserver-client = { module = \"org.mock-server:mockserver-client-java-no-dependencies\", version.ref = \"mockserver-client\" }\nnative-image-svm = { module = \"org.graalvm.nativeimage:svm\", version.ref = \"graalvm\" }\nopenjsse = { module = \"org.openjsse:openjsse\", version.ref = \"openjsse\" }\nplayservices-safetynet = { module = \"com.google.android.gms:play-services-safetynet\", version.ref = \"playservices-safetynet\" }\nsquare-retrofit = { module = \"com.squareup.retrofit2:retrofit\", version.ref = \"square-retrofit\" }\nrobolectric-android = { module = \"org.robolectric:android-all\", version.ref = \"robolectric-android\" }\nrobolectric = { module = \"org.robolectric:robolectric\", version.ref = \"robolectric\" }\nsignature-android-apilevel21 = { module = \"net.sf.androidscents.signature:android-api-level-21\", version.ref = \"signature-android-apilevel21\" }\nsignature-android-apilevel24 = { module = \"net.sf.androidscents.signature:android-api-level-24\", version.ref = \"signature-android-apilevel24\" }\nsquare-moshi = { module = \"com.squareup.moshi:moshi\", version.ref = \"square-moshi\" }\nsquare-moshi-compiler = { module = \"com.squareup.moshi:moshi-kotlin-codegen\", version.ref = \"square-moshi\" }\nsquare-moshi-kotlin = { module = \"com.squareup.moshi:moshi-kotlin\", version.ref = \"square-moshi\" }\nsquare-kotlin-poet = { module = \"com.squareup:kotlinpoet\", version.ref = \"square-kotlin-poet\" }\nsquare-okio = { module = \"com.squareup.okio:okio\", version.ref = \"square-okio\" }\nsquare-okio-fakefilesystem = { module = \"com.squareup.okio:okio-fakefilesystem\", version.ref = \"square-okio\" }\ntestcontainers = { module = \"org.testcontainers:testcontainers\", version.ref = \"testcontainers\" }\ntestcontainers-junit5 = { module = \"org.testcontainers:junit-jupiter\", version.ref = \"testcontainers\" }\nsquare-zstd-kmp-okio = { module = \"com.squareup.zstd:zstd-kmp-okio\", version.ref = \"zstd-kmp-okio\" }\n\n# Build Logic Dependencies\ngradlePlugin-android = { module = \"com.android.tools.build:gradle\", version.ref = \"agp\" }\ngradlePlugin-animalsniffer = { module = \"ru.vyarus:gradle-animalsniffer-plugin\", version.ref = \"animalsniffer\" }\ngradlePlugin-binaryCompatibilityValidator = { module = \"org.jetbrains.kotlinx:binary-compatibility-validator\", version.ref = \"binary-compatibility-validator\" }\ngradlePlugin-bnd = { module = \"biz.aQute.bnd:biz.aQute.bnd.gradle\", version.ref = \"bnd\" }\ngradlePlugin-dokka = { module = \"org.jetbrains.dokka:dokka-gradle-plugin\", version.ref = \"dokka\" }\ngradlePlugin-graalvm = { module = \"org.graalvm.buildtools.native:org.graalvm.buildtools.native.gradle.plugin\", version.ref = \"graalvm-plugin\" }\ngradlePlugin-kotlin = { module = \"org.jetbrains.kotlin:kotlin-gradle-plugin\", version.ref = \"kotlin\" }\ngradlePlugin-ksp = { module = \"com.google.devtools.ksp:symbol-processing-gradle-plugin\", version.ref = \"ksp\" }\ngradlePlugin-mavenPublish = { module = \"com.vanniktech:gradle-maven-publish-plugin\", version.ref = \"maven-publish\" }\ngradlePlugin-mrjar = { module = \"me.champeau.mrjar:me.champeau.mrjar.gradle.plugin\", version.ref = \"mrjar\" }\ngradlePlugin-shadow = { module = \"com.gradleup.shadow:shadow-gradle-plugin\", version.ref = \"shadow-plugin\" }\ngradlePlugin-spotless = { module = \"com.diffplug.spotless:spotless-plugin-gradle\", version.ref = \"spotless-plugin\" }\ngradlePlugin-tapmoc = { module =  \"com.gradleup.tapmoc:com.gradleup.tapmoc.gradle.plugin\", version = \"0.4.0\"}\n\n[plugins]\nandroid-application = { id = \"com.android.application\", version.ref = \"agp\" }\nandroid-junit5 = { id = \"de.mannodermaus.android-junit5\", version.ref = \"android-junit5\" }\nandroid-library = { id = \"com.android.library\", version.ref = \"agp\" }\nanimalsniffer = { id = \"ru.vyarus.animalsniffer\", version.ref = \"animalsniffer\" }\nbinary-compatibility-validator = { id = \"org.jetbrains.kotlinx.binary-compatibility-validator\", version.ref = \"binary-compatibility-validator\" }\nbnd = { id = \"biz.aQute.bnd\", version.ref = \"bnd\" }\nburst = { id = \"app.cash.burst\", version.ref = \"burst\" }\ndokka = { id = \"org.jetbrains.dokka\", version.ref = \"dokka\" }\ngraalvm = { id = \"org.graalvm.buildtools.native\", version.ref = \"graalvm-plugin\" }\nkotlin-jvm = { id = \"org.jetbrains.kotlin.jvm\", version.ref = \"kotlin\" }\nkotlin-multiplatform = { id = \"org.jetbrains.kotlin.multiplatform\", version.ref = \"kotlin\" }\nkotlin-serialization = { id = \"org.jetbrains.kotlin.plugin.serialization\", version.ref = \"kotlin\" }\nksp = { id = \"com.google.devtools.ksp\", version.ref = \"ksp\" }\ngradle-lint = { id = \"com.android.lint\", version.ref = \"lint-gradle\" }\nmaven-publish = { id = \"com.vanniktech.maven.publish.base\", version.ref = \"maven-publish\" }\nmaven-sympathy = { id = \"io.github.usefulness.maven-sympathy\", version.ref = \"maven-sympathy\" }\nmrjar = { id = \"me.champeau.mrjar\", version.ref = \"mrjar\" }\nshadow = { id = \"com.gradleup.shadow\", version.ref = \"shadow-plugin\" }\nspotless = { id = \"com.diffplug.spotless\", version.ref = \"spotless-plugin\" }\njlink = { id = \"com.github.iherasymenko.jlink\", version.ref = \"jlink\" }\nextra-java-module-info = { id = \"org.gradlex.extra-java-module-info\", version.ref = \"extra-java-module-info\" }\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.4.0-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "org.gradle.caching=true\norg.gradle.parallel=true\n\n# Enable configuration cache\norg.gradle.configuration-cache=true\norg.gradle.configuration-cache.problems=fail\norg.gradle.configuration-cache.parallel=true\n\n# Only configure projects reachable from the requested tasks, skipping unrelated subprojects\norg.gradle.isolated-projects=true\n\nandroid.useAndroidX=true\nkotlin.mpp.applyDefaultHierarchyTemplate=false\n\nandroidBuild=false\ngraalBuild=false\nloomBuild=false\ncontainerTests=false\nokhttpModuleTests=false\nokhttpDokka=false\n\n# Increase heap and metaspace to reduce GC pressure when loading 30+ plugin classpaths\norg.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8\n\n# AGP 9.0 Settings\nandroid.builtInKotlin=true\nandroid.newDsl=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "maven-tests/.gitignore",
    "content": "target\n"
  },
  {
    "path": "maven-tests/.mvn/jvm.config",
    "content": ""
  },
  {
    "path": "maven-tests/.mvn/maven.config",
    "content": ""
  },
  {
    "path": "maven-tests/.mvn/wrapper/maven-wrapper.properties",
    "content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\nwrapperVersion=3.3.4\ndistributionType=only-script\ndistributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/4.0.0-rc-5/apache-maven-4.0.0-rc-5-bin.zip\n"
  },
  {
    "path": "maven-tests/mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Apache Maven Wrapper startup batch script, version 3.3.4\n#\n# Optional ENV vars\n# -----------------\n#   JAVA_HOME - location of a JDK home dir, required when download maven via java source\n#   MVNW_REPOURL - repo url base for downloading maven distribution\n#   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven\n#   MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output\n# ----------------------------------------------------------------------------\n\nset -euf\n[ \"${MVNW_VERBOSE-}\" != debug ] || set -x\n\n# OS specific support.\nnative_path() { printf %s\\\\n \"$1\"; }\ncase \"$(uname)\" in\nCYGWIN* | MINGW*)\n  [ -z \"${JAVA_HOME-}\" ] || JAVA_HOME=\"$(cygpath --unix \"$JAVA_HOME\")\"\n  native_path() { cygpath --path --windows \"$1\"; }\n  ;;\nesac\n\n# set JAVACMD and JAVACCMD\nset_java_home() {\n  # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched\n  if [ -n \"${JAVA_HOME-}\" ]; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ]; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n      JAVACCMD=\"$JAVA_HOME/jre/sh/javac\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n      JAVACCMD=\"$JAVA_HOME/bin/javac\"\n\n      if [ ! -x \"$JAVACMD\" ] || [ ! -x \"$JAVACCMD\" ]; then\n        echo \"The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run.\" >&2\n        echo \"JAVA_HOME is set to \\\"$JAVA_HOME\\\", but \\\"\\$JAVA_HOME/bin/java\\\" or \\\"\\$JAVA_HOME/bin/javac\\\" does not exist.\" >&2\n        return 1\n      fi\n    fi\n  else\n    JAVACMD=\"$(\n      'set' +e\n      'unset' -f command 2>/dev/null\n      'command' -v java\n    )\" || :\n    JAVACCMD=\"$(\n      'set' +e\n      'unset' -f command 2>/dev/null\n      'command' -v javac\n    )\" || :\n\n    if [ ! -x \"${JAVACMD-}\" ] || [ ! -x \"${JAVACCMD-}\" ]; then\n      echo \"The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run.\" >&2\n      return 1\n    fi\n  fi\n}\n\n# hash string like Java String::hashCode\nhash_string() {\n  str=\"${1:-}\" h=0\n  while [ -n \"$str\" ]; do\n    char=\"${str%\"${str#?}\"}\"\n    h=$(((h * 31 + $(LC_CTYPE=C printf %d \"'$char\")) % 4294967296))\n    str=\"${str#?}\"\n  done\n  printf %x\\\\n $h\n}\n\nverbose() { :; }\n[ \"${MVNW_VERBOSE-}\" != true ] || verbose() { printf %s\\\\n \"${1-}\"; }\n\ndie() {\n  printf %s\\\\n \"$1\" >&2\n  exit 1\n}\n\ntrim() {\n  # MWRAPPER-139:\n  #   Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.\n  #   Needed for removing poorly interpreted newline sequences when running in more\n  #   exotic environments such as mingw bash on Windows.\n  printf \"%s\" \"${1}\" | tr -d '[:space:]'\n}\n\n# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties\nwhile IFS=\"=\" read -r key value; do\n  case \"${key-}\" in\n  distributionUrl) distributionUrl=$(trim \"${value-}\") ;;\n  distributionSha256Sum) distributionSha256Sum=$(trim \"${value-}\") ;;\n  esac\ndone <\"${0%/*}/.mvn/wrapper/maven-wrapper.properties\"\n[ -n \"${distributionUrl-}\" ] || die \"cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties\"\n\ncase \"${distributionUrl##*/}\" in\nmaven-mvnd-*bin.*)\n  MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/\n  case \"${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)\" in\n  *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;\n  :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;\n  :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;\n  :Linux*x86_64*) distributionPlatform=linux-amd64 ;;\n  *)\n    echo \"Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version\" >&2\n    distributionPlatform=linux-amd64\n    ;;\n  esac\n  distributionUrl=\"${distributionUrl%-bin.*}-$distributionPlatform.zip\"\n  ;;\nmaven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;\n*) MVN_CMD=\"mvn${0##*/mvnw}\" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;\nesac\n\n# apply MVNW_REPOURL and calculate MAVEN_HOME\n# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>\n[ -z \"${MVNW_REPOURL-}\" ] || distributionUrl=\"$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*\"$_MVNW_REPO_PATTERN\"}\"\ndistributionUrlName=\"${distributionUrl##*/}\"\ndistributionUrlNameMain=\"${distributionUrlName%.*}\"\ndistributionUrlNameMain=\"${distributionUrlNameMain%-bin}\"\nMAVEN_USER_HOME=\"${MAVEN_USER_HOME:-${HOME}/.m2}\"\nMAVEN_HOME=\"${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string \"$distributionUrl\")\"\n\nexec_maven() {\n  unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :\n  exec \"$MAVEN_HOME/bin/$MVN_CMD\" \"$@\" || die \"cannot exec $MAVEN_HOME/bin/$MVN_CMD\"\n}\n\nif [ -d \"$MAVEN_HOME\" ]; then\n  verbose \"found existing MAVEN_HOME at $MAVEN_HOME\"\n  exec_maven \"$@\"\nfi\n\ncase \"${distributionUrl-}\" in\n*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;\n*) die \"distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'\" ;;\nesac\n\n# prepare tmp dir\nif TMP_DOWNLOAD_DIR=\"$(mktemp -d)\" && [ -d \"$TMP_DOWNLOAD_DIR\" ]; then\n  clean() { rm -rf -- \"$TMP_DOWNLOAD_DIR\"; }\n  trap clean HUP INT TERM EXIT\nelse\n  die \"cannot create temp dir\"\nfi\n\nmkdir -p -- \"${MAVEN_HOME%/*}\"\n\n# Download and Install Apache Maven\nverbose \"Couldn't find MAVEN_HOME, downloading and installing it ...\"\nverbose \"Downloading from: $distributionUrl\"\nverbose \"Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName\"\n\n# select .zip or .tar.gz\nif ! command -v unzip >/dev/null; then\n  distributionUrl=\"${distributionUrl%.zip}.tar.gz\"\n  distributionUrlName=\"${distributionUrl##*/}\"\nfi\n\n# verbose opt\n__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''\n[ \"${MVNW_VERBOSE-}\" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v\n\n# normalize http auth\ncase \"${MVNW_PASSWORD:+has-password}\" in\n'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;\nhas-password) [ -n \"${MVNW_USERNAME-}\" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;\nesac\n\nif [ -z \"${MVNW_USERNAME-}\" ] && command -v wget >/dev/null; then\n  verbose \"Found wget ... using wget\"\n  wget ${__MVNW_QUIET_WGET:+\"$__MVNW_QUIET_WGET\"} \"$distributionUrl\" -O \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" || die \"wget: Failed to fetch $distributionUrl\"\nelif [ -z \"${MVNW_USERNAME-}\" ] && command -v curl >/dev/null; then\n  verbose \"Found curl ... using curl\"\n  curl ${__MVNW_QUIET_CURL:+\"$__MVNW_QUIET_CURL\"} -f -L -o \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" \"$distributionUrl\" || die \"curl: Failed to fetch $distributionUrl\"\nelif set_java_home; then\n  verbose \"Falling back to use Java to download\"\n  javaSource=\"$TMP_DOWNLOAD_DIR/Downloader.java\"\n  targetZip=\"$TMP_DOWNLOAD_DIR/$distributionUrlName\"\n  cat >\"$javaSource\" <<-END\n\tpublic class Downloader extends java.net.Authenticator\n\t{\n\t  protected java.net.PasswordAuthentication getPasswordAuthentication()\n\t  {\n\t    return new java.net.PasswordAuthentication( System.getenv( \"MVNW_USERNAME\" ), System.getenv( \"MVNW_PASSWORD\" ).toCharArray() );\n\t  }\n\t  public static void main( String[] args ) throws Exception\n\t  {\n\t    setDefault( new Downloader() );\n\t    java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );\n\t  }\n\t}\n\tEND\n  # For Cygwin/MinGW, switch paths to Windows format before running javac and java\n  verbose \" - Compiling Downloader.java ...\"\n  \"$(native_path \"$JAVACCMD\")\" \"$(native_path \"$javaSource\")\" || die \"Failed to compile Downloader.java\"\n  verbose \" - Running Downloader.java ...\"\n  \"$(native_path \"$JAVACMD\")\" -cp \"$(native_path \"$TMP_DOWNLOAD_DIR\")\" Downloader \"$distributionUrl\" \"$(native_path \"$targetZip\")\"\nfi\n\n# If specified, validate the SHA-256 sum of the Maven distribution zip file\nif [ -n \"${distributionSha256Sum-}\" ]; then\n  distributionSha256Result=false\n  if [ \"$MVN_CMD\" = mvnd.sh ]; then\n    echo \"Checksum validation is not supported for maven-mvnd.\" >&2\n    echo \"Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\" >&2\n    exit 1\n  elif command -v sha256sum >/dev/null; then\n    if echo \"$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName\" | sha256sum -c >/dev/null 2>&1; then\n      distributionSha256Result=true\n    fi\n  elif command -v shasum >/dev/null; then\n    if echo \"$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName\" | shasum -a 256 -c >/dev/null 2>&1; then\n      distributionSha256Result=true\n    fi\n  else\n    echo \"Checksum validation was requested but neither 'sha256sum' or 'shasum' are available.\" >&2\n    echo \"Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\" >&2\n    exit 1\n  fi\n  if [ $distributionSha256Result = false ]; then\n    echo \"Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised.\" >&2\n    echo \"If you updated your Maven version, you need to update the specified distributionSha256Sum property.\" >&2\n    exit 1\n  fi\nfi\n\n# unzip and move\nif command -v unzip >/dev/null; then\n  unzip ${__MVNW_QUIET_UNZIP:+\"$__MVNW_QUIET_UNZIP\"} \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -d \"$TMP_DOWNLOAD_DIR\" || die \"failed to unzip\"\nelse\n  tar xzf${__MVNW_QUIET_TAR:+\"$__MVNW_QUIET_TAR\"} \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -C \"$TMP_DOWNLOAD_DIR\" || die \"failed to untar\"\nfi\nprintf %s\\\\n \"$distributionUrl\" >\"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url\"\nmv -- \"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain\" \"$MAVEN_HOME\" || [ -d \"$MAVEN_HOME\" ] || die \"fail to move MAVEN_HOME\"\n\nclean || :\nexec_maven \"$@\"\n"
  },
  {
    "path": "maven-tests/mvnw.cmd",
    "content": "<# : batch portion\n@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Apache Maven Wrapper startup batch script, version 3.3.4\n@REM\n@REM Optional ENV vars\n@REM   MVNW_REPOURL - repo url base for downloading maven distribution\n@REM   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven\n@REM   MVNW_VERBOSE - true: enable verbose log; others: silence the output\n@REM ----------------------------------------------------------------------------\n\n@IF \"%__MVNW_ARG0_NAME__%\"==\"\" (SET __MVNW_ARG0_NAME__=%~nx0)\n@SET __MVNW_CMD__=\n@SET __MVNW_ERROR__=\n@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%\n@SET PSModulePath=\n@FOR /F \"usebackq tokens=1* delims==\" %%A IN (`powershell -noprofile \"& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}\"`) DO @(\n  IF \"%%A\"==\"MVN_CMD\" (set __MVNW_CMD__=%%B) ELSE IF \"%%B\"==\"\" (echo %%A) ELSE (echo %%A=%%B)\n)\n@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%\n@SET __MVNW_PSMODULEP_SAVE=\n@SET __MVNW_ARG0_NAME__=\n@SET MVNW_USERNAME=\n@SET MVNW_PASSWORD=\n@IF NOT \"%__MVNW_CMD__%\"==\"\" (%__MVNW_CMD__% %*)\n@echo Cannot start maven from wrapper >&2 && exit /b 1\n@GOTO :EOF\n: end batch / begin powershell #>\n\n$ErrorActionPreference = \"Stop\"\nif ($env:MVNW_VERBOSE -eq \"true\") {\n  $VerbosePreference = \"Continue\"\n}\n\n# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties\n$distributionUrl = (Get-Content -Raw \"$scriptDir/.mvn/wrapper/maven-wrapper.properties\" | ConvertFrom-StringData).distributionUrl\nif (!$distributionUrl) {\n  Write-Error \"cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties\"\n}\n\nswitch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {\n  \"maven-mvnd-*\" {\n    $USE_MVND = $true\n    $distributionUrl = $distributionUrl -replace '-bin\\.[^.]*$',\"-windows-amd64.zip\"\n    $MVN_CMD = \"mvnd.cmd\"\n    break\n  }\n  default {\n    $USE_MVND = $false\n    $MVN_CMD = $script -replace '^mvnw','mvn'\n    break\n  }\n}\n\n# apply MVNW_REPOURL and calculate MAVEN_HOME\n# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>\nif ($env:MVNW_REPOURL) {\n  $MVNW_REPO_PATTERN = if ($USE_MVND) { \"/org/apache/maven/\" } else { \"/maven/mvnd/\" }\n  $distributionUrl = \"$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')\"\n}\n$distributionUrlName = $distributionUrl -replace '^.*/',''\n$distributionUrlNameMain = $distributionUrlName -replace '\\.[^.]*$','' -replace '-bin$',''\n$MAVEN_HOME_PARENT = \"$HOME/.m2/wrapper/dists/$distributionUrlNameMain\"\nif ($env:MAVEN_USER_HOME) {\n  $MAVEN_HOME_PARENT = \"$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain\"\n}\n$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString(\"x2\")}) -join ''\n$MAVEN_HOME = \"$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME\"\n\nif (Test-Path -Path \"$MAVEN_HOME\" -PathType Container) {\n  Write-Verbose \"found existing MAVEN_HOME at $MAVEN_HOME\"\n  Write-Output \"MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD\"\n  exit $?\n}\n\nif (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {\n  Write-Error \"distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl\"\n}\n\n# prepare tmp dir\n$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile\n$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path \"$TMP_DOWNLOAD_DIR_HOLDER.dir\"\n$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null\ntrap {\n  if ($TMP_DOWNLOAD_DIR.Exists) {\n    try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }\n    catch { Write-Warning \"Cannot remove $TMP_DOWNLOAD_DIR\" }\n  }\n}\n\nNew-Item -Itemtype Directory -Path \"$MAVEN_HOME_PARENT\" -Force | Out-Null\n\n# Download and Install Apache Maven\nWrite-Verbose \"Couldn't find MAVEN_HOME, downloading and installing it ...\"\nWrite-Verbose \"Downloading from: $distributionUrl\"\nWrite-Verbose \"Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName\"\n\n$webclient = New-Object System.Net.WebClient\nif ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {\n  $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)\n}\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n$webclient.DownloadFile($distributionUrl, \"$TMP_DOWNLOAD_DIR/$distributionUrlName\") | Out-Null\n\n# If specified, validate the SHA-256 sum of the Maven distribution zip file\n$distributionSha256Sum = (Get-Content -Raw \"$scriptDir/.mvn/wrapper/maven-wrapper.properties\" | ConvertFrom-StringData).distributionSha256Sum\nif ($distributionSha256Sum) {\n  if ($USE_MVND) {\n    Write-Error \"Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\"\n  }\n  Import-Module $PSHOME\\Modules\\Microsoft.PowerShell.Utility -Function Get-FileHash\n  if ((Get-FileHash \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {\n    Write-Error \"Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property.\"\n  }\n}\n\n# unzip and move\nExpand-Archive \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -DestinationPath \"$TMP_DOWNLOAD_DIR\" | Out-Null\nRename-Item -Path \"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain\" -NewName $MAVEN_HOME_NAME | Out-Null\ntry {\n  Move-Item -Path \"$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME\" -Destination $MAVEN_HOME_PARENT | Out-Null\n} catch {\n  if (! (Test-Path -Path \"$MAVEN_HOME\" -PathType Container)) {\n    Write-Error \"fail to move MAVEN_HOME\"\n  }\n} finally {\n  try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }\n  catch { Write-Warning \"Cannot remove $TMP_DOWNLOAD_DIR\" }\n}\n\nWrite-Output \"MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD\"\n"
  },
  {
    "path": "maven-tests/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <groupId>com.squareup.okhttp3</groupId>\n  <artifactId>maven-tests</artifactId>\n  <version>1.0.0-SNAPSHOT</version>\n\n  <name>maven-tests</name>\n  <description>A simple maven-test.</description>\n\n  <properties>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <maven.compiler.source>21</maven.compiler.source>\n    <maven.compiler.target>21</maven.compiler.target>\n  </properties>\n\n  <dependencyManagement>\n    <dependencies>\n      <dependency>\n        <groupId>com.squareup.okhttp3</groupId>\n        <artifactId>okhttp-bom</artifactId>\n        <version>[5.2.0-SNAPSHOT,6.0.0-SNAPSHOT)</version>\n        <type>pom</type>\n        <scope>import</scope>\n      </dependency>\n    </dependencies>\n  </dependencyManagement>\n\n  <dependencies>\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>4.13.2</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>com.squareup.okhttp3</groupId>\n<!--      <artifactId>okhttp</artifactId>-->\n      <artifactId>okhttp-jvm</artifactId>\n    </dependency>\n\n    <dependency>\n      <groupId>com.squareup.okhttp3</groupId>\n      <artifactId>mockwebserver3</artifactId>\n    </dependency>\n\n    <dependency>\n      <groupId>com.squareup.okhttp3</groupId>\n      <artifactId>logging-interceptor</artifactId>\n    </dependency>\n  </dependencies>\n\n  <reporting>\n    <plugins>\n      <plugin>\n        <artifactId>maven-project-info-reports-plugin</artifactId>\n      </plugin>\n    </plugins>\n  </reporting>\n</project>\n"
  },
  {
    "path": "maven-tests/src/main/java/com/squareup/okhttp3/maventest/SampleHttpClient.java",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.squareup.okhttp3.maventest;\n\nimport java.io.IOException;\n\nimport okhttp3.Headers;\nimport okhttp3.HttpUrl;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic class SampleHttpClient {\n  private final OkHttpClient client;\n\n  public SampleHttpClient() {\n    client = new OkHttpClient.Builder().build();\n  }\n\n  public void makeCall(HttpUrl url) throws IOException {\n    try (Response response = client.newCall(new Request(url, Headers.EMPTY, \"GET\", null)).execute()) {\n      System.out.println(response.body().string());\n    }\n  }\n}\n"
  },
  {
    "path": "maven-tests/src/test/java/com/squareup/okhttp3/maventest/AppTest.java",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.squareup.okhttp3.maventest;\n\nimport mockwebserver3.MockResponse;\nimport mockwebserver3.MockWebServer;\nimport okhttp3.Headers;\nimport org.junit.Test;\n\nimport java.io.IOException;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest {\n  private final MockWebServer mockWebServer = new MockWebServer();\n\n  @Test\n  public void testApp() throws IOException {\n    mockWebServer.enqueue(new MockResponse(200, Headers.of(), \"Hello, Maven!\"));\n    mockWebServer.start();\n\n    new SampleHttpClient().makeCall(mockWebServer.url(\"/\"));\n  }\n}\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: OkHttp\nsite_url: https://square.github.io/okhttp/\nrepo_name: OkHttp\nrepo_url: https://github.com/square/okhttp\nsite_description: \"Square’s meticulous HTTP client for the JVM, Android, and GraalVM\"\nsite_author: Square, Inc.\nremote_branch: gh-pages\nedit_uri: \"\"\n\ncopyright: 'Copyright &copy; 2022 Block, Inc.'\n\ntheme:\n  name: 'material'\n  favicon: assets/images/icon-square.png\n  logo: assets/images/icon-square.png\n  palette:\n    - media: \"(prefers-color-scheme: light)\"\n      scheme: default\n      primary: teal\n      accent: blue\n      toggle:\n        icon: octicons/sun-24\n        name: \"Switch to Dark Mode\"\n    - media: \"(prefers-color-scheme: dark)\"\n      scheme: slate\n      primary: teal\n      accent: blue\n      toggle:\n        icon: octicons/moon-24\n        name: \"Switch to Light Mode\"\n  features:\n  - navigation.tabs\n\nextra_css:\n  - 'assets/css/app.css'\n\nmarkdown_extensions:\n  - smarty\n  - footnotes\n  - meta\n  - toc:\n      permalink: true\n  - attr_list\n  - pymdownx.betterem:\n      smart_enable: all\n  - pymdownx.caret\n  - pymdownx.emoji:\n      emoji_index: !!python/name:material.extensions.emoji.twemoji\n      emoji_generator: !!python/name:material.extensions.emoji.to_svg\n  - pymdownx.inlinehilite\n  - pymdownx.magiclink\n  - pymdownx.smartsymbols\n  - pymdownx.superfences\n  - pymdownx.tilde\n  - pymdownx.tabbed:\n      alternate_style: true\n  - tables\n\nplugins:\n  - search\n  - redirects:\n      redirect_maps:\n        # Redirect all feature pages to features/*\n        'caching.md': 'features/caching.md'\n        'calls.md': 'features/calls.md'\n        'connections.md': 'features/connections.md'\n        'events.md': 'features/events.md'\n        'https.md': 'features/events.md'\n        'interceptors.md': 'features/interceptors.md'\n        'r8_proguard.md': 'features/r8_proguard.md'\n        # Redirect all Security pages to security/*\n        'security.md': 'security/security.md'\n        'security_providers.md': 'security/security_providers.md'\n        'tls_configuration_history.md': 'security/tls_configuration_history.md'\n        # Redirect all changelog pages to changelog/*\n        'changelog.md': 'changelogs/changelog.md'\n        'upgrading_to_okhttp_4.md': 'changelogs/upgrading_to_okhttp_4.md'\n        'changelog_3x.md': 'changelogs/changelog_3x.md'\n        'changelog_2x.md': 'changelogs/changelog_2x.md'\n        'changelog_1x.md': 'changelogs/changelog_1x.md'\n        # Redirect all contributing pages to contribute/*\n        'contributing.md': 'contribute/contributing.md'\n        'code_of_conduct.md': 'contribute/code_of_conduct.md'\n        'concurrency.md': 'contribute/concurrency.md'\n        'debug_logging.md': 'contribute/debug_logging.md'\n\nnav:\n  - 'Overview':\n    - 'Overview': index.md\n    - 'Stack Overflow': https://stackoverflow.com/questions/tagged/okhttp?sort=active\n  - 'Features':\n    - 'Calls': features/calls.md\n    - 'Caching': features/caching.md\n    - 'Connections': features/connections.md\n    - 'Events': features/events.md\n    - 'HTTPS': features/https.md\n    - 'Interceptors': features/interceptors.md\n    - 'R8/Proguard': features/r8_proguard.md\n  - 'Recipes': recipes.md\n  - 'Security':\n    - 'Security': security/security.md\n    - 'Providers': security/security_providers.md\n    - 'Configuration History': security/tls_configuration_history.md\n  - 'Works with OkHttp': works_with_okhttp.md\n  - 'API': 5.x/okhttp/okhttp3/\n  - 'Change Logs':\n    - 'Change Log': changelogs/changelog.md\n    - '4.x Change Log': changelogs/changelog_4x.md\n    - 'Upgrading to OkHttp 4': changelogs/upgrading_to_okhttp_4.md\n    - '3.x Change Log': changelogs/changelog_3x.md\n    - '2.x Change Log': changelogs/changelog_2x.md\n    - '1.x Change Log': changelogs/changelog_1x.md\n  - 'Contributing':\n    - 'Contributing': contribute/contributing.md\n    - 'Code of Conduct': contribute/code_of_conduct.md\n    - 'Concurrency': contribute/concurrency.md\n    - 'Debug Logging': contribute/debug_logging.md\n"
  },
  {
    "path": "mockwebserver/Module.md",
    "content": "# Module mockwebserver\n\nA scriptable web server for testing HTTP clients.\n"
  },
  {
    "path": "mockwebserver/README.md",
    "content": "MockWebServer\n=============\n\nA scriptable web server for testing HTTP clients\n\n\n### Motivation\n\nThis library makes it easy to test that your app Does The Right Thing when it\nmakes HTTP and HTTPS calls. It lets you specify which responses to return and\nthen verify that requests were made as expected.\n\nBecause it exercises your full HTTP stack, you can be confident that you're\ntesting everything. You can even copy & paste HTTP responses from your real web\nserver to create representative test cases. Or test that your code survives in\nawkward-to-reproduce situations like 500 errors or slow-loading responses.\n\n\n### Example\n\nUse MockWebServer the same way that you use mocking frameworks like\n[Mockito](https://github.com/mockito/mockito):\n\n1. Script the mocks.\n2. Run application code.\n3. Verify that the expected requests were made.\n\nHere's a complete example:\n\n### Java\n```java\npublic void test() throws Exception {\n  // Create a MockWebServer. These are lean enough that you can create a new\n  // instance for every unit test.\n  MockWebServer server = new MockWebServer();\n\n  // Schedule some responses.\n  server.enqueue(new MockResponse.Builder()\n      .body(\"hello, world!\")\n      .build());\n  server.enqueue(new MockResponse.Builder()\n      .body(\"sup, bra?\")\n      .build());\n  server.enqueue(new MockResponse.Builder()\n      .body(\"yo dog\")\n      .build());\n\n  // Start the server.\n  server.start();\n\n  // Ask the server for its URL. You'll need this to make HTTP requests.\n  HttpUrl baseUrl = server.url(\"/v1/chat/\");\n\n  // Exercise your application code, which should make those HTTP requests.\n  // Responses are returned in the same order that they are enqueued.\n  Chat chat = new Chat(baseUrl);\n\n  chat.loadMore();\n  assertEquals(\"hello, world!\", chat.messages());\n\n  chat.loadMore();\n  chat.loadMore();\n  assertEquals(\"\"\n      + \"hello, world!\\n\"\n      + \"sup, bra?\\n\"\n      + \"yo dog\", chat.messages());\n\n  // Optional: confirm that your app made the HTTP requests you were expecting.\n  RecordedRequest request1 = server.takeRequest();\n  assertEquals(\"/v1/chat/messages/\", request1.getUrl().encodedPath());\n  assertNotNull(request1.getHeaders().get(\"Authorization\"));\n\n  RecordedRequest request2 = server.takeRequest();\n  assertEquals(\"/v1/chat/messages/2\", request2.getUrl().encodedPath());\n\n  RecordedRequest request3 = server.takeRequest();\n  assertEquals(\"/v1/chat/messages/3\", request3.getUrl().encodedPath());\n\n  // Shut down the server. Instances cannot be reused.\n  server.close();\n}\n```\n\n### Kotlin\n```kotlin\nfun test() {\n  // Create a MockWebServer. These are lean enough that you can create a new\n  // instance for every unit test.\n  val server = MockWebServer()\n\n  // Schedule some responses.\n  server.enqueue(MockResponse(body = \"hello, world!\"))\n  server.enqueue(MockResponse(body = \"sup, bra?\"))\n  server.enqueue(MockResponse(body = \"yo dog\"))\n\n  // Start the server.\n  server.start()\n\n  // Ask the server for its URL. You'll need this to make HTTP requests.\n  val baseUrl = server.url(\"/v1/chat/\")\n\n  // Exercise your application code, which should make those HTTP requests.\n  // Responses are returned in the same order that they are enqueued.\n  val chat = Chat(baseUrl)\n\n  chat.loadMore()\n  assertEquals(\"hello, world!\", chat.messages())\n\n  chat.loadMore()\n  chat.loadMore()\n  assertEquals(\"\"\n    + \"hello, world!\\n\"\n    + \"sup, bra?\\n\"\n    + \"yo dog\", chat.messages())\n\n  // Optional: confirm that your app made the HTTP requests you were expecting.\n  val request1 = server.takeRequest()\n  assertEquals(\"/v1/chat/messages/\", request1.url.encodedPath)\n  assertNotNull(request1.headers[\"Authorization\"])\n\n  val request2 = server.takeRequest()\n  assertEquals(\"/v1/chat/messages/2\", request2.url.encodedPath)\n\n  val request3 = server.takeRequest()\n  assertEquals(\"/v1/chat/messages/3\", request3.url.encodedPath)\n\n  // Shut down the server. Instances cannot be reused.\n  server.close()\n}\n```\n\nYour unit tests might move the `server` into a field so you can shut it down\nfrom your test's `tearDown()`.\n\n### API\n\n#### MockResponse\n\nMock responses default to an empty response body and a `200` status code.\nYou can set a custom body with a string, input stream or byte array. Also\nadd headers with a fluent builder API.\n\n### Java\n```java\nMockResponse response = new MockResponse.Builder()\n    .addHeader(\"Content-Type\", \"application/json; charset=utf-8\")\n    .addHeader(\"Cache-Control\", \"no-cache\")\n    .body(\"{}\")\n    .build();\n```\n\n### Kotlin\n```kotlin\nval response = MockResponse.Builder()\n  .addHeader(\"Content-Type\", \"application/json; charset=utf-8\")\n  .addHeader(\"Cache-Control\", \"no-cache\")\n  .body(\"{}\")\n  .build()\n```\n\nMockResponse can be used to simulate a slow network. This is useful for\ntesting timeouts and interactive testing.\n\n### Java\n```java\nMockResponse response = new MockResponse.Builder()\n    .throttleBody(1024, 1, TimeUnit.SECONDS)\n    .build();\n```\n\n### Kotlin\n```kotlin\nval response = MockResponse.Builder()\n  .throttleBody(1024, 1, TimeUnit.SECONDS)\n  .build()\n```\n\n#### RecordedRequest\n\nVerify requests by their method, path, HTTP version, body, and headers.\n\n### Java\n```java\nRecordedRequest request = server.takeRequest();\nassertEquals(\"POST /v1/chat/send HTTP/1.1\", request.getRequestLine());\nassertEquals(\"application/json; charset=utf-8\", request.getHeaders().get(\"Content-Type\"));\nassertEquals(\"{}\", request.getBody().readUtf8());\n```\n\n### Kotlin\n```kotlin\nval request = server.takeRequest()\nassertEquals(\"POST /v1/chat/send HTTP/1.1\", request.requestLine)\nassertEquals(\"application/json; charset=utf-8\", request.headers[\"Content-Type\"])\nassertEquals(\"{}\", request.body!!.utf8())\n```\n\n#### Dispatcher\n\nBy default MockWebServer uses a queue to specify a series of responses. Use a\nDispatcher (`import okhttp3.mockwebserver.Dispatcher`) to handle requests using another policy. One natural policy is to\ndispatch on the request path.\nYou can, for example, filter the request instead of using `server.enqueue()`.\n\n### Java\n```java\nfinal Dispatcher dispatcher = new Dispatcher() {\n\n    @Override\n    public MockResponse dispatch (RecordedRequest request) throws InterruptedException {\n\n        switch (request.getUrl().encodedPath()) {\n          case \"/v1/login/auth/\":\n              return new MockResponse.Builder()\n                  .code(200)\n                  .build();\n          case \"/v1/check/version/\":\n              return new MockResponse.Builder()\n                  .code(200)\n                  .body(\"version=9\")\n                  .build();\n          case \"/v1/profile/info\":\n            return new MockResponse.Builder()\n                .code(200)\n                .body(\"{\\\\\\\"info\\\\\\\":{\\\\\\\"name\\\":\\\"Lucas Albuquerque\\\",\\\"age\\\":\\\"21\\\",\\\"gender\\\":\\\"male\\\"}}\")\n                .build();\n        }\n        return new MockResponse.Builder()\n            .code(404)\n            .build();\n    }\n};\nserver.setDispatcher(dispatcher);\n```\n\n### Kotlin\n```kotlin\nval dispatcher = object : Dispatcher() {\n  override fun dispatch(request: RecordedRequest): MockResponse {\n    return when (request.url.encodedPath) {\n      \"/v1/login/auth/\" -> {\n        MockResponse.Builder()\n          .code(200)\n          .build()\n      }\n      \"/v1/check/version/\" -> {\n        MockResponse.Builder()\n          .code(200)\n          .body(\"version=9\")\n          .build()\n      }\n      \"/v1/profile/info\" -> {\n        MockResponse.Builder()\n          .code(200)\n          .body(\"{\\\\\\\"info\\\\\\\":{\\\\\\\"name\\\":\\\"Lucas Albuquerque\\\",\\\"age\\\":\\\"21\\\",\\\"gender\\\":\\\"male\\\"}}\")\n          .build()\n      }\n      else -> {\n        MockResponse.Builder()\n          .code(404)\n          .build()\n      }\n    }\n  }\n}\n\nserver.dispatcher = dispatcher\n```\n\n### Download\n\n```kotlin\ntestImplementation(\"com.squareup.okhttp3:mockwebserver3:5.3.0\")\n```\n\n### License\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "mockwebserver/api/mockwebserver3.api",
    "content": "public abstract class mockwebserver3/Dispatcher : java/io/Closeable {\n\tpublic fun <init> ()V\n\tpublic fun close ()V\n\tpublic abstract fun dispatch (Lmockwebserver3/RecordedRequest;)Lmockwebserver3/MockResponse;\n\tpublic fun peek ()Lmockwebserver3/MockResponse;\n}\n\npublic final class mockwebserver3/MockResponse {\n\tpublic fun <init> (ILokhttp3/Headers;Ljava/lang/String;)V\n\tpublic synthetic fun <init> (ILokhttp3/Headers;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic synthetic fun <init> (Lmockwebserver3/MockResponse$Builder;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun getBody ()Lmockwebserver3/MockResponseBody;\n\tpublic final fun getBodyDelayNanos ()J\n\tpublic final fun getCode ()I\n\tpublic final fun getDoNotReadRequestBody ()Z\n\tpublic final fun getFailHandshake ()Z\n\tpublic final fun getHeaders ()Lokhttp3/Headers;\n\tpublic final fun getHeadersDelayNanos ()J\n\tpublic final fun getInTunnel ()Z\n\tpublic final fun getInformationalResponses ()Ljava/util/List;\n\tpublic final fun getMessage ()Ljava/lang/String;\n\tpublic final fun getOnRequestBody ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getOnRequestStart ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getOnResponseBody ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getOnResponseEnd ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getOnResponseStart ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getPushPromises ()Ljava/util/List;\n\tpublic final fun getSettings ()Lokhttp3/internal/http2/Settings;\n\tpublic final fun getShutdownServer ()Z\n\tpublic final fun getSocketHandler ()Lmockwebserver3/SocketHandler;\n\tpublic final fun getStatus ()Ljava/lang/String;\n\tpublic final fun getThrottleBytesPerPeriod ()J\n\tpublic final fun getThrottlePeriodNanos ()J\n\tpublic final fun getTrailers ()Lokhttp3/Headers;\n\tpublic final fun getTrailersDelayNanos ()J\n\tpublic final fun getWebSocketListener ()Lokhttp3/WebSocketListener;\n\tpublic final fun newBuilder ()Lmockwebserver3/MockResponse$Builder;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class mockwebserver3/MockResponse$Builder : java/lang/Cloneable {\n\tpublic fun <init> ()V\n\tpublic final fun add100Continue ()Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun addHeader (Ljava/lang/String;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun addHeader (Ljava/lang/String;Ljava/lang/Object;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun addHeaderLenient (Ljava/lang/String;Ljava/lang/Object;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun addInformationalResponse (Lmockwebserver3/MockResponse;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun addPush (Lmockwebserver3/PushPromise;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun body (Ljava/lang/String;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun body (Lmockwebserver3/MockResponseBody;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun body (Lokio/Buffer;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun bodyDelay (JLjava/util/concurrent/TimeUnit;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun build ()Lmockwebserver3/MockResponse;\n\tpublic final fun chunkedBody (Ljava/lang/String;I)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun chunkedBody (Lokio/Buffer;I)Lmockwebserver3/MockResponse$Builder;\n\tpublic static synthetic fun chunkedBody$default (Lmockwebserver3/MockResponse$Builder;Ljava/lang/String;IILjava/lang/Object;)Lmockwebserver3/MockResponse$Builder;\n\tpublic static synthetic fun chunkedBody$default (Lmockwebserver3/MockResponse$Builder;Lokio/Buffer;IILjava/lang/Object;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun clearHeaders ()Lmockwebserver3/MockResponse$Builder;\n\tpublic synthetic fun clone ()Ljava/lang/Object;\n\tpublic fun clone ()Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun code (I)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun doNotReadRequestBody ()Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun failHandshake ()Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun getBody ()Lmockwebserver3/MockResponseBody;\n\tpublic final fun getBodyDelayNanos ()J\n\tpublic final fun getCode ()I\n\tpublic final fun getDoNotReadRequestBody ()Z\n\tpublic final fun getFailHandshake ()Z\n\tpublic final fun getHeaders ()Lokhttp3/Headers;\n\tpublic final fun getHeadersDelayNanos ()J\n\tpublic final fun getInTunnel ()Z\n\tpublic final fun getInformationalResponses ()Ljava/util/List;\n\tpublic final fun getOnRequestBody ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getOnRequestStart ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getOnResponseBody ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getOnResponseEnd ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getOnResponseStart ()Lmockwebserver3/SocketEffect;\n\tpublic final fun getPushPromises ()Ljava/util/List;\n\tpublic final fun getSettings ()Lokhttp3/internal/http2/Settings;\n\tpublic final fun getShutdownServer ()Z\n\tpublic final fun getSocketHandler ()Lmockwebserver3/SocketHandler;\n\tpublic final fun getStatus ()Ljava/lang/String;\n\tpublic final fun getThrottleBytesPerPeriod ()J\n\tpublic final fun getThrottlePeriodNanos ()J\n\tpublic final fun getTrailers ()Lokhttp3/Headers;\n\tpublic final fun getTrailersDelayNanos ()J\n\tpublic final fun getWebSocketListener ()Lokhttp3/WebSocketListener;\n\tpublic final fun headers (Lokhttp3/Headers;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun headersDelay (JLjava/util/concurrent/TimeUnit;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun inTunnel ()Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun onRequestBody (Lmockwebserver3/SocketEffect;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun onRequestStart (Lmockwebserver3/SocketEffect;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun onResponseBody (Lmockwebserver3/SocketEffect;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun onResponseEnd (Lmockwebserver3/SocketEffect;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun onResponseStart (Lmockwebserver3/SocketEffect;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun removeHeader (Ljava/lang/String;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun setHeader (Ljava/lang/String;Ljava/lang/Object;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun settings (Lokhttp3/internal/http2/Settings;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun shutdownServer (Z)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun socketHandler (Lmockwebserver3/SocketHandler;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun status (Ljava/lang/String;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun throttleBody (JJLjava/util/concurrent/TimeUnit;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun trailers (Lokhttp3/Headers;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun trailersDelay (JLjava/util/concurrent/TimeUnit;)Lmockwebserver3/MockResponse$Builder;\n\tpublic final fun webSocketUpgrade (Lokhttp3/WebSocketListener;)Lmockwebserver3/MockResponse$Builder;\n}\n\npublic abstract interface class mockwebserver3/MockResponseBody {\n\tpublic abstract fun getContentLength ()J\n\tpublic abstract fun writeTo (Lokio/BufferedSink;)V\n}\n\npublic final class mockwebserver3/MockWebServer : java/io/Closeable {\n\tpublic fun <init> ()V\n\tpublic fun close ()V\n\tpublic final fun enqueue (Lmockwebserver3/MockResponse;)V\n\tpublic final fun getBodyLimit ()J\n\tpublic final fun getDispatcher ()Lmockwebserver3/Dispatcher;\n\tpublic final fun getHostName ()Ljava/lang/String;\n\tpublic final fun getPort ()I\n\tpublic final fun getProtocolNegotiationEnabled ()Z\n\tpublic final fun getProtocols ()Ljava/util/List;\n\tpublic final fun getProxyAddress ()Ljava/net/Proxy;\n\tpublic final fun getRequestCount ()I\n\tpublic final fun getServerSocketFactory ()Ljavax/net/ServerSocketFactory;\n\tpublic final fun getSocketAddress ()Ljava/net/InetSocketAddress;\n\tpublic final fun getStarted ()Z\n\tpublic final fun noClientAuth ()V\n\tpublic final fun requestClientAuth ()V\n\tpublic final fun requireClientAuth ()V\n\tpublic final fun setBodyLimit (J)V\n\tpublic final fun setDispatcher (Lmockwebserver3/Dispatcher;)V\n\tpublic final fun setProtocolNegotiationEnabled (Z)V\n\tpublic final fun setProtocols (Ljava/util/List;)V\n\tpublic final fun setServerSocketFactory (Ljavax/net/ServerSocketFactory;)V\n\tpublic final fun start ()V\n\tpublic final fun start (I)V\n\tpublic final fun start (Ljava/net/InetAddress;I)V\n\tpublic static synthetic fun start$default (Lmockwebserver3/MockWebServer;IILjava/lang/Object;)V\n\tpublic final fun takeRequest ()Lmockwebserver3/RecordedRequest;\n\tpublic final fun takeRequest (JLjava/util/concurrent/TimeUnit;)Lmockwebserver3/RecordedRequest;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun url (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun useHttps (Ljavax/net/ssl/SSLSocketFactory;)V\n}\n\npublic final class mockwebserver3/PushPromise {\n\tpublic fun <init> (Ljava/lang/String;Ljava/lang/String;Lokhttp3/Headers;Lmockwebserver3/MockResponse;)V\n\tpublic final fun getHeaders ()Lokhttp3/Headers;\n\tpublic final fun getMethod ()Ljava/lang/String;\n\tpublic final fun getPath ()Ljava/lang/String;\n\tpublic final fun getResponse ()Lmockwebserver3/MockResponse;\n}\n\npublic class mockwebserver3/QueueDispatcher : mockwebserver3/Dispatcher {\n\tpublic fun <init> ()V\n\tpublic fun clear ()V\n\tpublic fun close ()V\n\tpublic fun dispatch (Lmockwebserver3/RecordedRequest;)Lmockwebserver3/MockResponse;\n\tpublic fun enqueue (Lmockwebserver3/MockResponse;)V\n\tprotected final fun getResponseQueue ()Ljava/util/concurrent/BlockingQueue;\n\tpublic fun peek ()Lmockwebserver3/MockResponse;\n\tpublic fun setFailFast (Lmockwebserver3/MockResponse;)V\n\tpublic fun setFailFast (Z)V\n}\n\npublic final class mockwebserver3/RecordedRequest {\n\tpublic fun <init> (IILokhttp3/Handshake;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lokhttp3/HttpUrl;Lokhttp3/Headers;Lokio/ByteString;JLjava/util/List;Ljava/io/IOException;)V\n\tpublic synthetic fun <init> (IILokhttp3/Handshake;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lokhttp3/HttpUrl;Lokhttp3/Headers;Lokio/ByteString;JLjava/util/List;Ljava/io/IOException;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun getBody ()Lokio/ByteString;\n\tpublic final fun getBodySize ()J\n\tpublic final fun getChunkSizes ()Ljava/util/List;\n\tpublic final fun getConnectionIndex ()I\n\tpublic final fun getExchangeIndex ()I\n\tpublic final fun getFailure ()Ljava/io/IOException;\n\tpublic final fun getHandshake ()Lokhttp3/Handshake;\n\tpublic final fun getHandshakeServerNames ()Ljava/util/List;\n\tpublic final fun getHeaders ()Lokhttp3/Headers;\n\tpublic final fun getMethod ()Ljava/lang/String;\n\tpublic final fun getRequestLine ()Ljava/lang/String;\n\tpublic final fun getTarget ()Ljava/lang/String;\n\tpublic final fun getUrl ()Lokhttp3/HttpUrl;\n\tpublic final fun getVersion ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic abstract interface class mockwebserver3/SocketEffect {\n}\n\npublic final class mockwebserver3/SocketEffect$CloseSocket : mockwebserver3/SocketEffect {\n\tpublic fun <init> ()V\n\tpublic fun <init> (ZZZ)V\n\tpublic synthetic fun <init> (ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun getCloseSocket ()Z\n\tpublic final fun getShutdownInput ()Z\n\tpublic final fun getShutdownOutput ()Z\n}\n\npublic final class mockwebserver3/SocketEffect$CloseStream : mockwebserver3/SocketEffect {\n\tpublic fun <init> ()V\n\tpublic fun <init> (I)V\n\tpublic synthetic fun <init> (IILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun getHttp2ErrorCode ()I\n}\n\npublic final class mockwebserver3/SocketEffect$ShutdownConnection : mockwebserver3/SocketEffect {\n\tpublic static final field INSTANCE Lmockwebserver3/SocketEffect$ShutdownConnection;\n}\n\npublic final class mockwebserver3/SocketEffect$Stall : mockwebserver3/SocketEffect {\n\tpublic static final field INSTANCE Lmockwebserver3/SocketEffect$Stall;\n}\n\npublic abstract interface class mockwebserver3/SocketHandler {\n\tpublic abstract fun handle (Lokio/Socket;)V\n}\n\n"
  },
  {
    "path": "mockwebserver/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyJavaModules(\"mockwebserver3\")\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(projects.okhttpTls)\n  testImplementation(projects.mockwebserver3Junit5)\n  testImplementation(libs.junit)\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n  testImplementation(libs.assertk)\n}\n\nkotlin {\n  explicitApi()\n}\n"
  },
  {
    "path": "mockwebserver/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule mockwebserver3 {\n  requires okhttp3;\n  exports mockwebserver3;\n  requires java.logging;\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/Dispatcher.kt",
    "content": "/*\n * Copyright (C) 2012 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3\n\nimport java.io.Closeable\n\n/** Handler for mock server requests. */\npublic abstract class Dispatcher : Closeable {\n  /**\n   * Returns a response to satisfy `request`. This method may block (for instance, to wait on\n   * a CountdownLatch).\n   */\n  @Throws(InterruptedException::class)\n  public abstract fun dispatch(request: RecordedRequest): MockResponse\n\n  /**\n   * Returns an early guess of the next response, used for policy on how an incoming request should\n   * be received. The default implementation returns an empty response. Mischievous implementations\n   * can return other values to test HTTP edge cases, such as unhappy socket policies or throttled\n   * request bodies.\n   */\n  public open fun peek(): MockResponse = MockResponse()\n\n  /**\n   * Release any resources held by this dispatcher. Any requests that are currently being dispatched\n   * should return immediately. Responses returned after shutdown will not be transmitted: their\n   * socket connections have already been closed.\n   */\n  public open override fun close() {}\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/MockResponse.kt",
    "content": "/*\n * Copyright (C) 2011 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\n  \"CANNOT_OVERRIDE_INVISIBLE_MEMBER\",\n  \"INVISIBLE_MEMBER\",\n  \"INVISIBLE_REFERENCE\",\n  \"ktlint:standard:property-naming\",\n)\n\npackage mockwebserver3\n\nimport java.util.concurrent.TimeUnit\nimport mockwebserver3.SocketEffect.CloseStream\nimport mockwebserver3.internal.toMockResponseBody\nimport okhttp3.Headers\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.WebSocketListener\nimport okhttp3.internal.addHeaderLenient\nimport okhttp3.internal.http2.ErrorCode\nimport okhttp3.internal.http2.Settings\nimport okio.Buffer\n\n/** A scripted response to be replayed by the mock web server. */\npublic class MockResponse {\n  /** Returns the HTTP response line, such as \"HTTP/1.1 200 OK\". */\n  public val status: String\n\n  public val code: Int\n    get() {\n      val statusParts = status.split(' ', limit = 3)\n      require(statusParts.size >= 2) { \"Unexpected status: $status\" }\n      return statusParts[1].toInt()\n    }\n\n  public val message: String\n    get() {\n      val statusParts = status.split(' ', limit = 3)\n      require(statusParts.size >= 2) { \"Unexpected status: $status\" }\n      return statusParts[2]\n    }\n\n  public val headers: Headers\n  public val trailers: Headers\n\n  // At most one of (body,webSocketListener,streamHandler) is non-null.\n  public val body: MockResponseBody?\n  public val webSocketListener: WebSocketListener?\n  public val socketHandler: SocketHandler?\n\n  public val inTunnel: Boolean\n  public val informationalResponses: List<MockResponse>\n\n  public val throttleBytesPerPeriod: Long\n  public val throttlePeriodNanos: Long\n\n  public val failHandshake: Boolean\n  public val onRequestStart: SocketEffect?\n  public val doNotReadRequestBody: Boolean\n  public val onRequestBody: SocketEffect?\n  public val onResponseStart: SocketEffect?\n  public val onResponseBody: SocketEffect?\n  public val onResponseEnd: SocketEffect?\n  public val shutdownServer: Boolean\n\n  public val headersDelayNanos: Long\n  public val bodyDelayNanos: Long\n  public val trailersDelayNanos: Long\n\n  /** The streams the server will push with this response. */\n  public val pushPromises: List<PushPromise>\n\n  public val settings: Settings\n\n  public constructor(\n    code: Int = 200,\n    headers: Headers = headersOf(),\n    body: String = \"\",\n  ) : this(\n    Builder()\n      .code(code)\n      .headers(headers)\n      .body(body),\n  )\n\n  private constructor(builder: Builder) {\n    this.status = builder.status\n    this.headers = builder.headers\n    this.trailers = builder.trailers\n    this.body = builder.body\n    this.socketHandler = builder.socketHandler\n    this.webSocketListener = builder.webSocketListener\n    this.inTunnel = builder.inTunnel\n    this.informationalResponses = builder.informationalResponses\n    this.throttleBytesPerPeriod = builder.throttleBytesPerPeriod\n    this.throttlePeriodNanos = builder.throttlePeriodNanos\n    this.failHandshake = builder.failHandshake\n    this.onRequestStart = builder.onRequestStart\n    this.doNotReadRequestBody = builder.doNotReadRequestBody\n    this.onRequestBody = builder.onRequestBody\n    this.onResponseStart = builder.onResponseStart\n    this.onResponseBody = builder.onResponseBody\n    this.onResponseEnd = builder.onResponseEnd\n    this.shutdownServer = builder.shutdownServer\n    this.headersDelayNanos = builder.headersDelayNanos\n    this.bodyDelayNanos = builder.bodyDelayNanos\n    this.trailersDelayNanos = builder.trailersDelayNanos\n    this.pushPromises = builder.pushPromises\n    this.settings = builder.settings\n  }\n\n  public fun newBuilder(): Builder = Builder(this)\n\n  public override fun toString(): String = status\n\n  public class Builder : Cloneable {\n    public var inTunnel: Boolean\n      private set\n\n    private val informationalResponses_: MutableList<MockResponse>\n    public val informationalResponses: List<MockResponse>\n      get() = informationalResponses_.toList()\n\n    public var status: String\n      private set\n\n    public var code: Int\n      get() {\n        val statusParts = status.split(' ', limit = 3)\n        require(statusParts.size >= 2) { \"Unexpected status: $status\" }\n        return statusParts[1].toInt()\n      }\n      private set(value) {\n        val reason =\n          when (value) {\n            in 100..199 -> \"Informational\"\n            in 200..299 -> \"OK\"\n            in 300..399 -> \"Redirection\"\n            in 400..499 -> \"Client Error\"\n            in 500..599 -> \"Server Error\"\n            else -> \"Mock Response\"\n          }\n        status = \"HTTP/1.1 $value $reason\"\n      }\n\n    private var headers_: Headers.Builder\n    public val headers: Headers\n      get() = headers_.build()\n\n    private var trailers_: Headers.Builder\n    public val trailers: Headers\n      get() = trailers_.build()\n\n    // At most one of (body,webSocketListener,socketHandler) is non-null.\n    private var bodyVar: MockResponseBody? = null\n    private var socketHandlerVar: SocketHandler? = null\n    private var webSocketListenerVar: WebSocketListener? = null\n\n    public var body: MockResponseBody?\n      get() = bodyVar\n      private set(value) {\n        bodyVar = value\n        socketHandlerVar = null\n        webSocketListenerVar = null\n      }\n    public var socketHandler: SocketHandler?\n      get() = socketHandlerVar\n      private set(value) {\n        socketHandlerVar = value\n        bodyVar = null\n        webSocketListenerVar = null\n      }\n    public var webSocketListener: WebSocketListener?\n      get() = webSocketListenerVar\n      private set(value) {\n        webSocketListenerVar = value\n        bodyVar = null\n        socketHandlerVar = null\n      }\n\n    public var throttleBytesPerPeriod: Long\n      private set\n    public var throttlePeriodNanos: Long\n      private set\n\n    public var failHandshake: Boolean\n      private set\n    public var onRequestStart: SocketEffect?\n      private set\n    public var doNotReadRequestBody: Boolean\n      private set\n    public var onRequestBody: SocketEffect?\n      private set\n    public var onResponseStart: SocketEffect?\n      private set\n    public var onResponseBody: SocketEffect?\n      private set\n    public var onResponseEnd: SocketEffect?\n      private set\n    public var shutdownServer: Boolean\n      private set\n\n    public var headersDelayNanos: Long\n      private set\n    public var bodyDelayNanos: Long\n      private set\n    public var trailersDelayNanos: Long\n      private set\n\n    private val pushPromises_: MutableList<PushPromise>\n    public val pushPromises: List<PushPromise>\n      get() = pushPromises_.toList()\n\n    private val settings_: Settings\n    public val settings: Settings\n      get() = Settings().apply { merge(settings_) }\n\n    public constructor() {\n      this.inTunnel = false\n      this.informationalResponses_ = mutableListOf()\n      this.status = \"HTTP/1.1 200 OK\"\n      this.bodyVar = null\n      this.socketHandlerVar = null\n      this.webSocketListenerVar = null\n      this.headers_ =\n        Headers\n          .Builder()\n          .add(\"Content-Length\", \"0\")\n      this.trailers_ = Headers.Builder()\n      this.throttleBytesPerPeriod = Long.MAX_VALUE\n      this.throttlePeriodNanos = 0L\n      this.failHandshake = false\n      this.onRequestStart = null\n      this.doNotReadRequestBody = false\n      this.onRequestBody = null\n      this.onResponseStart = null\n      this.onResponseBody = null\n      this.onResponseEnd = null\n      this.shutdownServer = false\n      this.headersDelayNanos = 0L\n      this.bodyDelayNanos = 0L\n      this.trailersDelayNanos = 0L\n      this.pushPromises_ = mutableListOf()\n      this.settings_ = Settings()\n    }\n\n    internal constructor(mockResponse: MockResponse) {\n      this.inTunnel = mockResponse.inTunnel\n      this.informationalResponses_ = mockResponse.informationalResponses.toMutableList()\n      this.status = mockResponse.status\n      this.headers_ = mockResponse.headers.newBuilder()\n      this.trailers_ = mockResponse.trailers.newBuilder()\n      this.bodyVar = mockResponse.body\n      this.socketHandlerVar = mockResponse.socketHandler\n      this.webSocketListenerVar = mockResponse.webSocketListener\n      this.throttleBytesPerPeriod = mockResponse.throttleBytesPerPeriod\n      this.throttlePeriodNanos = mockResponse.throttlePeriodNanos\n      this.failHandshake = mockResponse.failHandshake\n      this.onRequestStart = mockResponse.onRequestStart\n      this.doNotReadRequestBody = mockResponse.doNotReadRequestBody\n      this.onRequestBody = mockResponse.onRequestBody\n      this.onResponseStart = mockResponse.onResponseStart\n      this.onResponseBody = mockResponse.onResponseBody\n      this.onResponseEnd = mockResponse.onResponseEnd\n      this.shutdownServer = mockResponse.shutdownServer\n      this.headersDelayNanos = mockResponse.headersDelayNanos\n      this.bodyDelayNanos = mockResponse.bodyDelayNanos\n      this.trailersDelayNanos = mockResponse.trailersDelayNanos\n      this.pushPromises_ = mockResponse.pushPromises.toMutableList()\n      this.settings_ =\n        Settings().apply {\n          merge(mockResponse.settings)\n        }\n    }\n\n    public fun code(code: Int): Builder =\n      apply {\n        this.code = code\n      }\n\n    /** Sets the status and returns this. */\n    public fun status(status: String): Builder =\n      apply {\n        this.status = status\n      }\n\n    /**\n     * Removes all HTTP headers including any \"Content-Length\" and \"Transfer-encoding\" headers that\n     * were added by default.\n     */\n    public fun clearHeaders(): Builder =\n      apply {\n        headers_ = Headers.Builder()\n      }\n\n    /**\n     * Adds [header] as an HTTP header. For well-formed HTTP [header] should contain a name followed\n     * by a colon and a value.\n     */\n    public fun addHeader(header: String): Builder =\n      apply {\n        headers_.add(header)\n      }\n\n    /**\n     * Adds a new header with the name and value. This may be used to add multiple headers with the\n     * same name.\n     */\n    public fun addHeader(\n      name: String,\n      value: Any,\n    ): Builder =\n      apply {\n        headers_.add(name, value.toString())\n      }\n\n    /**\n     * Adds a new header with the name and value. This may be used to add multiple headers with the\n     * same name. Unlike [addHeader] this does not validate the name and\n     * value.\n     */\n    public fun addHeaderLenient(\n      name: String,\n      value: Any,\n    ): Builder =\n      apply {\n        addHeaderLenient(headers_, name, value.toString())\n      }\n\n    /** Removes all headers named [name], then adds a new header with the name and value. */\n    public fun setHeader(\n      name: String,\n      value: Any,\n    ): Builder =\n      apply {\n        removeHeader(name)\n        addHeader(name, value)\n      }\n\n    /** Removes all headers named [name]. */\n    public fun removeHeader(name: String): Builder =\n      apply {\n        headers_.removeAll(name)\n      }\n\n    public fun body(body: Buffer): Builder = body(body.toMockResponseBody())\n\n    public fun body(body: MockResponseBody): Builder =\n      apply {\n        setHeader(\"Content-Length\", body.contentLength)\n        this.body = body\n      }\n\n    /** Sets the response body to the UTF-8 encoded bytes of [body]. */\n    public fun body(body: String): Builder = body(Buffer().writeUtf8(body))\n\n    public fun socketHandler(socketHandler: SocketHandler): Builder =\n      apply {\n        this.socketHandler = socketHandler\n      }\n\n    /**\n     * Sets the response body to [body], chunked every [maxChunkSize] bytes.\n     */\n    public fun chunkedBody(\n      body: Buffer,\n      maxChunkSize: Int = Int.MAX_VALUE,\n    ): Builder =\n      apply {\n        removeHeader(\"Content-Length\")\n        headers_.add(\"Transfer-encoding: chunked\")\n\n        val bytesOut = Buffer()\n        while (!body.exhausted()) {\n          val chunkSize = minOf(body.size, maxChunkSize.toLong())\n          bytesOut.writeHexadecimalUnsignedLong(chunkSize)\n          bytesOut.writeUtf8(\"\\r\\n\")\n          bytesOut.write(body, chunkSize)\n          bytesOut.writeUtf8(\"\\r\\n\")\n        }\n        bytesOut.writeUtf8(\"0\\r\\n\") // Last chunk. Trailers follow!\n        this.body = bytesOut.toMockResponseBody()\n      }\n\n    /**\n     * Sets the response body to the UTF-8 encoded bytes of [body],\n     * chunked every [maxChunkSize] bytes.\n     */\n    public fun chunkedBody(\n      body: String,\n      maxChunkSize: Int = Int.MAX_VALUE,\n    ): Builder = chunkedBody(Buffer().writeUtf8(body), maxChunkSize)\n\n    /** Sets the headers and returns this. */\n    public fun headers(headers: Headers): Builder =\n      apply {\n        this.headers_ = headers.newBuilder()\n      }\n\n    /** Sets the trailers and returns this. */\n    public fun trailers(trailers: Headers): Builder =\n      apply {\n        this.trailers_ = trailers.newBuilder()\n      }\n\n    /** Don't trust the client during the SSL handshake. */\n    public fun failHandshake(): Builder =\n      apply {\n        failHandshake = true\n      }\n\n    /** Trigger [socketEffect] before the request headers are read. */\n    public fun onRequestStart(socketEffect: SocketEffect?): Builder =\n      apply {\n        this.onRequestStart = socketEffect\n      }\n\n    /**\n     * Process the response without even attempting to reading the request body. For HTTP/2 this\n     * will close the response stream after the response body or trailers. For HTTP/1 this will\n     * close the socket after the response body or trailers.\n     */\n    public fun doNotReadRequestBody(): Builder =\n      apply {\n        doNotReadRequestBody = true\n        onResponseEnd = CloseStream(ErrorCode.NO_ERROR.httpCode)\n      }\n\n    /** Trigger [socketEffect] while reading the request body. */\n    public fun onRequestBody(socketEffect: SocketEffect?): Builder =\n      apply {\n        this.onRequestBody = socketEffect\n      }\n\n    /** Trigger [socketEffect] before the response headers are sent. */\n    public fun onResponseStart(socketEffect: SocketEffect?): Builder =\n      apply {\n        this.onResponseStart = socketEffect\n      }\n\n    /** Trigger [socketEffect] while writing the response body. */\n    public fun onResponseBody(socketEffect: SocketEffect?): Builder =\n      apply {\n        this.onResponseBody = socketEffect\n      }\n\n    /** Trigger [socketEffect] after writing the response body. */\n    public fun onResponseEnd(socketEffect: SocketEffect?): Builder =\n      apply {\n        this.onResponseEnd = socketEffect\n      }\n\n    public fun shutdownServer(shutdownServer: Boolean): Builder =\n      apply {\n        this.shutdownServer = shutdownServer\n      }\n\n    /**\n     * Throttles the request reader and response writer to sleep for the given period after each\n     * series of [bytesPerPeriod] bytes are transferred. Use this to simulate network behavior.\n     */\n    public fun throttleBody(\n      bytesPerPeriod: Long,\n      period: Long,\n      unit: TimeUnit,\n    ): Builder =\n      apply {\n        throttleBytesPerPeriod = bytesPerPeriod\n        throttlePeriodNanos = unit.toNanos(period)\n      }\n\n    public fun headersDelay(\n      delay: Long,\n      unit: TimeUnit,\n    ): Builder =\n      apply {\n        headersDelayNanos = unit.toNanos(delay)\n      }\n\n    /**\n     * Set the delayed time of the response body to [delay]. This applies to the response body\n     * only; response headers are not affected.\n     */\n    public fun bodyDelay(\n      delay: Long,\n      unit: TimeUnit,\n    ): Builder =\n      apply {\n        bodyDelayNanos = unit.toNanos(delay)\n      }\n\n    public fun trailersDelay(\n      delay: Long,\n      unit: TimeUnit,\n    ): Builder =\n      apply {\n        trailersDelayNanos = unit.toNanos(delay)\n      }\n\n    /**\n     * When [protocols][MockWebServer.protocols] include [HTTP_2][okhttp3.Protocol], this attaches a\n     * pushed stream to this response.\n     */\n    public fun addPush(promise: PushPromise): Builder =\n      apply {\n        this.pushPromises_ += promise\n      }\n\n    /**\n     * When [protocols][MockWebServer.protocols] include [HTTP_2][okhttp3.Protocol], this pushes\n     * [settings] before writing the response.\n     */\n    public fun settings(settings: Settings): Builder =\n      apply {\n        this.settings_.clear()\n        this.settings_.merge(settings)\n      }\n\n    /**\n     * Attempts to perform a web socket upgrade on the connection.\n     * This will overwrite any previously set status, body, or streamHandler.\n     */\n    public fun webSocketUpgrade(listener: WebSocketListener): Builder =\n      apply {\n        status = \"HTTP/1.1 101 Switching Protocols\"\n        setHeader(\"Connection\", \"Upgrade\")\n        setHeader(\"Upgrade\", \"websocket\")\n        webSocketListener = listener\n      }\n\n    /**\n     * Configures this response to be served as a response to an HTTP CONNECT request, either for\n     * doing HTTPS through an HTTP proxy, or HTTP/2 prior knowledge through an HTTP proxy.\n     *\n     * When a new connection is received, all in-tunnel responses are served before the connection is\n     * upgraded to HTTPS or HTTP/2.\n     */\n    public fun inTunnel(): Builder =\n      apply {\n        removeHeader(\"Content-Length\")\n        inTunnel = true\n      }\n\n    /**\n     * Adds an HTTP 1xx response to precede this response. Note that this response's\n     * [headers delay][headersDelay] applies after this response is transmitted. Set a\n     * headers delay on that response to delay its transmission.\n     */\n    public fun addInformationalResponse(response: MockResponse): Builder =\n      apply {\n        informationalResponses_ += response\n      }\n\n    public fun add100Continue(): Builder =\n      apply {\n        addInformationalResponse(MockResponse(code = 100))\n      }\n\n    public override fun clone(): Builder = build().newBuilder()\n\n    public fun build(): MockResponse = MockResponse(this)\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/MockResponseBody.kt",
    "content": "/*\n * Copyright (c) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage mockwebserver3\n\nimport java.io.IOException\nimport okio.BufferedSink\n\n/**\n * The body of a [MockResponse].\n *\n * Unlike [okhttp3.ResponseBody], this interface is designed to be implemented by writers and not\n * called by readers.\n */\npublic interface MockResponseBody {\n  /** The length of this response in bytes, or -1 if unknown. */\n  public val contentLength: Long\n\n  @Throws(IOException::class)\n  public fun writeTo(sink: BufferedSink)\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/MockWebServer.kt",
    "content": "/*\n * Copyright (C) 2011 Google Inc.\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\n  \"CANNOT_OVERRIDE_INVISIBLE_MEMBER\",\n  \"INVISIBLE_MEMBER\",\n  \"INVISIBLE_REFERENCE\",\n  \"ktlint:standard:property-naming\",\n)\n\npackage mockwebserver3\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.ProtocolException\nimport java.net.Proxy\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.net.SocketException\nimport java.security.SecureRandom\nimport java.security.cert.CertificateException\nimport java.security.cert.X509Certificate\nimport java.util.Collections\nimport java.util.Locale\nimport java.util.concurrent.ConcurrentHashMap\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.logging.Level\nimport java.util.logging.Logger\nimport javax.net.ServerSocketFactory\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManager\nimport javax.net.ssl.X509TrustManager\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.SocketEffect.CloseStream\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.SocketEffect.Stall\nimport mockwebserver3.internal.DEFAULT_REQUEST_LINE_HTTP_1\nimport mockwebserver3.internal.DEFAULT_REQUEST_LINE_HTTP_2\nimport mockwebserver3.internal.MockWebServerSocket\nimport mockwebserver3.internal.RecordedRequest\nimport mockwebserver3.internal.RequestLine\nimport mockwebserver3.internal.ThrottledSink\nimport mockwebserver3.internal.TriggerSink\nimport mockwebserver3.internal.decodeRequestLine\nimport okhttp3.Headers\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.addHeaderLenient\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.http.HttpMethod\nimport okhttp3.internal.http2.ErrorCode\nimport okhttp3.internal.http2.Header\nimport okhttp3.internal.http2.Http2Connection\nimport okhttp3.internal.http2.Http2Stream\nimport okhttp3.internal.immutableListOf\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.threadFactory\nimport okhttp3.internal.toImmutableList\nimport okhttp3.internal.ws.RealWebSocket\nimport okhttp3.internal.ws.WebSocketExtensions\nimport okhttp3.internal.ws.WebSocketProtocol\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.Sink\nimport okio.Timeout\nimport okio.buffer\n\n/**\n * A scriptable web server. Callers supply canned responses and the server replays them upon request\n * in sequence.\n */\npublic class MockWebServer : Closeable {\n  private val taskRunnerBackend =\n    TaskRunner.RealBackend(\n      threadFactory(\"MockWebServer TaskRunner\", daemon = false),\n    )\n  private val taskRunner = TaskRunner(taskRunnerBackend)\n  private val requestQueue = LinkedBlockingQueue<RecordedRequest>()\n  private val openClientSockets =\n    Collections.newSetFromMap(ConcurrentHashMap<Socket, Boolean>())\n  private val openConnections =\n    Collections.newSetFromMap(ConcurrentHashMap<Http2Connection, Boolean>())\n\n  private val atomicRequestCount = AtomicInteger()\n\n  private var serverSocketFactory_: ServerSocketFactory? = null\n  private var serverSocket: ServerSocket? = null\n\n  /** Non-null after [start]. */\n  private var socketAddress_: InetSocketAddress? = null\n\n  private var sslSocketFactory: SSLSocketFactory? = null\n  private var clientAuth = CLIENT_AUTH_NONE\n\n  private var closed: Boolean = false\n\n  /**\n   * The number of HTTP requests received thus far by this server. This may exceed the number of\n   * HTTP connections when connection reuse is in practice.\n   */\n  public val requestCount: Int\n    get() = atomicRequestCount.get()\n\n  /** The number of bytes of the POST body to keep in memory to the given limit. */\n  public var bodyLimit: Long = Long.MAX_VALUE\n\n  public var serverSocketFactory: ServerSocketFactory?\n    @Synchronized get() = serverSocketFactory_\n\n    @Synchronized set(value) {\n      check(socketAddress_ == null) { \"serverSocketFactory must not be set after start()\" }\n      serverSocketFactory_ = value\n    }\n\n  /**\n   * The dispatcher used to respond to HTTP requests. The default dispatcher is a [QueueDispatcher],\n   * which serves a fixed sequence of responses from a [queue][enqueue].\n   *\n   * Other dispatchers can be configured. They can vary the response based on timing or the content\n   * of the request.\n   */\n  public var dispatcher: Dispatcher = QueueDispatcher()\n\n  public val socketAddress: InetSocketAddress\n    get() = socketAddress_ ?: error(\"call start() first\")\n\n  public val port: Int\n    get() = socketAddress.port\n\n  public val hostName: String\n    get() = socketAddress.address.hostName\n\n  /** Returns the address of this server, to connect to it as an HTTP proxy. */\n  public val proxyAddress: Proxy\n    get() = Proxy(Proxy.Type.HTTP, socketAddress)\n\n  /**\n   * True if ALPN is used on incoming HTTPS connections to negotiate a protocol like HTTP/1.1 or\n   * HTTP/2. This is true by default; set to false to disable negotiation and restrict connections\n   * to HTTP/1.1.\n   */\n  public var protocolNegotiationEnabled: Boolean = true\n\n  /**\n   * The protocols supported by ALPN on incoming HTTPS connections in order of preference. The list\n   * must contain [Protocol.HTTP_1_1]. It must not contain null.\n   *\n   * This list is ignored when [negotiation is disabled][protocolNegotiationEnabled].\n   */\n  public var protocols: List<Protocol> = immutableListOf(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    set(value) {\n      val protocolList = value.toImmutableList()\n      require(Protocol.H2_PRIOR_KNOWLEDGE !in protocolList || protocolList.size == 1) {\n        \"protocols containing h2_prior_knowledge cannot use other protocols: $protocolList\"\n      }\n      require(Protocol.HTTP_1_1 in protocolList || Protocol.H2_PRIOR_KNOWLEDGE in protocolList) {\n        \"protocols doesn't contain http/1.1: $protocolList\"\n      }\n      require(null !in protocolList as List<Protocol?>) { \"protocols must not contain null\" }\n      field = protocolList\n    }\n\n  public val started: Boolean\n    get() = socketAddress_ != null\n\n  /**\n   * Returns a URL for connecting to this server.\n   *\n   * @param path the request path, such as \"/\".\n   */\n  public fun url(path: String): HttpUrl =\n    HttpUrl\n      .Builder()\n      .scheme(if (sslSocketFactory != null) \"https\" else \"http\")\n      .host(hostName)\n      .port(port)\n      .build()\n      .resolve(path)!!\n\n  /**\n   * Serve requests with HTTPS rather than otherwise.\n   */\n  public fun useHttps(sslSocketFactory: SSLSocketFactory) {\n    this.sslSocketFactory = sslSocketFactory\n  }\n\n  /**\n   * Configure the server to not perform SSL authentication of the client. This leaves\n   * authentication to another layer such as in an HTTP cookie or header. This is the default and\n   * most common configuration.\n   */\n  public fun noClientAuth() {\n    this.clientAuth = CLIENT_AUTH_NONE\n  }\n\n  /**\n   * Configure the server to [want client auth][SSLSocket.setWantClientAuth]. If the\n   * client presents a certificate that is [trusted][TrustManager] the handshake will\n   * proceed normally. The connection will also proceed normally if the client presents no\n   * certificate at all! But if the client presents an untrusted certificate the handshake\n   * will fail and no connection will be established.\n   */\n  public fun requestClientAuth() {\n    this.clientAuth = CLIENT_AUTH_REQUESTED\n  }\n\n  /**\n   * Configure the server to [need client auth][SSLSocket.setNeedClientAuth]. If the\n   * client presents a certificate that is [trusted][TrustManager] the handshake will\n   * proceed normally. If the client presents an untrusted certificate or no certificate at all the\n   * handshake will fail and no connection will be established.\n   */\n  public fun requireClientAuth() {\n    this.clientAuth = CLIENT_AUTH_REQUIRED\n  }\n\n  /**\n   * Awaits the next HTTP request, removes it, and returns it. Callers should use this to verify the\n   * request was sent as intended. This method will block until the request is available, possibly\n   * forever.\n   *\n   * @return the head of the request queue\n   */\n  @Throws(InterruptedException::class)\n  public fun takeRequest(): RecordedRequest = requestQueue.take()\n\n  /**\n   * Awaits the next HTTP request (waiting up to the specified wait time if necessary), removes it,\n   * and returns it. Callers should use this to verify the request was sent as intended within the\n   * given time.\n   *\n   * @param timeout how long to wait before giving up, in units of [unit]\n   * @param unit a [TimeUnit] determining how to interpret the [timeout] parameter\n   * @return the head of the request queue\n   */\n  @Throws(InterruptedException::class)\n  public fun takeRequest(\n    timeout: Long,\n    unit: TimeUnit,\n  ): RecordedRequest? = requestQueue.poll(timeout, unit)\n\n  /**\n   * Scripts [response] to be returned to a request made in sequence. The first request is\n   * served by the first enqueued response; the second request by the second enqueued response; and\n   * so on.\n   *\n   * @throws ClassCastException if the default dispatcher has been\n   * replaced with [setDispatcher][dispatcher].\n   */\n  public fun enqueue(response: MockResponse) {\n    (dispatcher as QueueDispatcher).enqueue(response)\n  }\n\n  /**\n   * Starts the server on the loopback interface for the given port.\n   *\n   * @param port the port to listen to, or 0 for any available port. Automated tests should always\n   * use port 0 to avoid flakiness when a specific port is unavailable.\n   */\n  @Throws(IOException::class)\n  @JvmOverloads\n  public fun start(port: Int = 0) {\n    start(InetAddress.getByName(\"localhost\"), port)\n  }\n\n  /**\n   * Starts the server on the given address and port.\n   *\n   * @param inetAddress the address to create the server socket on\n   * @param port the port to listen to, or 0 for any available port. Automated tests should always\n   * use port 0 to avoid flakiness when a specific port is unavailable.\n   */\n  @Throws(IOException::class)\n  public fun start(\n    inetAddress: InetAddress,\n    port: Int,\n  ) {\n    start(InetSocketAddress(inetAddress, port))\n  }\n\n  /**\n   * Starts the server and binds to the given socket address.\n   *\n   * @param socketAddress the socket address to bind the server on\n   */\n  @Synchronized\n  @Throws(IOException::class)\n  private fun start(socketAddress: InetSocketAddress) {\n    check(!closed) { \"close() already called\" }\n\n    val alreadyStartedAddress = socketAddress_\n    if (alreadyStartedAddress != null) {\n      check(socketAddress.address == alreadyStartedAddress.address) {\n        \"unexpected address\"\n      }\n      check(socketAddress.port == 0 || socketAddress.port == alreadyStartedAddress.port) {\n        \"unexpected port\"\n      }\n      return // Already started.\n    }\n\n    var boundSocketAddress = socketAddress\n    try {\n      val serverSocketFactory =\n        serverSocketFactory_\n          ?: (ServerSocketFactory.getDefault()!!.also { this.serverSocketFactory_ = it })\n\n      val serverSocket =\n        serverSocketFactory\n          .createServerSocket()!!\n          .also { this.serverSocket = it }\n\n      // Reuse if the user specified a port\n      serverSocket.reuseAddress = socketAddress.port != 0\n      serverSocket.bind(socketAddress, 50)\n\n      // If the local port was 0, it'll be non-zero after bind().\n      boundSocketAddress = InetSocketAddress(boundSocketAddress.address, serverSocket.localPort)\n    } finally {\n      this.socketAddress_ = boundSocketAddress\n    }\n\n    taskRunner.newQueue().execute(toString(), cancelable = false) {\n      try {\n        logger.fine(\"$this starting to accept connections\")\n        acceptConnections()\n      } catch (e: Throwable) {\n        logger.log(Level.WARNING, \"$this failed unexpectedly\", e)\n      }\n\n      // Release all sockets and all threads, even if any close fails.\n      serverSocket?.closeQuietly()\n\n      val openClientSocket = openClientSockets.iterator()\n      while (openClientSocket.hasNext()) {\n        openClientSocket.next().closeQuietly()\n        openClientSocket.remove()\n      }\n\n      val httpConnection = openConnections.iterator()\n      while (httpConnection.hasNext()) {\n        httpConnection.next().closeQuietly()\n        httpConnection.remove()\n      }\n      dispatcher.close()\n    }\n  }\n\n  @Throws(Exception::class)\n  private fun acceptConnections() {\n    var nextConnectionIndex = 0\n    while (true) {\n      val socket: Socket\n      try {\n        socket = serverSocket!!.accept()\n      } catch (e: SocketException) {\n        logger.fine(\"${this@MockWebServer} done accepting connections: ${e.message}\")\n        return\n      }\n\n      val peek = dispatcher.peek()\n      if (peek.onRequestStart is CloseSocket) {\n        dispatchBookkeepingRequest(\n          connectionIndex = nextConnectionIndex++,\n          exchangeIndex = 0,\n          socket = MockWebServerSocket(socket),\n        )\n        socket.close()\n      } else {\n        openClientSockets.add(socket)\n        serveConnection(nextConnectionIndex++, socket, peek)\n      }\n    }\n  }\n\n  public override fun close() {\n    if (closed) return\n    closed = true\n\n    if (!started) return // Nothing to shut down.\n    val serverSocket = this.serverSocket ?: return // If this is null, start() must have failed.\n\n    // Cause acceptConnections() to break out.\n    serverSocket.closeQuietly()\n\n    // Await shutdown.\n    for (queue in taskRunner.activeQueues()) {\n      if (!queue.idleLatch().await(5, TimeUnit.SECONDS)) {\n        throw AssertionError(\"Gave up waiting for queue to shut down\")\n      }\n    }\n    taskRunnerBackend.shutdown()\n  }\n\n  private fun serveConnection(\n    connectionIndex: Int,\n    raw: Socket,\n    firstExchangePeek: MockResponse,\n  ) {\n    taskRunner.newQueue().execute(\"MockWebServer ${raw.remoteSocketAddress}\", cancelable = false) {\n      try {\n        SocketHandler(connectionIndex, raw, firstExchangePeek).handle()\n      } catch (e: IOException) {\n        logger.fine(\"$this connection from ${raw.inetAddress} failed: $e\")\n      } catch (e: Exception) {\n        logger.log(Level.SEVERE, \"$this connection from ${raw.inetAddress} crashed\", e)\n      }\n    }\n  }\n\n  internal inner class SocketHandler(\n    private val connectionIndex: Int,\n    private val raw: Socket,\n    private val firstExchangePeek: MockResponse,\n  ) {\n    private var nextExchangeIndex = 0\n\n    @Throws(Exception::class)\n    fun handle() {\n      if (!processTunnelRequests()) return\n\n      val protocol: Protocol\n      val socket: MockWebServerSocket\n      when {\n        sslSocketFactory != null -> {\n          if (firstExchangePeek.failHandshake) {\n            dispatchBookkeepingRequest(\n              connectionIndex = connectionIndex,\n              exchangeIndex = nextExchangeIndex++,\n              socket = MockWebServerSocket(raw),\n            )\n            processHandshakeFailure(raw)\n            return\n          }\n          val sslSocket =\n            sslSocketFactory!!.createSocket(\n              raw,\n              raw.inetAddress.hostAddress,\n              raw.port,\n              true,\n            ) as SSLSocket\n          sslSocket.useClientMode = false\n          if (clientAuth == CLIENT_AUTH_REQUIRED) {\n            sslSocket.needClientAuth = true\n          } else if (clientAuth == CLIENT_AUTH_REQUESTED) {\n            sslSocket.wantClientAuth = true\n          }\n          openClientSockets.add(sslSocket)\n\n          if (protocolNegotiationEnabled) {\n            Platform.get().configureTlsExtensions(sslSocket, null, protocols)\n          }\n\n          sslSocket.startHandshake()\n\n          // Wait until after handshake to grab the buffered socket\n          socket = MockWebServerSocket(sslSocket)\n\n          if (protocolNegotiationEnabled) {\n            val protocolString = Platform.get().getSelectedProtocol(sslSocket)\n            protocol =\n              when {\n                protocolString != null -> Protocol.get(protocolString)\n                else -> Protocol.HTTP_1_1\n              }\n            Platform.get().afterHandshake(sslSocket)\n          } else {\n            protocol = Protocol.HTTP_1_1\n          }\n          openClientSockets.remove(raw)\n        }\n\n        else -> {\n          protocol =\n            when {\n              Protocol.H2_PRIOR_KNOWLEDGE in protocols -> Protocol.H2_PRIOR_KNOWLEDGE\n              else -> Protocol.HTTP_1_1\n            }\n          socket = MockWebServerSocket(raw)\n        }\n      }\n\n      if (firstExchangePeek.onRequestStart == Stall) {\n        dispatchBookkeepingRequest(\n          connectionIndex = connectionIndex,\n          exchangeIndex = nextExchangeIndex++,\n          socket = socket,\n        )\n        return // Ignore the socket until the server is shut down!\n      }\n\n      if (protocol === Protocol.HTTP_2 || protocol === Protocol.H2_PRIOR_KNOWLEDGE) {\n        val http2SocketHandler = Http2SocketHandler(connectionIndex, socket, protocol)\n        val connection =\n          Http2Connection\n            .Builder(false, taskRunner)\n            .socket(socket, socket.javaNetSocket.remoteSocketAddress.toString())\n            .listener(http2SocketHandler)\n            .build()\n        connection.start()\n        openConnections.add(connection)\n        openClientSockets.remove(socket.javaNetSocket)\n        return\n      } else if (protocol !== Protocol.HTTP_1_1) {\n        throw AssertionError()\n      }\n\n      while (processOneRequest(socket)) {\n      }\n\n      if (nextExchangeIndex == 0) {\n        logger.warning(\n          \"${this@MockWebServer} connection from ${raw.inetAddress} didn't make a request\",\n        )\n      }\n\n      socket.close()\n      openClientSockets.remove(socket.javaNetSocket)\n    }\n\n    /**\n     * Respond to `CONNECT` requests until a non-tunnel response is peeked. Returns true if further\n     * calls should be attempted on the socket.\n     */\n    @Throws(IOException::class, InterruptedException::class)\n    private fun processTunnelRequests(): Boolean {\n      if (!dispatcher.peek().inTunnel) return true // No tunnel requests.\n\n      val socket = MockWebServerSocket(raw)\n      while (true) {\n        val socketStillGood = processOneRequest(socket)\n\n        // Clean up after the last exchange on a socket.\n        if (!socketStillGood) {\n          raw.close()\n          openClientSockets.remove(raw)\n          return false\n        }\n\n        if (!dispatcher.peek().inTunnel) return true // No more tunnel requests.\n      }\n    }\n\n    /**\n     * Reads a request and writes its response. Returns true if further calls should be attempted\n     * on the socket.\n     */\n    @Throws(IOException::class, InterruptedException::class)\n    private fun processOneRequest(socket: MockWebServerSocket): Boolean {\n      if (socket.source.exhausted()) {\n        return false // No more requests on this socket.\n      }\n\n      val request =\n        readRequest(\n          socket = socket,\n          connectionIndex = connectionIndex,\n          exchangeIndex = nextExchangeIndex++,\n        )\n      atomicRequestCount.incrementAndGet()\n      requestQueue.add(request)\n\n      if (request.failure != null) {\n        return false // Nothing to respond to.\n      }\n\n      val response = dispatcher.dispatch(request)\n\n      try {\n        if (handleSocketEffect(response.onResponseStart, socket)) {\n          return false\n        }\n\n        var reuseSocket = true\n        val requestWantsSocket = \"Upgrade\".equals(request.headers[\"Connection\"], ignoreCase = true)\n        val requestWantsWebSocket =\n          requestWantsSocket &&\n            \"websocket\".equals(request.headers[\"Upgrade\"], ignoreCase = true)\n        val responseWantsSocket = response.socketHandler != null\n        val responseWantsWebSocket = response.webSocketListener != null\n        if (requestWantsWebSocket && responseWantsWebSocket) {\n          handleWebSocketUpgrade(socket, request, response)\n          reuseSocket = false\n        } else if (requestWantsSocket && responseWantsSocket) {\n          writeHttpResponse(socket, response)\n          reuseSocket = false\n        } else {\n          writeHttpResponse(socket, response)\n        }\n\n        if (logger.isLoggable(Level.FINE)) {\n          logger.fine(\n            \"${this@MockWebServer} received request: $request and responded: $response\",\n          )\n        }\n\n        // See warnings associated with these socket policies in SocketPolicy.\n        if (handleSocketEffect(response.onResponseEnd, socket)) {\n          return false\n        }\n\n        return reuseSocket\n      } finally {\n        if (response.shutdownServer) {\n          close()\n        }\n      }\n    }\n  }\n\n  @Throws(Exception::class)\n  private fun processHandshakeFailure(raw: Socket) {\n    val context = SSLContext.getInstance(\"TLS\")\n    context.init(null, arrayOf<TrustManager>(UNTRUSTED_TRUST_MANAGER), SecureRandom())\n    val sslSocketFactory = context.socketFactory\n    val socket =\n      sslSocketFactory.createSocket(\n        raw,\n        raw.inetAddress.hostAddress,\n        raw.port,\n        true,\n      ) as SSLSocket\n    try {\n      socket.startHandshake() // we're testing a handshake failure\n      throw AssertionError()\n    } catch (_: IOException) {\n    }\n    socket.close()\n  }\n\n  @Throws(InterruptedException::class)\n  private fun dispatchBookkeepingRequest(\n    connectionIndex: Int,\n    exchangeIndex: Int,\n    socket: MockWebServerSocket,\n    requestLine: RequestLine = DEFAULT_REQUEST_LINE_HTTP_1,\n  ) {\n    val request =\n      RecordedRequest(\n        requestLine = requestLine,\n        headers = headersOf(),\n        chunkSizes = null,\n        bodySize = 0L,\n        body = null,\n        connectionIndex = connectionIndex,\n        exchangeIndex = exchangeIndex,\n        socket = socket,\n      )\n    atomicRequestCount.incrementAndGet()\n    requestQueue.add(request)\n    dispatcher.dispatch(request)\n  }\n\n  /** @param exchangeIndex the index of this request on this connection.*/\n  @Throws(IOException::class)\n  private fun readRequest(\n    socket: MockWebServerSocket,\n    connectionIndex: Int,\n    exchangeIndex: Int,\n  ): RecordedRequest {\n    var request: RequestLine = DEFAULT_REQUEST_LINE_HTTP_1\n    val headers = Headers.Builder()\n    var contentLength = -1L\n    var chunked = false\n    var hasBody = false\n    val requestBody = TruncatingBuffer(bodyLimit)\n    var chunkSizes: List<Int>? = null\n    var failure: IOException? = null\n\n    try {\n      val requestLineString = socket.source.readUtf8LineStrict()\n      if (requestLineString.isEmpty()) {\n        throw ProtocolException(\"no request because the stream is exhausted\")\n      }\n      request = decodeRequestLine(requestLineString)\n\n      while (true) {\n        val header = socket.source.readUtf8LineStrict()\n        if (header.isEmpty()) {\n          break\n        }\n        addHeaderLenient(headers, header)\n        val lowercaseHeader = header.lowercase(Locale.US)\n        if (contentLength == -1L && lowercaseHeader.startsWith(\"content-length:\")) {\n          contentLength = header.substring(15).trim().toLong()\n        }\n        if (lowercaseHeader.startsWith(\"transfer-encoding:\") &&\n          lowercaseHeader.substring(18).trim() == \"chunked\"\n        ) {\n          chunked = true\n        }\n      }\n\n      val peek = dispatcher.peek()\n      for (response in peek.informationalResponses) {\n        writeHttpResponse(socket, response)\n      }\n\n      val requestBodySink =\n        requestBody\n          .withThrottlingAndSocketEffect(\n            policy = peek,\n            socketEffect = peek.onRequestBody,\n            expectedByteCount = contentLength,\n            socket = socket,\n          ).buffer()\n      requestBodySink.use {\n        when {\n          peek.doNotReadRequestBody -> {\n            hasBody = false // Ignore the body completely.\n          }\n\n          contentLength != -1L -> {\n            hasBody = contentLength > 0L || HttpMethod.permitsRequestBody(request.method)\n            requestBodySink.write(socket.source, contentLength)\n          }\n\n          chunked -> {\n            chunkSizes = mutableListOf()\n            hasBody = true\n            while (true) {\n              val chunkSize =\n                socket.source\n                  .readUtf8LineStrict()\n                  .trim()\n                  .toInt(16)\n              if (chunkSize == 0) {\n                readEmptyLine(socket.source)\n                break\n              }\n              chunkSizes.add(chunkSize)\n              requestBodySink.write(socket.source, chunkSize.toLong())\n              readEmptyLine(socket.source)\n            }\n          }\n\n          else -> {\n            hasBody = false // No request body.\n          }\n        }\n      }\n\n      require(!hasBody || HttpMethod.permitsRequestBody(request.method)) {\n        \"Request must not have a body: $request\"\n      }\n    } catch (e: IOException) {\n      failure = e\n    }\n\n    return RecordedRequest(\n      requestLine = request,\n      headers = headers.build(),\n      chunkSizes = chunkSizes,\n      bodySize = requestBody.receivedByteCount,\n      body =\n        when {\n          hasBody -> requestBody.buffer.readByteString()\n          else -> null\n        },\n      connectionIndex = connectionIndex,\n      exchangeIndex = exchangeIndex,\n      socket = socket,\n      failure = failure,\n    )\n  }\n\n  @Throws(IOException::class)\n  private fun handleWebSocketUpgrade(\n    socket: MockWebServerSocket,\n    request: RecordedRequest,\n    response: MockResponse,\n  ) {\n    val key = request.headers[\"Sec-WebSocket-Key\"]\n    val webSocketResponse =\n      response\n        .newBuilder()\n        .setHeader(\"Sec-WebSocket-Accept\", WebSocketProtocol.acceptHeader(key!!))\n        .build()\n    writeHttpResponse(socket, webSocketResponse)\n\n    // Adapt the request and response into our Request and Response domain model.\n    val scheme = if (request.handshake != null) \"https\" else \"http\"\n    val authority = request.headers[\"Host\"] // Has host and port.\n    val fancyRequest =\n      Request\n        .Builder()\n        .url(\"$scheme://$authority/\")\n        .headers(request.headers)\n        .build()\n    val fancyResponse =\n      Response\n        .Builder()\n        .code(webSocketResponse.code)\n        .message(webSocketResponse.message)\n        .headers(webSocketResponse.headers)\n        .request(fancyRequest)\n        .protocol(Protocol.HTTP_1_1)\n        .build()\n\n    val webSocket =\n      RealWebSocket(\n        taskRunner = taskRunner,\n        originalRequest = fancyRequest,\n        listener = webSocketResponse.webSocketListener!!,\n        random = SecureRandom(),\n        pingIntervalMillis = 0,\n        extensions = WebSocketExtensions.parse(webSocketResponse.headers),\n        // Compress all messages if compression is enabled.\n        minimumDeflateSize = 0L,\n        webSocketCloseTimeout = RealWebSocket.CANCEL_AFTER_CLOSE_MILLIS,\n      )\n    val name = \"MockWebServer WebSocket ${request.url.encodedPath}\"\n\n    webSocket.initReaderAndWriter(\n      name = name,\n      socket = socket,\n      client = false,\n    )\n\n    webSocket.loopReader(fancyResponse)\n\n    // Even if messages are no longer being read we need to wait for the connection close signal.\n    socket.awaitClosed()\n  }\n\n  @Throws(IOException::class)\n  private fun writeHttpResponse(\n    socket: MockWebServerSocket,\n    response: MockResponse,\n  ) {\n    socket.sleepWhileOpen(response.headersDelayNanos)\n    socket.sink.writeUtf8(response.status)\n    socket.sink.writeUtf8(\"\\r\\n\")\n\n    writeHeaders(socket.sink, response.headers)\n\n    if (response.socketHandler != null) {\n      response.socketHandler.handle(socket)\n      return\n    }\n\n    val body = response.body ?: return\n    socket.sleepWhileOpen(response.bodyDelayNanos)\n    val responseBodySink =\n      socket.sink\n        .withThrottlingAndSocketEffect(\n          policy = response,\n          socketEffect = response.onResponseBody,\n          expectedByteCount = body.contentLength,\n          socket = socket,\n        ).buffer()\n    body.writeTo(responseBodySink)\n    responseBodySink.emit()\n\n    socket.sleepWhileOpen(response.trailersDelayNanos)\n    if (\"chunked\".equals(response.headers[\"Transfer-Encoding\"], ignoreCase = true)) {\n      writeHeaders(socket.sink, response.trailers)\n    }\n  }\n\n  @Throws(IOException::class)\n  private fun writeHeaders(\n    sink: BufferedSink,\n    headers: Headers,\n  ) {\n    for ((name, value) in headers) {\n      sink.writeUtf8(name)\n      sink.writeUtf8(\": \")\n      sink.writeUtf8(value)\n      sink.writeUtf8(\"\\r\\n\")\n    }\n    sink.writeUtf8(\"\\r\\n\")\n    sink.flush()\n  }\n\n  /** Returns a sink that applies throttling and disconnecting. */\n  private fun Sink.withThrottlingAndSocketEffect(\n    policy: MockResponse,\n    socketEffect: SocketEffect?,\n    expectedByteCount: Long,\n    socket: MockWebServerSocket,\n    stream: Http2Stream? = null,\n  ): Sink {\n    var result: Sink = this\n\n    if (policy.throttlePeriodNanos > 0L) {\n      result =\n        ThrottledSink(\n          socket = socket,\n          delegate = result,\n          bytesPerPeriod = policy.throttleBytesPerPeriod,\n          periodDelayNanos = policy.throttlePeriodNanos,\n        )\n    }\n\n    if (socketEffect != null) {\n      val halfwayByteCount =\n        when {\n          expectedByteCount != -1L -> expectedByteCount / 2\n          else -> 0L\n        }\n      result =\n        TriggerSink(\n          delegate = result,\n          triggerByteCount = halfwayByteCount,\n        ) {\n          result.flush()\n          handleSocketEffect(socketEffect, socket, stream)\n        }\n    }\n\n    return result\n  }\n\n  /** Returns true if processing this exchange is complete. */\n  private fun handleSocketEffect(\n    effect: SocketEffect?,\n    socket: MockWebServerSocket,\n    stream: Http2Stream? = null,\n  ): Boolean {\n    if (effect == null) return false\n\n    when (effect) {\n      is CloseStream -> {\n        if (stream != null) {\n          stream.close(ErrorCode.fromHttp2(effect.http2ErrorCode)!!, null)\n        } else {\n          socket.close()\n        }\n      }\n\n      ShutdownConnection -> {\n        if (stream != null) {\n          stream.connection.shutdown(ErrorCode.NO_ERROR)\n        } else {\n          socket.close()\n        }\n      }\n\n      is CloseSocket -> {\n        if (effect.shutdownInput) socket.shutdownInput()\n        if (effect.shutdownOutput) socket.shutdownOutput()\n        if (effect.closeSocket) socket.close()\n      }\n\n      Stall -> {\n        // Sleep until the socket is closed.\n        socket.sleepWhileOpen(TimeUnit.MINUTES.toNanos(60))\n        error(\"expected timeout\")\n      }\n    }\n\n    return true\n  }\n\n  @Throws(IOException::class)\n  private fun readEmptyLine(source: BufferedSource) {\n    val line = source.readUtf8LineStrict()\n    check(line.isEmpty()) { \"Expected empty but was: $line\" }\n  }\n\n  public override fun toString(): String {\n    val socketAddress = socketAddress_\n    return when {\n      closed -> \"MockWebServer{closed}\"\n      socketAddress != null -> \"MockWebServer{port=${socketAddress.port}}\"\n      else -> \"MockWebServer{new}\"\n    }\n  }\n\n  /** A buffer wrapper that drops data after [bodyLimit] bytes. */\n  private class TruncatingBuffer(\n    private var remainingByteCount: Long,\n  ) : Sink {\n    val buffer = Buffer()\n    var receivedByteCount = 0L\n\n    @Throws(IOException::class)\n    override fun write(\n      source: Buffer,\n      byteCount: Long,\n    ) {\n      val toRead = minOf(remainingByteCount, byteCount)\n      if (toRead > 0L) {\n        source.read(buffer, toRead)\n      }\n      val toSkip = byteCount - toRead\n      if (toSkip > 0L) {\n        source.skip(toSkip)\n      }\n      remainingByteCount -= toRead\n      receivedByteCount += byteCount\n    }\n\n    @Throws(IOException::class)\n    override fun flush() {\n    }\n\n    override fun timeout(): Timeout = Timeout.NONE\n\n    @Throws(IOException::class)\n    override fun close() {\n    }\n  }\n\n  /** Processes HTTP requests layered over HTTP/2. */\n  private inner class Http2SocketHandler(\n    private val connectionIndex: Int,\n    private val socket: MockWebServerSocket,\n    private val protocol: Protocol,\n  ) : Http2Connection.Listener() {\n    private val nextExchangeIndex = AtomicInteger()\n\n    @Throws(IOException::class)\n    override fun onStream(stream: Http2Stream) {\n      val peek = dispatcher.peek()\n      if (handleSocketEffect(peek.onRequestStart, socket, stream)) {\n        dispatchBookkeepingRequest(\n          connectionIndex = connectionIndex,\n          exchangeIndex = nextExchangeIndex.getAndIncrement(),\n          socket = socket,\n          requestLine = DEFAULT_REQUEST_LINE_HTTP_2,\n        )\n        return\n      }\n\n      val request = readRequest(stream)\n      atomicRequestCount.incrementAndGet()\n      requestQueue.add(request)\n      if (request.failure != null) {\n        return // Nothing to respond to.\n      }\n\n      val response = dispatcher.dispatch(request)\n\n      try {\n        if (handleSocketEffect(peek.onResponseStart, socket, stream)) {\n          return\n        }\n\n        writeResponse(stream, request, response)\n        if (logger.isLoggable(Level.FINE)) {\n          logger.fine(\n            \"${this@MockWebServer} received request: $request \" +\n              \"and responded: $response protocol is $protocol\",\n          )\n        }\n\n        handleSocketEffect(peek.onResponseEnd, socket, stream)\n      } finally {\n        if (response.shutdownServer) {\n          close()\n        }\n      }\n    }\n\n    @Throws(IOException::class)\n    private fun readRequest(stream: Http2Stream): RecordedRequest {\n      val streamHeaders = stream.takeHeaders()\n      val httpHeaders = Headers.Builder()\n      var method = \"<:method omitted>\"\n      var path = \"<:path omitted>\"\n      var readBody = true\n      for ((name, value) in streamHeaders) {\n        if (name == Header.TARGET_METHOD_UTF8) {\n          method = value\n        } else if (name == Header.TARGET_PATH_UTF8) {\n          path = value\n        } else if (protocol === Protocol.HTTP_2 || protocol === Protocol.H2_PRIOR_KNOWLEDGE) {\n          httpHeaders.add(name, value)\n        } else {\n          throw IllegalStateException()\n        }\n        if (name == \"expect\" && value.equals(\"100-continue\", ignoreCase = true)) {\n          // Don't read the body unless we've invited the client to send it.\n          readBody = false\n        }\n      }\n      val headers = httpHeaders.build()\n\n      val peek = dispatcher.peek()\n      for (response in peek.informationalResponses) {\n        socket.sleepWhileOpen(response.headersDelayNanos)\n        stream.writeHeaders(response.toHttp2Headers(), outFinished = false, flushHeaders = true)\n        if (response.code == 100) {\n          readBody = true\n        }\n      }\n\n      val requestLine =\n        RequestLine(\n          method = method,\n          target = path,\n          version = \"HTTP/2\",\n        )\n      var exception: IOException? = null\n      var bodyByteString: ByteString? = null\n      if (readBody && peek.socketHandler == null && !peek.doNotReadRequestBody) {\n        val body = Buffer()\n        try {\n          val contentLengthString = headers[\"content-length\"]\n          val requestBodySink =\n            body\n              .withThrottlingAndSocketEffect(\n                policy = peek,\n                socketEffect = peek.onRequestBody,\n                expectedByteCount = contentLengthString?.toLong() ?: Long.MAX_VALUE,\n                socket = socket,\n                stream = stream,\n              ).buffer()\n          requestBodySink.use {\n            it.writeAll(stream.source)\n          }\n        } catch (e: IOException) {\n          exception = e\n        } finally {\n          bodyByteString = body.readByteString()\n        }\n      }\n\n      return RecordedRequest(\n        requestLine = requestLine,\n        headers = headers,\n        chunkSizes = null, // No chunked encoding for HTTP/2.\n        bodySize = bodyByteString?.size?.toLong() ?: 0,\n        body =\n          when {\n            HttpMethod.permitsRequestBody(method) -> bodyByteString\n            else -> null\n          },\n        connectionIndex = connectionIndex,\n        exchangeIndex = nextExchangeIndex.getAndIncrement(),\n        socket = socket,\n        failure = exception,\n      )\n    }\n\n    private fun MockResponse.toHttp2Headers(): List<Header> {\n      val result = mutableListOf<Header>()\n      result += Header(Header.RESPONSE_STATUS, code.toString())\n      for ((name, value) in headers) {\n        result += Header(name, value)\n      }\n      return result\n    }\n\n    @Throws(IOException::class)\n    private fun writeResponse(\n      stream: Http2Stream,\n      request: RecordedRequest,\n      response: MockResponse,\n    ) {\n      val settings = response.settings\n      stream.connection.setSettings(settings)\n\n      val bodyDelayNanos = response.bodyDelayNanos\n      val trailers = response.trailers\n      val body = response.body\n      val socketHandler = response.socketHandler\n      val outFinished = (\n        body == null &&\n          response.pushPromises.isEmpty() &&\n          socketHandler == null\n      )\n      val flushHeaders = body == null || bodyDelayNanos != 0L\n      require(!outFinished || trailers.size == 0) {\n        \"unsupported: no body and non-empty trailers $trailers\"\n      }\n\n      socket.sleepWhileOpen(response.headersDelayNanos)\n      stream.writeHeaders(response.toHttp2Headers(), outFinished, flushHeaders)\n\n      if (trailers.size > 0) {\n        stream.enqueueTrailers(trailers)\n      }\n      pushPromises(stream, request, response.pushPromises)\n      if (body != null) {\n        socket.sleepWhileOpen(bodyDelayNanos)\n        val responseBodySink =\n          stream\n            .sink\n            .withThrottlingAndSocketEffect(\n              policy = response,\n              socketEffect = response.onResponseBody,\n              expectedByteCount = body.contentLength,\n              socket = socket,\n              stream = stream,\n            ).buffer()\n        responseBodySink.use {\n          body.writeTo(it)\n\n          // Delay trailers by sleeping before we close the stream. It's the same on the wire.\n          if (response.trailersDelayNanos != 0L) {\n            it.flush()\n            socket.sleepWhileOpen(response.trailersDelayNanos)\n          }\n        }\n      } else if (socketHandler != null) {\n        socketHandler.handle(stream)\n      } else if (!outFinished) {\n        socket.sleepWhileOpen(response.trailersDelayNanos)\n        stream.close(ErrorCode.NO_ERROR, null)\n      }\n    }\n\n    @Throws(IOException::class)\n    private fun pushPromises(\n      stream: Http2Stream,\n      request: RecordedRequest,\n      promises: List<PushPromise>,\n    ) {\n      for (pushPromise in promises) {\n        val pushedHeaders = mutableListOf<Header>()\n        pushedHeaders.add(Header(Header.TARGET_AUTHORITY, url(pushPromise.path).host))\n        pushedHeaders.add(Header(Header.TARGET_METHOD, pushPromise.method))\n        pushedHeaders.add(Header(Header.TARGET_PATH, pushPromise.path))\n        val pushPromiseHeaders = pushPromise.headers\n        for ((name, value) in pushPromiseHeaders) {\n          pushedHeaders.add(Header(name, value))\n        }\n        val requestLine =\n          RequestLine(\n            method = pushPromise.method,\n            target = pushPromise.path,\n            version = \"HTTP/2\",\n          )\n        requestQueue.add(\n          RecordedRequest(\n            requestLine = requestLine,\n            headers = pushPromise.headers,\n            chunkSizes = null, // No chunked encoding for HTTP/2.\n            bodySize = 0,\n            body = null,\n            connectionIndex = connectionIndex,\n            exchangeIndex = nextExchangeIndex.getAndIncrement(),\n            socket = socket,\n          ),\n        )\n        val hasBody = pushPromise.response.body != null\n        val pushedStream = stream.connection.pushStream(stream.id, pushedHeaders, hasBody)\n        writeResponse(pushedStream, request, pushPromise.response)\n      }\n    }\n  }\n\n  private companion object {\n    private const val CLIENT_AUTH_NONE = 0\n    private const val CLIENT_AUTH_REQUESTED = 1\n    private const val CLIENT_AUTH_REQUIRED = 2\n\n    private val UNTRUSTED_TRUST_MANAGER =\n      object : X509TrustManager {\n        @Throws(CertificateException::class)\n        override fun checkClientTrusted(\n          chain: Array<X509Certificate>,\n          authType: String,\n        ) = throw CertificateException()\n\n        override fun checkServerTrusted(\n          chain: Array<X509Certificate>,\n          authType: String,\n        ) = throw AssertionError()\n\n        override fun getAcceptedIssuers(): Array<X509Certificate> = throw AssertionError()\n      }\n\n    private val logger = Logger.getLogger(MockWebServer::class.java.name)\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/PushPromise.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3\n\nimport okhttp3.Headers\n\n/** An HTTP request initiated by the server. */\npublic class PushPromise(\n  public val method: String,\n  public val path: String,\n  public val headers: Headers,\n  public val response: MockResponse,\n)\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/QueueDispatcher.kt",
    "content": "/*\n * Copyright (C) 2012 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3\n\nimport java.net.HttpURLConnection\nimport java.net.HttpURLConnection.HTTP_UNAVAILABLE\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.logging.Logger\n\n/**\n * Default dispatcher that processes a script of responses. Populate the script by calling\n * [enqueue].\n */\npublic open class QueueDispatcher : Dispatcher() {\n  protected val responseQueue: BlockingQueue<MockResponse> = LinkedBlockingQueue()\n  private var failFastResponse: MockResponse? = null\n\n  @Throws(InterruptedException::class)\n  override fun dispatch(request: RecordedRequest): MockResponse {\n    // To permit interactive/browser testing, ignore requests for favicons.\n    val requestLine = request.requestLine\n    if (requestLine == \"GET /favicon.ico HTTP/1.1\") {\n      logger.info(\"served $requestLine\")\n      return MockResponse(code = HttpURLConnection.HTTP_NOT_FOUND)\n    }\n\n    if (failFastResponse != null && responseQueue.peek() == null) {\n      // Fail fast if there's no response queued up.\n      return failFastResponse!!\n    }\n\n    val result = responseQueue.take()\n\n    // If take() returned because we're shutting down, then enqueue another dead letter so that any\n    // other threads waiting on take() will also return.\n    if (result == DEAD_LETTER) responseQueue.add(DEAD_LETTER)\n\n    return result\n  }\n\n  public override fun peek(): MockResponse = responseQueue.peek() ?: failFastResponse ?: super.peek()\n\n  public open fun enqueue(response: MockResponse) {\n    responseQueue.add(response)\n  }\n\n  public open fun clear() {\n    responseQueue.clear()\n  }\n\n  public override fun close() {\n    responseQueue.add(DEAD_LETTER)\n  }\n\n  public open fun setFailFast(failFast: Boolean) {\n    setFailFast(\n      failFastResponse =\n        when {\n          failFast -> MockResponse(code = HttpURLConnection.HTTP_NOT_FOUND)\n          else -> null\n        },\n    )\n  }\n\n  public open fun setFailFast(failFastResponse: MockResponse?) {\n    this.failFastResponse = failFastResponse\n  }\n\n  private companion object {\n    /**\n     * Enqueued on shutdown to release threads waiting on [dispatch]. Note that this response\n     * isn't transmitted because the connection is closed before this response is returned.\n     */\n    private val DEAD_LETTER = MockResponse(code = HTTP_UNAVAILABLE)\n\n    private val logger = Logger.getLogger(QueueDispatcher::class.java.name)\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/RecordedRequest.kt",
    "content": "/*\n * Copyright (C) 2011 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage mockwebserver3\n\nimport java.io.IOException\nimport okhttp3.Handshake\nimport okhttp3.Headers\nimport okhttp3.HttpUrl\nimport okio.ByteString\n\n/** An HTTP request that came into the mock web server. */\npublic class RecordedRequest(\n  /**\n   * The index of the socket connection that carried this request. If two recorded requests share a\n   * connection index, they also shared a socket connection.\n   */\n  public val connectionIndex: Int,\n  /**\n   * The index of this exchange on its HTTP connection. A request is uniquely identified by the\n   * (connection index, exchange index) pair.\n   */\n  public val exchangeIndex: Int,\n  /**\n   * The TLS handshake of the connection that carried this request, or null if the request was\n   * received without TLS.\n   */\n  public val handshake: Handshake?,\n  /**\n   * Returns the name of the server the client requested via the SNI (Server Name Indication)\n   * attribute in the TLS handshake. Unlike the rest of the HTTP exchange, this name is sent in\n   * cleartext and may be monitored or blocked by a proxy or other middlebox.\n   */\n  public val handshakeServerNames: List<String>,\n  /** A string like `GET` or `POST`. */\n  public val method: String,\n  /**\n   * The request target from the original HTTP request.\n   *\n   * For origin-form requests this is a path like `/index.html`, that is combined with the `Host`\n   * header to create the request URL.\n   *\n   * For HTTP proxy requests this will be either an absolute-form string like\n   * `http://example.com/index.html` (HTTP proxy) or an authority-form string like\n   * `example.com:443` (HTTPS proxy).\n   *\n   * For OPTIONS requests, this may be an asterisk, `*`.\n   */\n  public val target: String,\n  /** A string like `HTTP/1.1` or `HTTP/2`. */\n  public val version: String,\n  /** The request URL built using the request line, headers, and local host name. */\n  public val url: HttpUrl,\n  /** All headers. */\n  public val headers: Headers,\n  /** The body of this request, or null if it has none. This may be truncated. */\n  public val body: ByteString?,\n  /** The total size of the body of this request (before truncation).*/\n  public val bodySize: Long,\n  /**\n   * The sizes of the chunks of this request's body, or null if the request's body was not encoded\n   * with chunked encoding.\n   */\n  public val chunkSizes: List<Int>?,\n  /**\n   * The failure MockWebServer recorded when attempting to decode this request. If, for example,\n   * the inbound request was truncated, this exception will be non-null.\n   */\n  public val failure: IOException? = null,\n) {\n  public val requestLine: String\n    get() = \"$method $target $version\"\n\n  public override fun toString(): String = requestLine\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/SocketEffect.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3\n\n/**\n * An adverse action to take on a socket, intended to exercise failure modes in the calling code.\n */\npublic sealed interface SocketEffect {\n  /**\n   * Close the TCP socket that carries this request.\n   *\n   * Using this as [MockResponse.onResponseEnd] is the default for HTTP/1.0.\n   */\n  public class CloseSocket(\n    public val closeSocket: Boolean = true,\n    public val shutdownInput: Boolean = false,\n    public val shutdownOutput: Boolean = false,\n  ) : SocketEffect\n\n  /**\n   * On HTTP/2, send a [GOAWAY frame](https://tools.ietf.org/html/rfc7540#section-6.8) immediately\n   * after the response and will close the connection when the client's socket is exhausted.\n   *\n   * On HTTP/1 this closes the socket.\n   */\n  public object ShutdownConnection : SocketEffect\n\n  /**\n   * On HTTP/2 this will send the error code on the stream.\n   *\n   * On HTTP/1 this closes the socket.\n   */\n  public class CloseStream(\n    public val http2ErrorCode: Int = 0,\n  ) : SocketEffect\n\n  /** Stop processing this. */\n  public object Stall : SocketEffect\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/SocketHandler.kt",
    "content": "/*\n * Copyright (C) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3\n\nimport okio.Socket\n\n/**\n * Handles a call's request and response streams directly. Use this instead of [MockResponseBody] to\n * begin sending response data before all request data has been received.\n *\n * See [okhttp3.RequestBody.isDuplex].\n */\npublic interface SocketHandler {\n  public fun handle(socket: Socket)\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/internal/BufferMockResponseBody.kt",
    "content": "/*\n * Copyright (c) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n@file:JvmName(\"MockResponseBodiesKt\")\n\npackage mockwebserver3.internal\n\nimport mockwebserver3.MockResponseBody\nimport okio.Buffer\nimport okio.BufferedSink\n\ninternal fun Buffer.toMockResponseBody(): MockResponseBody {\n  val defensiveCopy = clone()\n  return BufferMockResponseBody(defensiveCopy)\n}\n\ninternal class BufferMockResponseBody(\n  val buffer: Buffer,\n) : MockResponseBody {\n  override val contentLength = buffer.size\n\n  override fun writeTo(sink: BufferedSink) {\n    buffer.copyTo(sink.buffer)\n    sink.emitCompleteSegments()\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/internal/MockWebServerSocket.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3.internal\n\nimport java.io.Closeable\nimport java.io.InterruptedIOException\nimport java.net.InetAddress\nimport java.net.Socket\nimport java.util.concurrent.CountDownLatch\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Handshake\nimport okhttp3.Handshake.Companion.handshake\nimport okhttp3.internal.connection.BufferedSocket\nimport okhttp3.internal.platform.Platform\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ForwardingSink\nimport okio.ForwardingSource\nimport okio.asOkioSocket\nimport okio.buffer\n\n/**\n * Adapts a [java.net.Socket] to MockWebServer's needs.\n *\n * Note that [asOkioSocket] returns a socket that closes the underlying [java.net.Socket] when both\n * of its component streams are closed. This class takes advantage of that.\n */\ninternal class MockWebServerSocket(\n  val javaNetSocket: Socket,\n) : Closeable,\n  BufferedSocket {\n  private val delegate = javaNetSocket.asOkioSocket()\n  private val closedLatch = CountDownLatch(2)\n\n  override val source: BufferedSource =\n    object : ForwardingSource(delegate.source) {\n      private var closed = false\n\n      override fun close() {\n        if (closed) return\n        try {\n          super.close()\n        } finally {\n          closedLatch.countDown()\n        }\n      }\n    }.buffer()\n\n  override val sink: BufferedSink =\n    object : ForwardingSink(delegate.sink) {\n      private var closed = false\n\n      override fun close() {\n        if (closed) return\n        try {\n          super.close()\n        } finally {\n          closedLatch.countDown()\n        }\n      }\n    }.buffer()\n\n  val localAddress: InetAddress\n    get() = javaNetSocket.localAddress\n\n  val localPort: Int\n    get() = javaNetSocket.localPort\n\n  val scheme: String\n    get() =\n      when (javaNetSocket) {\n        is SSLSocket -> \"https\"\n        else -> \"http\"\n      }\n\n  val handshake: Handshake?\n    get() = (javaNetSocket as? SSLSocket)?.session?.handshake()\n\n  val handshakeServerNames: List<String>\n    get() =\n      (javaNetSocket as? SSLSocket)\n        ?.let { Platform.Companion.get().getHandshakeServerNames(it) }\n        ?: listOf()\n\n  fun shutdownInput() {\n    javaNetSocket.shutdownInput()\n  }\n\n  fun shutdownOutput() {\n    javaNetSocket.shutdownOutput()\n  }\n\n  /** Sleeps [nanos], throwing if the socket is closed before that period has elapsed. */\n  fun sleepWhileOpen(nanos: Long) {\n    var ms = nanos / 1_000_000L\n    val ns = nanos - (ms * 1_000_000L)\n\n    while (ms > 100) {\n      Thread.sleep(100)\n      if (javaNetSocket.isClosed) throw InterruptedIOException(\"socket closed\")\n      ms -= 100L\n    }\n\n    if (ms > 0L || ns > 0) {\n      Thread.sleep(ms, ns.toInt())\n    }\n  }\n\n  override fun cancel() {\n    delegate.cancel()\n  }\n\n  override fun close() {\n    javaNetSocket.close()\n  }\n\n  fun awaitClosed() {\n    closedLatch.await()\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/internal/RecordedRequestFactory.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3.internal\n\nimport java.io.IOException\nimport java.net.Inet6Address\nimport java.net.ProtocolException\nimport mockwebserver3.RecordedRequest\nimport okhttp3.Headers\nimport okhttp3.HttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okio.ByteString\n\ninternal fun RecordedRequest(\n  requestLine: RequestLine,\n  headers: Headers,\n  chunkSizes: List<Int>?,\n  bodySize: Long,\n  body: ByteString?,\n  connectionIndex: Int,\n  exchangeIndex: Int,\n  socket: MockWebServerSocket,\n  failure: IOException? = null,\n): RecordedRequest {\n  val requestUrl =\n    when (requestLine.method) {\n      \"CONNECT\" -> \"${socket.scheme}://${requestLine.target}/\".toHttpUrlOrNull()\n      else -> null\n    }\n      ?: requestLine.target.toHttpUrlOrNull()\n      ?: requestUrl(socket, requestLine, headers)\n\n  return RecordedRequest(\n    connectionIndex = connectionIndex,\n    exchangeIndex = exchangeIndex,\n    handshake = socket.handshake,\n    handshakeServerNames = socket.handshakeServerNames,\n    method = requestLine.method,\n    target = requestLine.target,\n    version = requestLine.version,\n    url = requestUrl,\n    headers = headers,\n    body = body,\n    bodySize = bodySize,\n    chunkSizes = chunkSizes,\n    failure = failure,\n  )\n}\n\ninternal fun decodeRequestLine(requestLine: String?): RequestLine {\n  val parts =\n    when {\n      requestLine != null -> requestLine.split(' ', limit = 3)\n      else -> return DEFAULT_REQUEST_LINE_HTTP_1\n    }\n\n  if (parts.size != 3) {\n    throw ProtocolException(\"unexpected request line: $requestLine\")\n  }\n\n  return RequestLine(\n    method = parts[0],\n    target = parts[1],\n    version = parts[2],\n  )\n}\n\ninternal class RequestLine(\n  val method: String,\n  val target: String,\n  val version: String,\n) {\n  override fun toString() = \"$method $target $version\"\n}\n\ninternal val DEFAULT_REQUEST_LINE_HTTP_1 =\n  RequestLine(\n    method = \"GET\",\n    target = \"/\",\n    version = \"HTTP/1.1\",\n  )\n\ninternal val DEFAULT_REQUEST_LINE_HTTP_2 =\n  RequestLine(\n    method = \"GET\",\n    target = \"/\",\n    version = \"HTTP/2\",\n  )\n\nprivate fun requestUrl(\n  socket: MockWebServerSocket,\n  requestLine: RequestLine,\n  headers: Headers,\n): HttpUrl {\n  val hostAndPort =\n    headers[\":authority\"]\n      ?: headers[\"Host\"]\n      ?: when (val inetAddress = socket.localAddress) {\n        is Inet6Address -> \"[${inetAddress.hostAddress}]:${socket.localPort}\"\n        else -> \"${inetAddress.hostAddress}:${socket.localPort}\"\n      }\n\n  // For OPTIONS, the request target may be a '*', like 'OPTIONS * HTTP/1.1'.\n  val path =\n    when {\n      requestLine.method == \"OPTIONS\" && requestLine.target == \"*\" -> \"/\"\n      else -> requestLine.target\n    }\n\n  return \"${socket.scheme}://$hostAndPort$path\".toHttpUrl()\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/internal/ThrottledSink.kt",
    "content": "/*\n * Copyright (c) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage mockwebserver3.internal\n\nimport okio.Buffer\nimport okio.Sink\n\n/**\n * A sink that sleeps [periodDelayNanos] every [bytesPerPeriod] bytes. Unlike [okio.Throttler],\n * this permits any interval to be used.\n */\ninternal class ThrottledSink(\n  private val socket: MockWebServerSocket,\n  private val delegate: Sink,\n  private val bytesPerPeriod: Long,\n  private val periodDelayNanos: Long,\n) : Sink by delegate {\n  private var bytesWrittenSinceLastDelay = 0L\n\n  override fun write(\n    source: Buffer,\n    byteCount: Long,\n  ) {\n    var bytesLeft = byteCount\n\n    while (bytesLeft > 0) {\n      if (bytesWrittenSinceLastDelay == bytesPerPeriod) {\n        flush()\n        socket.sleepWhileOpen(periodDelayNanos)\n        bytesWrittenSinceLastDelay = 0\n      }\n\n      val toWrite = minOf(bytesLeft, bytesPerPeriod - bytesWrittenSinceLastDelay)\n      bytesWrittenSinceLastDelay += toWrite\n      bytesLeft -= toWrite\n      delegate.write(source, toWrite)\n    }\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/main/kotlin/mockwebserver3/internal/TriggerSink.kt",
    "content": "/*\n * Copyright (c) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage mockwebserver3.internal\n\nimport okio.Buffer\nimport okio.Sink\n\n/**\n * A sink that executes [trigger] after [triggerByteCount] bytes are written, and then skips all\n * subsequent bytes.\n */\ninternal class TriggerSink(\n  private val delegate: Sink,\n  private val triggerByteCount: Long,\n  private val trigger: () -> Unit,\n) : Sink by delegate {\n  private var bytesWritten = 0L\n\n  override fun write(\n    source: Buffer,\n    byteCount: Long,\n  ) {\n    if (byteCount == 0L) return // Avoid double-triggering.\n\n    if (bytesWritten == triggerByteCount) {\n      source.skip(byteCount)\n      return\n    }\n\n    val toWrite = minOf(byteCount, triggerByteCount - bytesWritten)\n    bytesWritten += toWrite\n\n    delegate.write(source, toWrite)\n\n    if (bytesWritten == triggerByteCount) {\n      trigger()\n    }\n\n    source.skip(byteCount - toWrite)\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/test/java/mockwebserver3/CustomDispatcherTest.kt",
    "content": "/*\n * Copyright (C) 2012 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport java.net.HttpURLConnection\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.atomic.AtomicInteger\nimport mockwebserver3.junit5.StartStop\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\n\n@Timeout(30)\nclass CustomDispatcherTest {\n  @StartStop private val mockWebServer = MockWebServer()\n\n  @Test\n  fun simpleDispatch() {\n    val requestsMade = mutableListOf<RecordedRequest>()\n    val dispatcher: Dispatcher =\n      object : Dispatcher() {\n        override fun dispatch(request: RecordedRequest): MockResponse {\n          requestsMade.add(request)\n          return MockResponse()\n        }\n      }\n    assertThat(requestsMade.size).isEqualTo(0)\n    mockWebServer.dispatcher = dispatcher\n    val url = mockWebServer.url(\"/\").toUrl()\n    val conn = url.openConnection() as HttpURLConnection\n    conn.responseCode // Force the connection to hit the \"server\".\n    // Make sure our dispatcher got the request.\n    assertThat(requestsMade.size).isEqualTo(1)\n  }\n\n  @Test\n  fun outOfOrderResponses() {\n    val firstResponseCode = AtomicInteger()\n    val secondResponseCode = AtomicInteger()\n    val secondRequest = \"/bar\"\n    val firstRequest = \"/foo\"\n    val latch = CountDownLatch(1)\n    val dispatcher: Dispatcher =\n      object : Dispatcher() {\n        override fun dispatch(request: RecordedRequest): MockResponse {\n          if (request.url.encodedPath == firstRequest) {\n            latch.await()\n          }\n          return MockResponse()\n        }\n      }\n    mockWebServer.dispatcher = dispatcher\n    val startsFirst = buildRequestThread(firstRequest, firstResponseCode)\n    startsFirst.start()\n    val endsFirst = buildRequestThread(secondRequest, secondResponseCode)\n    endsFirst.start()\n    endsFirst.join()\n    // First response is still waiting.\n    assertThat(firstResponseCode.get()).isEqualTo(0)\n    // Second response is done.\n    assertThat(secondResponseCode.get()).isEqualTo(200)\n    latch.countDown()\n    startsFirst.join()\n    // And now it's done!\n    assertThat(firstResponseCode.get()).isEqualTo(200)\n    // (Still done).\n    assertThat(secondResponseCode.get()).isEqualTo(200)\n  }\n\n  private fun buildRequestThread(\n    path: String,\n    responseCode: AtomicInteger,\n  ): Thread =\n    Thread {\n      val url = mockWebServer.url(path).toUrl()\n      val conn: HttpURLConnection\n      try {\n        conn = url.openConnection() as HttpURLConnection\n        responseCode.set(conn.responseCode) // Force the connection to hit the \"server\".\n      } catch (ignored: IOException) {\n      }\n    }\n}\n"
  },
  {
    "path": "mockwebserver/src/test/java/mockwebserver3/MockResponseSniTest.kt",
    "content": "/*\n * Copyright (C) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isTrue\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Dns\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Request\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass MockResponseSniTest {\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @Test\n  fun clientSendsServerNameAndServerReceivesIt() {\n    // java.net.ConnectException: Connection refused\n    platform.assumeNotConscrypt()\n\n    val handshakeCertificates = localhost()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n\n    val dns =\n      Dns {\n        Dns.SYSTEM.lookup(server.hostName)\n      }\n\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).dns(dns)\n        .build()\n\n    server.enqueue(MockResponse())\n\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(\"localhost.localdomain\")\n        .build()\n    val call = client.newCall(Request(url = url))\n    val response = call.execute()\n    assertThat(response.isSuccessful).isTrue()\n\n    val recordedRequest = server.takeRequest()\n    // https://github.com/bcgit/bc-java/issues/1773\n    if (!platform.isBouncyCastle()) {\n      assertThat(recordedRequest.handshakeServerNames).containsExactly(url.host)\n    }\n  }\n\n  /**\n   * Use different hostnames for the TLS handshake (including SNI) and the HTTP request (in the\n   * Host header).\n   */\n  @Test\n  fun domainFronting() {\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"server name\")\n        .addSubjectAlternativeName(\"url-host.com\")\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(heldCertificate)\n        .addTrustedCertificate(heldCertificate.certificate)\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n\n    val dns =\n      Dns {\n        Dns.SYSTEM.lookup(server.hostName)\n      }\n\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).dns(dns)\n        .build()\n\n    server.enqueue(MockResponse())\n\n    val call =\n      client.newCall(\n        Request(\n          url = \"https://url-host.com:${server.port}/\".toHttpUrl(),\n          headers = headersOf(\"Host\", \"header-host\"),\n        ),\n      )\n    val response = call.execute()\n    assertThat(response.isSuccessful).isTrue()\n\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.url.host).isEqualTo(\"header-host\")\n\n    // https://github.com/bcgit/bc-java/issues/1773\n    if (!platform.isBouncyCastle()) {\n      assertThat(recordedRequest.handshakeServerNames).containsExactly(\"url-host.com\")\n    }\n  }\n\n  /** No SNI for literal IPv6 addresses. */\n  @Test\n  fun ipv6() {\n    val recordedRequest = requestToHostnameViaProxy(\"2607:f8b0:400b:804::200e\")\n    assertThat(recordedRequest.url.host).isEqualTo(\"2607:f8b0:400b:804::200e\")\n    assertThat(recordedRequest.handshakeServerNames).isEmpty()\n  }\n\n  /** No SNI for literal IPv4 addresses. */\n  @Test\n  fun ipv4() {\n    val recordedRequest = requestToHostnameViaProxy(\"76.223.91.57\")\n    assertThat(recordedRequest.url.host).isEqualTo(\"76.223.91.57\")\n    assertThat(recordedRequest.handshakeServerNames).isEmpty()\n  }\n\n  @Test\n  fun regularHostname() {\n    val recordedRequest = requestToHostnameViaProxy(\"cash.app\")\n    assertThat(recordedRequest.url.host).isEqualTo(\"cash.app\")\n    // https://github.com/bcgit/bc-java/issues/1773\n    if (!platform.isBouncyCastle()) {\n      assertThat(recordedRequest.handshakeServerNames).containsExactly(\"cash.app\")\n    }\n  }\n\n  /**\n   * Connect to [hostnameOrIpAddress] and return what was received. To fake an arbitrary hostname we\n   * tell MockWebServer to act as a proxy.\n   */\n  private fun requestToHostnameViaProxy(hostnameOrIpAddress: String): RecordedRequest {\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"server name\")\n        .addSubjectAlternativeName(hostnameOrIpAddress)\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(heldCertificate)\n        .addTrustedCertificate(heldCertificate.certificate)\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .build()\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse())\n\n    val call =\n      client.newCall(\n        Request(\n          url =\n            server\n              .url(\"/\")\n              .newBuilder()\n              .host(hostnameOrIpAddress)\n              .build(),\n        ),\n      )\n    val response = call.execute()\n    assertThat(response.isSuccessful).isTrue()\n\n    server.takeRequest() // Discard the CONNECT tunnel.\n    return server.takeRequest()\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/test/java/mockwebserver3/MockWebServerTest.kt",
    "content": "/*\n * Copyright (C) 2011 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isBetween\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isGreaterThan\nimport assertk.assertions.isGreaterThanOrEqualTo\nimport assertk.assertions.isNotEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport java.io.BufferedReader\nimport java.io.Closeable\nimport java.io.IOException\nimport java.io.InputStreamReader\nimport java.net.ConnectException\nimport java.net.HttpURLConnection\nimport java.net.InetAddress\nimport java.net.ProtocolException\nimport java.net.SocketTimeoutException\nimport java.nio.charset.StandardCharsets.UTF_8\nimport java.time.Duration\nimport java.util.concurrent.TimeUnit\nimport javax.net.ssl.HttpsURLConnection\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.SocketEffect.CloseSocket\nimport okhttp3.Headers\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.RecordingHostnameVerifier\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.TestUtil.assumeNotWindows\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Suppress(\"deprecation\")\n@Timeout(30)\n@Tag(\"Slow\")\nclass MockWebServerTest {\n  @RegisterExtension\n  var platform = PlatformRule()\n\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    server.start()\n  }\n\n  @AfterEach\n  fun tearDown() {\n    server.close()\n  }\n\n  @Test\n  fun defaultMockResponse() {\n    val builder = MockResponse.Builder()\n    assertThat(headersToList(builder)).containsExactly(\"Content-Length: 0\")\n    assertThat(builder.status).isEqualTo(\"HTTP/1.1 200 OK\")\n  }\n\n  @Test\n  fun setResponseMockReason() {\n    val reasons =\n      arrayOf<String?>(\n        \"Mock Response\",\n        \"Informational\",\n        \"OK\",\n        \"Redirection\",\n        \"Client Error\",\n        \"Server Error\",\n        \"Mock Response\",\n      )\n    for (i in 0..599) {\n      val builder = MockResponse.Builder().code(i)\n      val expectedReason = reasons[i / 100]\n      assertThat(builder.status).isEqualTo(\"HTTP/1.1 $i $expectedReason\")\n      assertThat(headersToList(builder)).containsExactly(\"Content-Length: 0\")\n    }\n  }\n\n  @Test\n  fun setStatusControlsWholeStatusLine() {\n    val builder = MockResponse.Builder().status(\"HTTP/1.1 202 That'll do pig\")\n    assertThat(headersToList(builder)).containsExactly(\"Content-Length: 0\")\n    assertThat(builder.status).isEqualTo(\"HTTP/1.1 202 That'll do pig\")\n  }\n\n  @Test\n  fun setBodyAdjustsHeaders() {\n    val builder = MockResponse.Builder().body(\"ABC\")\n    assertThat(headersToList(builder)).containsExactly(\"Content-Length: 3\")\n    val response = builder.build()\n    val body = Buffer()\n    response.body!!.writeTo(body)\n    assertThat(body.readUtf8()).isEqualTo(\"ABC\")\n  }\n\n  @Test\n  fun mockResponseAddHeader() {\n    val builder =\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"Cookie: s=square\")\n        .addHeader(\"Cookie\", \"a=android\")\n    assertThat(headersToList(builder)).containsExactly(\"Cookie: s=square\", \"Cookie: a=android\")\n  }\n\n  @Test\n  fun mockResponseSetHeader() {\n    val builder =\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"Cookie: s=square\")\n        .addHeader(\"Cookie: a=android\")\n        .addHeader(\"Cookies: delicious\")\n    builder.setHeader(\"cookie\", \"r=robot\")\n    assertThat(headersToList(builder)).containsExactly(\"Cookies: delicious\", \"cookie: r=robot\")\n  }\n\n  @Test\n  fun mockResponseSetHeaders() {\n    val builder =\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"Cookie: s=square\")\n        .addHeader(\"Cookies: delicious\")\n    builder.headers(Headers.Builder().add(\"Cookie\", \"a=android\").build())\n    assertThat(headersToList(builder)).containsExactly(\"Cookie: a=android\")\n  }\n\n  @Test\n  fun regularResponse() {\n    server.enqueue(MockResponse.Builder().body(\"hello world\").build())\n    val url = server.url(\"/\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    connection.setRequestProperty(\"Accept-Language\", \"en-US\")\n    val reader = BufferedReader(InputStreamReader(connection.inputStream, UTF_8))\n    assertThat(connection.responseCode).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(reader.readLine()).isEqualTo(\"hello world\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    assertThat(request.headers[\"Accept-Language\"]).isEqualTo(\"en-US\")\n\n    // Server has no more requests.\n    assertThat(server.takeRequest(100, TimeUnit.MILLISECONDS)).isNull()\n  }\n\n  @Test\n  fun redirect() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .addHeader(\"Location: \" + server.url(\"/new-path\"))\n        .body(\"This page has moved!\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"This is the new location!\")\n        .build(),\n    )\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val reader = BufferedReader(InputStreamReader(connection!!.getInputStream(), UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"This is the new location!\")\n    val first = server.takeRequest()\n    assertThat(first.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    val redirect = server.takeRequest()\n    assertThat(redirect.requestLine).isEqualTo(\"GET /new-path HTTP/1.1\")\n  }\n\n  /**\n   * Test that MockWebServer blocks for a call to enqueue() if a request is made before a mock\n   * response is ready.\n   */\n  @Test\n  fun dispatchBlocksWaitingForEnqueue() {\n    Thread {\n      try {\n        Thread.sleep(1000)\n      } catch (ignored: InterruptedException) {\n      }\n      server.enqueue(\n        MockResponse\n          .Builder()\n          .body(\"enqueued in the background\")\n          .build(),\n      )\n    }.start()\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val reader = BufferedReader(InputStreamReader(connection!!.getInputStream(), UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"enqueued in the background\")\n  }\n\n  @Test\n  fun nonHexadecimalChunkSize() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"G\\r\\nxxxxxxxxxxxxxxxx\\r\\n0\\r\\n\\r\\n\")\n        .clearHeaders()\n        .addHeader(\"Transfer-encoding: chunked\")\n        .build(),\n    )\n    val connection = server.url(\"/\").toUrl().openConnection()\n    try {\n      connection.getInputStream().read()\n      fail<Unit>()\n    } catch (expected: IOException) {\n      // Expected.\n    }\n  }\n\n  @Test\n  fun responseTimeout() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .clearHeaders()\n        .addHeader(\"Content-Length: 4\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEF\")\n        .build(),\n    )\n    val urlConnection = server.url(\"/\").toUrl().openConnection()\n    urlConnection!!.readTimeout = 1000\n    val inputStream = urlConnection.getInputStream()\n    assertThat(inputStream!!.read()).isEqualTo('A'.code)\n    assertThat(inputStream.read()).isEqualTo('B'.code)\n    assertThat(inputStream.read()).isEqualTo('C'.code)\n    try {\n      inputStream.read() // if Content-Length was accurate, this would return -1 immediately\n      fail<Unit>()\n    } catch (expected: SocketTimeoutException) {\n      // Expected.\n    }\n    val urlConnection2 = server.url(\"/\").toUrl().openConnection()\n    val in2 = urlConnection2!!.getInputStream()\n    assertThat(in2!!.read()).isEqualTo('D'.code)\n    assertThat(in2.read()).isEqualTo('E'.code)\n    assertThat(in2.read()).isEqualTo('F'.code)\n    assertThat(in2.read()).isEqualTo(-1)\n    val c0e0 = server.takeRequest()\n    assertThat(c0e0.connectionIndex).isEqualTo(0)\n    assertThat(c0e0.exchangeIndex).isEqualTo(0)\n    val c1e0 = server.takeRequest()\n    assertThat(c1e0.connectionIndex).isEqualTo(1)\n    assertThat(c1e0.exchangeIndex).isEqualTo(0)\n  }\n\n  @Disabled(\"Not actually failing where expected\")\n  @Test\n  fun disconnectAtStart() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseSocket())\n        .build(),\n    )\n    server.enqueue(MockResponse()) // The jdk's HttpUrlConnection is a bastard.\n    server.enqueue(MockResponse())\n    try {\n      server\n        .url(\"/a\")\n        .toUrl()\n        .openConnection()\n        .getInputStream()\n      fail<Unit>()\n    } catch (expected: IOException) {\n      // Expected.\n    }\n    server\n      .url(\"/b\")\n      .toUrl()\n      .openConnection()\n      .getInputStream() // Should succeed.\n  }\n\n  @Test\n  fun clearDispatcherQueue() {\n    server.enqueue(MockResponse(body = \"A\"))\n    (server.dispatcher as QueueDispatcher).clear()\n    server.enqueue(MockResponse(body = \"B\"))\n    val inputStream =\n      server\n        .url(\"/a\")\n        .toUrl()\n        .openConnection()\n        .getInputStream()\n    assertThat(inputStream!!.read()).isEqualTo('B'.code)\n  }\n\n  /**\n   * Throttle the request body by sleeping 500ms after every 3 bytes. With a 6-byte request, this\n   * should yield one sleep for a total delay of 500ms.\n   */\n  @Test\n  fun throttleRequest() {\n    assumeNotWindows()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .throttleBody(3, 500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val startNanos = System.nanoTime()\n    val connection = server.url(\"/\").toUrl().openConnection()\n    connection.doOutput = true\n    connection.getOutputStream().write(\"ABCDEF\".toByteArray(UTF_8))\n    val inputStream = connection.getInputStream()\n    assertThat(inputStream.read()).isEqualTo(-1)\n    val elapsedNanos = System.nanoTime() - startNanos\n    val elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos)\n    assertThat(elapsedMillis).isBetween(500L, 1000L)\n  }\n\n  /**\n   * Throttle the response body by sleeping 500ms after every 3 bytes. With a 6-byte response, this\n   * should yield one sleep for a total delay of 500ms.\n   */\n  @Test\n  fun throttleResponse() {\n    assumeNotWindows()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABCDEF\")\n        .throttleBody(3, 500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val startNanos = System.nanoTime()\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val inputStream = connection!!.getInputStream()\n    assertThat(inputStream!!.read()).isEqualTo('A'.code)\n    assertThat(inputStream.read()).isEqualTo('B'.code)\n    assertThat(inputStream.read()).isEqualTo('C'.code)\n    assertThat(inputStream.read()).isEqualTo('D'.code)\n    assertThat(inputStream.read()).isEqualTo('E'.code)\n    assertThat(inputStream.read()).isEqualTo('F'.code)\n    assertThat(inputStream.read()).isEqualTo(-1)\n    val elapsedNanos = System.nanoTime() - startNanos\n    val elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos)\n    assertThat(elapsedMillis).isBetween(500L, 1000L)\n  }\n\n  /** Delay the response body by sleeping 1s.  */\n  @Test\n  fun delayResponse() {\n    assumeNotWindows()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABCDEF\")\n        .bodyDelay(1, TimeUnit.SECONDS)\n        .build(),\n    )\n    val startNanos = System.nanoTime()\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val inputStream = connection!!.getInputStream()\n    assertThat(inputStream!!.read()).isEqualTo('A'.code)\n    val elapsedNanos = System.nanoTime() - startNanos\n    val elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos)\n    assertThat(elapsedMillis).isGreaterThanOrEqualTo(1000L)\n    inputStream.close()\n  }\n\n  @Test\n  fun disconnectRequestHalfway() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestBody(CloseSocket())\n        .build(),\n    )\n    // Limit the size of the request body that the server holds in memory to an arbitrary\n    // 3.5 MBytes so this test can pass on devices with little memory.\n    server.bodyLimit = 7 * 512 * 1024\n    val connection = server.url(\"/\").toUrl().openConnection() as HttpURLConnection\n    connection.requestMethod = \"POST\"\n    connection.doOutput = true\n    connection.setFixedLengthStreamingMode(1024 * 1024 * 1024) // 1 GB\n    connection.connect()\n    val out = connection.outputStream\n    val data = ByteArray(1024 * 1024)\n    var i = 0\n    while (i < 1024) {\n      try {\n        out!!.write(data)\n        out.flush()\n        if (i == 513) {\n          // pause slightly after half way to make result more predictable\n          Thread.sleep(100)\n        }\n      } catch (e: IOException) {\n        break\n      }\n      i++\n    }\n    // Halfway +/- 0.5%\n    assertThat(i.toFloat()).isCloseTo(512f, 5f)\n  }\n\n  @Test\n  fun disconnectResponseHalfway() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ab\")\n        .onResponseBody(CloseSocket())\n        .build(),\n    )\n    val connection = server.url(\"/\").toUrl().openConnection()\n    assertThat(connection!!.contentLength).isEqualTo(2)\n    val inputStream = connection.getInputStream()\n    assertThat(inputStream!!.read()).isEqualTo('a'.code)\n    try {\n      val byteRead = inputStream.read()\n      // OpenJDK behavior: end of stream.\n      assertThat(byteRead).isEqualTo(-1)\n    } catch (e: ProtocolException) {\n      // On Android, HttpURLConnection is implemented by OkHttp v2. OkHttp\n      // treats an incomplete response body as a ProtocolException.\n    } catch (ioe: IOException) {\n      // Change in https://bugs.openjdk.org/browse/JDK-8335135\n      assertThat(ioe.message).isEqualTo(\"Premature EOF\")\n    }\n  }\n\n  private fun headersToList(response: MockResponse.Builder): List<String> {\n    val headers = response.build().headers\n    return headers.map { (key, value) -> \"$key: $value\" }.toList()\n  }\n\n  @Test\n  fun closeWithoutStart() {\n    val server = MockWebServer()\n    server.close()\n  }\n\n  @Test\n  fun closeViaClosable() {\n    val server: Closeable = MockWebServer()\n    server.close()\n  }\n\n  @Test\n  fun closeWithoutEnqueue() {\n    val server = MockWebServer()\n    server.start()\n    server.close()\n  }\n\n  @Test\n  fun portValidAfterStart() {\n    assertThat(server.port).isGreaterThan(0)\n  }\n\n  @Test\n  fun hostNameValidAfterStart() {\n    assertThat(server.hostName).isNotNull()\n  }\n\n  @Test\n  fun proxyAddressValidAfterStart() {\n    assertThat(server.proxyAddress).isNotNull()\n  }\n\n  @Test\n  fun differentInstancesGetDifferentPorts() {\n    val other = MockWebServer()\n    other.use {\n      other.start()\n      assertThat(other.port).isNotEqualTo(server.port)\n    }\n  }\n\n  @Test\n  fun cannotAccessAddressBeforeStart() {\n    val other = MockWebServer()\n    assertFailsWith<IllegalStateException> {\n      other.socketAddress\n    }\n    assertFailsWith<IllegalStateException> {\n      other.hostName\n    }\n    assertFailsWith<IllegalStateException> {\n      other.port\n    }\n    assertFailsWith<IllegalStateException> {\n      other.proxyAddress\n    }\n    other.use {\n      other.start()\n      assertThat(other.socketAddress).isNotNull()\n      assertThat(other.hostName).isNotNull()\n      assertThat(other.port).isNotNull()\n      assertThat(other.proxyAddress).isNotNull()\n    }\n  }\n\n  @Test\n  fun startIsIdempotentIfAddressIsConsistent() {\n    val other = MockWebServer()\n    val addressA = InetAddress.getByAddress(\"localhost\", byteArrayOf(127, 0, 0, 1))\n    val addressB = InetAddress.getByAddress(\"localhost\", byteArrayOf(127, 0, 0, 2))\n    other.use {\n      other.start(addressA, 0)\n\n      // Same address is okay.\n      other.start(addressA, 0)\n\n      // Same address with bound port is okay.\n      other.start(addressA, other.port)\n\n      // Different address is not okay.\n      assertFailsWith<IllegalStateException> {\n        other.start(addressB, 0)\n      }\n\n      // Different port is not okay.\n      assertFailsWith<IllegalStateException> {\n        other.start(addressA, other.port - 1)\n      }\n    }\n  }\n\n  @Test\n  fun toStringIncludesLifecycleState() {\n    val other = MockWebServer()\n    assertThat(other.toString()).isEqualTo(\"MockWebServer{new}\")\n    other.use {\n      other.start()\n      assertThat(other.toString()).isEqualTo(\"MockWebServer{port=${other.port}}\")\n    }\n    assertThat(other.toString()).isEqualTo(\"MockWebServer{closed}\")\n  }\n\n  @Test\n  fun closeWhileBlockedDispatching() {\n    // Enqueue a request that'll cause MockWebServer to hang on QueueDispatcher.dispatch().\n    val connection = server.url(\"/\").toUrl().openConnection() as HttpURLConnection\n    connection.readTimeout = 500\n    assertFailsWith<SocketTimeoutException> {\n      connection.responseCode\n    }\n\n    // Closing the server should unblock the dispatcher.\n    server.close()\n  }\n\n  @Test\n  fun requestUrlReconstructed() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"hello world\")\n        .build(),\n    )\n    val url = server.url(\"/a/deep/path?key=foo%20bar\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    val inputStream = connection.inputStream\n    val reader = BufferedReader(InputStreamReader(inputStream, UTF_8))\n    assertThat(connection.responseCode).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(reader.readLine()).isEqualTo(\"hello world\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\n      \"GET /a/deep/path?key=foo%20bar HTTP/1.1\",\n    )\n    val requestUrl = request.url\n    assertThat(requestUrl.scheme).isEqualTo(\"http\")\n    assertThat(requestUrl.host).isEqualTo(server.hostName)\n    assertThat(requestUrl.port).isEqualTo(server.port)\n    assertThat(requestUrl.encodedPath).isEqualTo(\"/a/deep/path\")\n    assertThat(requestUrl.queryParameter(\"key\")).isEqualTo(\"foo bar\")\n  }\n\n  @Test\n  fun shutdownServerAfterRequest() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .shutdownServer(true)\n        .build(),\n    )\n    val url = server.url(\"/\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    assertThat(connection.responseCode).isEqualTo(HttpURLConnection.HTTP_OK)\n    val refusedConnection = url.openConnection() as HttpURLConnection\n    assertFailsWith<ConnectException> {\n      refusedConnection.responseCode\n    }.also { expected ->\n      assertThat(expected.message!!).contains(\"refused\")\n    }\n  }\n\n  @Test\n  fun http100Continue() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"response\")\n        .build(),\n    )\n    val url = server.url(\"/\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    connection.doOutput = true\n    connection.setRequestProperty(\"Expect\", \"100-Continue\")\n    connection.outputStream.write(\"request\".toByteArray(UTF_8))\n    val inputStream = connection.inputStream\n    val reader = BufferedReader(InputStreamReader(inputStream, UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"response\")\n    val request = server.takeRequest()\n    assertThat(request.body?.utf8()).isEqualTo(\"request\")\n  }\n\n  @Test\n  fun http100ContinueChunkedStreaming() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"response\")\n        .add100Continue()\n        .build(),\n    )\n    val url = server.url(\"/\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    connection.doOutput = true\n    connection.setRequestProperty(\"Expect\", \"100-Continue\")\n    connection.setChunkedStreamingMode(0)\n    connection.outputStream.write(\"request\".toByteArray(UTF_8))\n    val inputStream = connection.inputStream\n    val reader = BufferedReader(InputStreamReader(inputStream, UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"response\")\n    val request = server.takeRequest()\n    assertThat(request.body?.utf8()).isEqualTo(\"request\")\n  }\n\n  @Test\n  fun multiple1xxResponses() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .add100Continue()\n        .add100Continue()\n        .body(\"response\")\n        .build(),\n    )\n    val url = server.url(\"/\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    connection.doOutput = true\n    connection.outputStream.write(\"request\".toByteArray(UTF_8))\n    val inputStream = connection.inputStream\n    val reader = BufferedReader(InputStreamReader(inputStream, UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"response\")\n    val request = server.takeRequest()\n    assertThat(request.body?.utf8()).isEqualTo(\"request\")\n  }\n\n  @Test\n  fun testH2PriorKnowledgeServerFallback() {\n    try {\n      server.protocols = listOf(Protocol.H2_PRIOR_KNOWLEDGE, Protocol.HTTP_1_1)\n      fail<Unit>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\n        \"protocols containing h2_prior_knowledge cannot use other protocols: \" +\n          \"[h2_prior_knowledge, http/1.1]\",\n      )\n    }\n  }\n\n  @Test\n  fun testH2PriorKnowledgeServerDuplicates() {\n    try {\n      // Treating this use case as user error\n      server.protocols = listOf(Protocol.H2_PRIOR_KNOWLEDGE, Protocol.H2_PRIOR_KNOWLEDGE)\n      fail<Unit>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\n        \"protocols containing h2_prior_knowledge cannot use other protocols: \" +\n          \"[h2_prior_knowledge, h2_prior_knowledge]\",\n      )\n    }\n  }\n\n  @Test\n  fun testMockWebServerH2PriorKnowledgeProtocol() {\n    server.protocols = listOf(Protocol.H2_PRIOR_KNOWLEDGE)\n    assertThat(server.protocols.size).isEqualTo(1)\n    assertThat(server.protocols[0]).isEqualTo(Protocol.H2_PRIOR_KNOWLEDGE)\n  }\n\n  @Test\n  fun https() {\n    val handshakeCertificates = platform.localhostHandshakeCertificates()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val connection = url.toUrl().openConnection() as HttpsURLConnection\n    connection.sslSocketFactory = handshakeCertificates.sslSocketFactory()\n    connection.hostnameVerifier = RecordingHostnameVerifier()\n    assertThat(connection.responseCode).isEqualTo(HttpURLConnection.HTTP_OK)\n    val reader = BufferedReader(InputStreamReader(connection.inputStream, UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"abc\")\n    val request = server.takeRequest()\n    assertThat(request.url.scheme).isEqualTo(\"https\")\n    val handshake = request.handshake\n    assertThat(handshake!!.tlsVersion).isNotNull()\n    assertThat(handshake.cipherSuite).isNotNull()\n    assertThat(handshake.localPrincipal).isNotNull()\n    assertThat(handshake.localCertificates.size).isEqualTo(1)\n    assertThat(handshake.peerPrincipal).isNull()\n    assertThat(handshake.peerCertificates.size).isEqualTo(0)\n  }\n\n  @Test\n  fun httpsWithClientAuth() {\n    platform.assumeNotBouncyCastle()\n    platform.assumeNotConscrypt()\n\n    val clientCa =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .build()\n    val serverCa =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .build()\n    val serverCertificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(serverCa)\n        .addSubjectAlternativeName(server.hostName)\n        .build()\n    val serverHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(clientCa.certificate)\n        .heldCertificate(serverCertificate)\n        .build()\n    server.useHttps(serverHandshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    server.requestClientAuth()\n    val clientCertificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(clientCa)\n        .build()\n    val clientHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(serverCa.certificate)\n        .heldCertificate(clientCertificate)\n        .build()\n    val url = server.url(\"/\")\n    val connection = url.toUrl().openConnection() as HttpsURLConnection\n    connection.sslSocketFactory = clientHandshakeCertificates.sslSocketFactory()\n    connection.hostnameVerifier = RecordingHostnameVerifier()\n    assertThat(connection.responseCode).isEqualTo(HttpURLConnection.HTTP_OK)\n    val reader = BufferedReader(InputStreamReader(connection.inputStream, UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"abc\")\n    val request = server.takeRequest()\n    assertThat(request.url.scheme).isEqualTo(\"https\")\n    val handshake = request.handshake\n    assertThat(handshake!!.tlsVersion).isNotNull()\n    assertThat(handshake.cipherSuite).isNotNull()\n    assertThat(handshake.localPrincipal).isNotNull()\n    assertThat(handshake.localCertificates.size).isEqualTo(1)\n    assertThat(handshake.peerPrincipal).isNotNull()\n    assertThat(handshake.peerCertificates.size).isEqualTo(1)\n  }\n\n  @Test\n  fun proxiedRequestGetsCorrectRequestUrl() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Result\")\n        .build(),\n    )\n    val proxiedClient =\n      OkHttpClient\n        .Builder()\n        .proxy(server.proxyAddress)\n        .readTimeout(Duration.ofMillis(100))\n        .build()\n    val request = Request.Builder().url(\"http://android.com/\").build()\n    proxiedClient.newCall(request).execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"Result\")\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.url).isEqualTo(\"http://android.com/\".toHttpUrl())\n  }\n\n  @Test\n  fun startTwice() {\n    val server2 = MockWebServer()\n    server2.start()\n    server2.start()\n    server2.close()\n  }\n\n  @Test\n  fun closeTwice() {\n    val server2 = MockWebServer()\n    server2.start()\n    server2.close()\n    assertFailsWith<IllegalStateException> {\n      server2.start()\n    }\n    server2.close()\n  }\n\n  @Test\n  fun recordedBodyIsNullForGetRequests() {\n    server.enqueue(MockResponse())\n    val client = OkHttpClient()\n    val request =\n      Request(\n        url = server.url(\"/\"),\n      )\n    client.newCall(request).execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"\")\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body).isNull()\n  }\n\n  @Test\n  fun recordedBodyIsNullWithDoNotRead() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .doNotReadRequestBody()\n        .build(),\n    )\n    val client = OkHttpClient()\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"hello\".toRequestBody(),\n      )\n    client.newCall(request).execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"\")\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body).isNull()\n  }\n\n  @Test\n  fun recordedBodyIsEmptyForEmptyPostRequests() {\n    server.enqueue(MockResponse())\n    val client = OkHttpClient()\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"\".toRequestBody(),\n      )\n    client.newCall(request).execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"\")\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body).isEqualTo(ByteString.EMPTY)\n  }\n\n  @Test\n  fun recordedBodyIsNonEmptyForNonEmptyPostRequests() {\n    server.enqueue(MockResponse())\n    val client = OkHttpClient()\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"hello\".toRequestBody(),\n      )\n    client.newCall(request).execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"\")\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body).isEqualTo(\"hello\".encodeUtf8())\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/test/java/mockwebserver3/RecordedRequestTest.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage mockwebserver3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.net.InetAddress\nimport java.net.Socket\nimport mockwebserver3.internal.DEFAULT_REQUEST_LINE_HTTP_1\nimport mockwebserver3.internal.MockWebServerSocket\nimport mockwebserver3.internal.RecordedRequest\nimport mockwebserver3.internal.decodeRequestLine\nimport okhttp3.Headers\nimport okhttp3.Headers.Companion.headersOf\nimport okio.Buffer\nimport okio.ByteString\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\n\n@Timeout(30)\nclass RecordedRequestTest {\n  private val headers: Headers = Headers.EMPTY\n\n  @Test fun testIPv4() {\n    val socket =\n      MockWebServerSocket(\n        FakeSocket(\n          localAddress = InetAddress.getByAddress(\"127.0.0.1\", byteArrayOf(127, 0, 0, 1)),\n          localPort = 80,\n        ),\n      )\n    val request = RecordedRequest(DEFAULT_REQUEST_LINE_HTTP_1, headers, emptyList(), 0, ByteString.EMPTY, 0, 0, socket)\n    assertThat(request.url.toString()).isEqualTo(\"http://127.0.0.1/\")\n  }\n\n  @Test fun testAuthorityForm() {\n    val socket =\n      MockWebServerSocket(\n        FakeSocket(\n          localAddress = InetAddress.getByAddress(\"127.0.0.1\", byteArrayOf(127, 0, 0, 1)),\n          localPort = 80,\n        ),\n      )\n    val requestLine = decodeRequestLine(\"CONNECT example.com:8080 HTTP/1.1\")\n    val request = RecordedRequest(requestLine, headers, emptyList(), 0, ByteString.EMPTY, 0, 0, socket)\n    assertThat(request.target).isEqualTo(\"example.com:8080\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com:8080/\")\n  }\n\n  @Test fun testAbsoluteForm() {\n    val socket =\n      MockWebServerSocket(\n        FakeSocket(\n          localAddress = InetAddress.getByAddress(\"127.0.0.1\", byteArrayOf(127, 0, 0, 1)),\n          localPort = 80,\n        ),\n      )\n    val requestLine = decodeRequestLine(\"GET http://example.com:8080/index.html HTTP/1.1\")\n    val request = RecordedRequest(requestLine, headers, emptyList(), 0, ByteString.EMPTY, 0, 0, socket)\n    assertThat(request.target).isEqualTo(\"http://example.com:8080/index.html\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com:8080/index.html\")\n  }\n\n  @Test fun testAsteriskForm() {\n    val socket =\n      MockWebServerSocket(\n        FakeSocket(\n          localAddress = InetAddress.getByAddress(\"127.0.0.1\", byteArrayOf(127, 0, 0, 1)),\n          localPort = 80,\n        ),\n      )\n    val requestLine = decodeRequestLine(\"OPTIONS * HTTP/1.1\")\n    val request =\n      RecordedRequest(\n        requestLine,\n        headers,\n        emptyList(),\n        0,\n        ByteString.EMPTY,\n        0,\n        0,\n        socket,\n      )\n    assertThat(request.target).isEqualTo(\"*\")\n    assertThat(request.url.toString()).isEqualTo(\"http://127.0.0.1/\")\n  }\n\n  @Test fun testIpv6() {\n    val socket =\n      MockWebServerSocket(\n        FakeSocket(\n          localAddress =\n            InetAddress.getByAddress(\n              \"::1\",\n              byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),\n            ),\n          localPort = 80,\n        ),\n      )\n    val request = RecordedRequest(DEFAULT_REQUEST_LINE_HTTP_1, headers, emptyList(), 0, ByteString.EMPTY, 0, 0, socket)\n    assertThat(request.url.toString()).isEqualTo(\"http://[::1]/\")\n  }\n\n  @Test fun testUsesLocal() {\n    val socket =\n      MockWebServerSocket(\n        FakeSocket(\n          localAddress = InetAddress.getByAddress(\"127.0.0.1\", byteArrayOf(127, 0, 0, 1)),\n          localPort = 80,\n        ),\n      )\n    val request = RecordedRequest(DEFAULT_REQUEST_LINE_HTTP_1, headers, emptyList(), 0, ByteString.EMPTY, 0, 0, socket)\n    assertThat(request.url.toString()).isEqualTo(\"http://127.0.0.1/\")\n  }\n\n  @Test fun testHostname() {\n    val headers = headersOf(\"Host\", \"host-from-header.com\")\n    val socket =\n      MockWebServerSocket(\n        FakeSocket(\n          localAddress =\n            InetAddress.getByAddress(\n              \"host-from-address.com\",\n              byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),\n            ),\n          localPort = 80,\n        ),\n      )\n    val request = RecordedRequest(DEFAULT_REQUEST_LINE_HTTP_1, headers, emptyList(), 0, ByteString.EMPTY, 0, 0, socket)\n    assertThat(request.url.toString()).isEqualTo(\"http://host-from-header.com/\")\n  }\n\n  private class FakeSocket(\n    private val localAddress: InetAddress,\n    private val localPort: Int,\n    private val remoteAddress: InetAddress = localAddress,\n    private val remotePort: Int = 1234,\n  ) : Socket() {\n    override fun getInputStream() = Buffer().inputStream()\n\n    override fun getOutputStream() = Buffer().outputStream()\n\n    override fun getInetAddress() = remoteAddress\n\n    override fun getLocalAddress() = localAddress\n\n    override fun getLocalPort() = localPort\n\n    override fun getPort() = remotePort\n  }\n}\n"
  },
  {
    "path": "mockwebserver/src/test/java/mockwebserver3/internal/http2/Http2Server.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3.internal.http2\n\nimport java.io.File\nimport java.io.IOException\nimport java.net.InetSocketAddress\nimport java.net.ProtocolException\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.util.logging.Level\nimport java.util.logging.Logger\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.Protocol\nimport okhttp3.Protocol.Companion.get\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.connection.asBufferedSocket\nimport okhttp3.internal.http2.Header\nimport okhttp3.internal.http2.Http2Connection\nimport okhttp3.internal.http2.Http2Stream\nimport okhttp3.internal.platform.Platform\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport okio.buffer\nimport okio.source\n\n/** A basic HTTP/2 server that serves the contents of a local directory.  */\nclass Http2Server(\n  private val baseDirectory: File,\n  private val sslSocketFactory: SSLSocketFactory,\n) : Http2Connection.Listener() {\n  private fun run() {\n    val serverSocket = ServerSocket(8888)\n    serverSocket.reuseAddress = true\n    while (true) {\n      var socket: Socket? = null\n      try {\n        socket = serverSocket.accept()\n        val sslSocket = doSsl(socket)\n        val protocolString = Platform.get().getSelectedProtocol(sslSocket)\n        val protocol = if (protocolString != null) get(protocolString) else null\n        if (protocol != Protocol.HTTP_2) {\n          throw ProtocolException(\"Protocol $protocol unsupported\")\n        }\n        val connection =\n          Http2Connection\n            .Builder(false, TaskRunner.INSTANCE)\n            .socket(sslSocket.asBufferedSocket(), sslSocket.peerName())\n            .listener(this)\n            .build()\n        connection.start()\n      } catch (e: IOException) {\n        logger.log(Level.INFO, \"Http2Server connection failure: $e\")\n        socket?.closeQuietly()\n      } catch (e: Exception) {\n        logger.log(Level.WARNING, \"Http2Server unexpected failure\", e)\n        socket?.closeQuietly()\n      }\n    }\n  }\n\n  private fun doSsl(socket: Socket): SSLSocket {\n    val sslSocket =\n      sslSocketFactory.createSocket(\n        socket,\n        socket.inetAddress.hostAddress,\n        socket.port,\n        true,\n      ) as SSLSocket\n    sslSocket.useClientMode = false\n    Platform.get().configureTlsExtensions(sslSocket, null, listOf(Protocol.HTTP_2))\n    sslSocket.startHandshake()\n    return sslSocket\n  }\n\n  override fun onStream(stream: Http2Stream) {\n    try {\n      val requestHeaders = stream.takeHeaders()\n      var path: String? = null\n      var i = 0\n      val size = requestHeaders.size\n      while (i < size) {\n        if (requestHeaders.name(i) == Header.TARGET_PATH_UTF8) {\n          path = requestHeaders.value(i)\n          break\n        }\n        i++\n      }\n      if (path == null) {\n        // TODO: send bad request error\n        throw AssertionError()\n      }\n      val file = File(baseDirectory.toString() + path)\n      if (file.isDirectory) {\n        serveDirectory(stream, file.listFiles()!!)\n      } else if (file.exists()) {\n        serveFile(stream, file)\n      } else {\n        send404(stream, path)\n      }\n    } catch (e: IOException) {\n      Platform.get().log(\"Failure serving Http2Stream: \" + e.message, Platform.INFO, null)\n    }\n  }\n\n  private fun send404(\n    stream: Http2Stream,\n    path: String,\n  ) {\n    val responseHeaders =\n      listOf(\n        Header(\":status\", \"404\"),\n        Header(\":version\", \"HTTP/1.1\"),\n        Header(\"content-type\", \"text/plain\"),\n      )\n    stream.writeHeaders(\n      responseHeaders = responseHeaders,\n      outFinished = false,\n      flushHeaders = false,\n    )\n    val out = stream.sink.buffer()\n    out.writeUtf8(\"Not found: $path\")\n    out.close()\n  }\n\n  private fun serveDirectory(\n    stream: Http2Stream,\n    files: Array<File>,\n  ) {\n    val responseHeaders =\n      listOf(\n        Header(\":status\", \"200\"),\n        Header(\":version\", \"HTTP/1.1\"),\n        Header(\"content-type\", \"text/html; charset=UTF-8\"),\n      )\n    stream.writeHeaders(\n      responseHeaders = responseHeaders,\n      outFinished = false,\n      flushHeaders = false,\n    )\n    val out = stream.sink.buffer()\n    for (file in files) {\n      val target = if (file.isDirectory) file.name + \"/\" else file.name\n      out.writeUtf8(\"<a href='$target'>$target</a><br>\")\n    }\n    out.close()\n  }\n\n  private fun serveFile(\n    stream: Http2Stream,\n    file: File,\n  ) {\n    val responseHeaders =\n      listOf(\n        Header(\":status\", \"200\"),\n        Header(\":version\", \"HTTP/1.1\"),\n        Header(\"content-type\", contentType(file)),\n      )\n    stream.writeHeaders(\n      responseHeaders = responseHeaders,\n      outFinished = false,\n      flushHeaders = false,\n    )\n    file.source().use { source ->\n      stream.sink.buffer().use { sink ->\n        sink.writeAll(source)\n      }\n    }\n  }\n\n  private fun contentType(file: File): String =\n    when {\n      file.name.endsWith(\".css\") -> \"text/css\"\n      file.name.endsWith(\".gif\") -> \"image/gif\"\n      file.name.endsWith(\".html\") -> \"text/html\"\n      file.name.endsWith(\".jpeg\") -> \"image/jpeg\"\n      file.name.endsWith(\".jpg\") -> \"image/jpeg\"\n      file.name.endsWith(\".js\") -> \"application/javascript\"\n      file.name.endsWith(\".png\") -> \"image/png\"\n      else -> \"text/plain\"\n    }\n\n  private fun Socket.peerName(): String {\n    val address = remoteSocketAddress\n    return if (address is InetSocketAddress) address.hostName else address.toString()\n  }\n\n  companion object {\n    val logger: Logger = Logger.getLogger(Http2Server::class.java.name)\n\n    @JvmStatic\n    fun main(args: Array<String>) {\n      if (args.size != 1 || args[0].startsWith(\"-\")) {\n        println(\"Usage: Http2Server <base directory>\")\n        return\n      }\n      val server =\n        Http2Server(\n          File(args[0]),\n          localhost().sslContext().socketFactory,\n        )\n      server.run()\n    }\n  }\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/api/mockwebserver.api",
    "content": "public abstract class okhttp3/mockwebserver/Dispatcher {\n\tpublic fun <init> ()V\n\tpublic abstract fun dispatch (Lokhttp3/mockwebserver/RecordedRequest;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic fun peek ()Lokhttp3/mockwebserver/MockResponse;\n\tpublic fun shutdown ()V\n}\n\npublic final class okhttp3/mockwebserver/MockResponse : java/lang/Cloneable {\n\tpublic static final field Companion Lokhttp3/mockwebserver/MockResponse$Companion;\n\tpublic final fun -deprecated_getHeaders ()Lokhttp3/Headers;\n\tpublic final fun -deprecated_getHttp2ErrorCode ()I\n\tpublic final fun -deprecated_getSocketPolicy ()Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic final fun -deprecated_getStatus ()Ljava/lang/String;\n\tpublic final fun -deprecated_getTrailers ()Lokhttp3/Headers;\n\tpublic fun <init> ()V\n\tpublic final fun addHeader (Ljava/lang/String;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun addHeader (Ljava/lang/String;Ljava/lang/Object;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun addHeaderLenient (Ljava/lang/String;Ljava/lang/Object;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun clearHeaders ()Lokhttp3/mockwebserver/MockResponse;\n\tpublic synthetic fun clone ()Ljava/lang/Object;\n\tpublic fun clone ()Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun getBody ()Lokio/Buffer;\n\tpublic final fun getBodyDelay (Ljava/util/concurrent/TimeUnit;)J\n\tpublic final fun getHeaders ()Lokhttp3/Headers;\n\tpublic final fun getHeadersDelay (Ljava/util/concurrent/TimeUnit;)J\n\tpublic final fun getHttp2ErrorCode ()I\n\tpublic final fun getPushPromises ()Ljava/util/List;\n\tpublic final fun getSettings ()Lokhttp3/internal/http2/Settings;\n\tpublic final fun getSocketPolicy ()Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic final fun getStatus ()Ljava/lang/String;\n\tpublic final fun getThrottleBytesPerPeriod ()J\n\tpublic final fun getThrottlePeriod (Ljava/util/concurrent/TimeUnit;)J\n\tpublic final fun getTrailers ()Lokhttp3/Headers;\n\tpublic final fun getWebSocketListener ()Lokhttp3/WebSocketListener;\n\tpublic final fun headers (Lokhttp3/Headers;)V\n\tpublic final fun http2ErrorCode (I)V\n\tpublic final fun removeHeader (Ljava/lang/String;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setBody (Ljava/lang/String;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setBody (Lokio/Buffer;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setBodyDelay (JLjava/util/concurrent/TimeUnit;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setChunkedBody (Ljava/lang/String;I)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setChunkedBody (Lokio/Buffer;I)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setHeader (Ljava/lang/String;Ljava/lang/Object;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setHeaders (Lokhttp3/Headers;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setHeadersDelay (JLjava/util/concurrent/TimeUnit;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setHttp2ErrorCode (I)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setResponseCode (I)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setSocketPolicy (Lokhttp3/mockwebserver/SocketPolicy;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setStatus (Ljava/lang/String;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setTrailers (Lokhttp3/Headers;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun socketPolicy (Lokhttp3/mockwebserver/SocketPolicy;)V\n\tpublic final fun status (Ljava/lang/String;)V\n\tpublic final fun throttleBody (JJLjava/util/concurrent/TimeUnit;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun trailers (Lokhttp3/Headers;)V\n\tpublic final fun withPush (Lokhttp3/mockwebserver/PushPromise;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun withSettings (Lokhttp3/internal/http2/Settings;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun withWebSocketUpgrade (Lokhttp3/WebSocketListener;)Lokhttp3/mockwebserver/MockResponse;\n}\n\npublic final class okhttp3/mockwebserver/MockResponse$Companion {\n}\n\npublic final class okhttp3/mockwebserver/MockWebServer : org/junit/rules/ExternalResource, java/io/Closeable {\n\tpublic static final field Companion Lokhttp3/mockwebserver/MockWebServer$Companion;\n\tpublic final fun -deprecated_bodyLimit (J)V\n\tpublic final fun -deprecated_port ()I\n\tpublic final fun -deprecated_protocolNegotiationEnabled (Z)V\n\tpublic final fun -deprecated_protocols ()Ljava/util/List;\n\tpublic final fun -deprecated_protocols (Ljava/util/List;)V\n\tpublic final fun -deprecated_requestCount ()I\n\tpublic final fun -deprecated_serverSocketFactory (Ljavax/net/ServerSocketFactory;)V\n\tpublic fun <init> ()V\n\tpublic fun close ()V\n\tpublic final fun enqueue (Lokhttp3/mockwebserver/MockResponse;)V\n\tpublic final fun getBodyLimit ()J\n\tpublic final fun getDelegate ()Lmockwebserver3/MockWebServer;\n\tpublic final fun getDispatcher ()Lokhttp3/mockwebserver/Dispatcher;\n\tpublic final fun getHostName ()Ljava/lang/String;\n\tpublic final fun getPort ()I\n\tpublic final fun getProtocolNegotiationEnabled ()Z\n\tpublic final fun getRequestCount ()I\n\tpublic final fun getServerSocketFactory ()Ljavax/net/ServerSocketFactory;\n\tpublic final fun noClientAuth ()V\n\tpublic final fun protocols ()Ljava/util/List;\n\tpublic final fun requestClientAuth ()V\n\tpublic final fun requireClientAuth ()V\n\tpublic final fun setBodyLimit (J)V\n\tpublic final fun setDispatcher (Lokhttp3/mockwebserver/Dispatcher;)V\n\tpublic final fun setProtocolNegotiationEnabled (Z)V\n\tpublic final fun setProtocols (Ljava/util/List;)V\n\tpublic final fun setServerSocketFactory (Ljavax/net/ServerSocketFactory;)V\n\tpublic final fun shutdown ()V\n\tpublic final fun start ()V\n\tpublic final fun start (I)V\n\tpublic final fun start (Ljava/net/InetAddress;I)V\n\tpublic static synthetic fun start$default (Lokhttp3/mockwebserver/MockWebServer;IILjava/lang/Object;)V\n\tpublic final fun takeRequest ()Lokhttp3/mockwebserver/RecordedRequest;\n\tpublic final fun takeRequest (JLjava/util/concurrent/TimeUnit;)Lokhttp3/mockwebserver/RecordedRequest;\n\tpublic final fun toProxyAddress ()Ljava/net/Proxy;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun url (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun useHttps (Ljavax/net/ssl/SSLSocketFactory;Z)V\n}\n\npublic final class okhttp3/mockwebserver/MockWebServer$Companion {\n}\n\npublic final class okhttp3/mockwebserver/PushPromise {\n\tpublic final fun -deprecated_headers ()Lokhttp3/Headers;\n\tpublic final fun -deprecated_method ()Ljava/lang/String;\n\tpublic final fun -deprecated_path ()Ljava/lang/String;\n\tpublic final fun -deprecated_response ()Lokhttp3/mockwebserver/MockResponse;\n\tpublic fun <init> (Ljava/lang/String;Ljava/lang/String;Lokhttp3/Headers;Lokhttp3/mockwebserver/MockResponse;)V\n\tpublic final fun headers ()Lokhttp3/Headers;\n\tpublic final fun method ()Ljava/lang/String;\n\tpublic final fun path ()Ljava/lang/String;\n\tpublic final fun response ()Lokhttp3/mockwebserver/MockResponse;\n}\n\npublic final class okhttp3/mockwebserver/QueueDispatcher : okhttp3/mockwebserver/Dispatcher {\n\tpublic fun <init> ()V\n\tpublic fun dispatch (Lokhttp3/mockwebserver/RecordedRequest;)Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun enqueueResponse (Lokhttp3/mockwebserver/MockResponse;)V\n\tpublic fun peek ()Lokhttp3/mockwebserver/MockResponse;\n\tpublic final fun setFailFast (Lokhttp3/mockwebserver/MockResponse;)V\n\tpublic final fun setFailFast (Z)V\n\tpublic fun shutdown ()V\n}\n\npublic final class okhttp3/mockwebserver/RecordedRequest {\n\tpublic final fun -deprecated_utf8Body ()Ljava/lang/String;\n\tpublic fun <init> (Ljava/lang/String;Lokhttp3/Headers;Ljava/util/List;JLokio/Buffer;ILjava/net/Socket;)V\n\tpublic fun <init> (Ljava/lang/String;Lokhttp3/Headers;Ljava/util/List;JLokio/Buffer;ILjava/net/Socket;Ljava/io/IOException;)V\n\tpublic synthetic fun <init> (Ljava/lang/String;Lokhttp3/Headers;Ljava/util/List;JLokio/Buffer;ILjava/net/Socket;Ljava/io/IOException;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun getBody ()Lokio/Buffer;\n\tpublic final fun getBodySize ()J\n\tpublic final fun getChunkSizes ()Ljava/util/List;\n\tpublic final fun getFailure ()Ljava/io/IOException;\n\tpublic final fun getHandshake ()Lokhttp3/Handshake;\n\tpublic final fun getHeader (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun getHeaders ()Lokhttp3/Headers;\n\tpublic final fun getMethod ()Ljava/lang/String;\n\tpublic final fun getPath ()Ljava/lang/String;\n\tpublic final fun getRequestLine ()Ljava/lang/String;\n\tpublic final fun getRequestUrl ()Lokhttp3/HttpUrl;\n\tpublic final fun getSequenceNumber ()I\n\tpublic final fun getTlsVersion ()Lokhttp3/TlsVersion;\n\tpublic final fun getUtf8Body ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/mockwebserver/SocketPolicy : java/lang/Enum {\n\tpublic static final field CONTINUE_ALWAYS Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field DISCONNECT_AFTER_REQUEST Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field DISCONNECT_AT_END Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field DISCONNECT_AT_START Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field DISCONNECT_DURING_REQUEST_BODY Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field DISCONNECT_DURING_RESPONSE_BODY Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field DO_NOT_READ_REQUEST_BODY Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field EXPECT_CONTINUE Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field FAIL_HANDSHAKE Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field KEEP_OPEN Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field NO_RESPONSE Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field RESET_STREAM_AT_START Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field SHUTDOWN_INPUT_AT_END Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field SHUTDOWN_OUTPUT_AT_END Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field SHUTDOWN_SERVER_AFTER_RESPONSE Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field STALL_SOCKET_AT_START Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static final field UPGRADE_TO_SSL_AT_END Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static fun getEntries ()Lkotlin/enums/EnumEntries;\n\tpublic static fun valueOf (Ljava/lang/String;)Lokhttp3/mockwebserver/SocketPolicy;\n\tpublic static fun values ()[Lokhttp3/mockwebserver/SocketPolicy;\n}\n\n"
  },
  {
    "path": "mockwebserver-deprecated/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyJavaModules(\"okhttp3.mockwebserver\")\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n  api(projects.mockwebserver3)\n  api(libs.junit)\n  api(libs.square.okio)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(projects.okhttpTls)\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.mockwebserver {\n  requires okhttp3;\n  exports okhttp3.mockwebserver;\n  requires java.logging;\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/kotlin/okhttp3/mockwebserver/DeprecationBridge.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nimport java.util.concurrent.TimeUnit.MILLISECONDS\nimport mockwebserver3.SocketEffect\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.SocketEffect.CloseStream\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport okio.Buffer\nimport okio.ByteString\n\ninternal fun Dispatcher.wrap(): mockwebserver3.Dispatcher {\n  if (this is QueueDispatcher) return this.delegate\n\n  val delegate = this\n  return object : mockwebserver3.Dispatcher() {\n    override fun dispatch(request: mockwebserver3.RecordedRequest): mockwebserver3.MockResponse = delegate.dispatch(request.unwrap()).wrap()\n\n    override fun peek(): mockwebserver3.MockResponse = delegate.peek().wrap()\n\n    override fun close() {\n      delegate.shutdown()\n    }\n  }\n}\n\ninternal fun MockResponse.wrap(): mockwebserver3.MockResponse {\n  val result = mockwebserver3.MockResponse.Builder()\n  val copyFromWebSocketListener = webSocketListener\n  if (copyFromWebSocketListener != null) {\n    result.webSocketUpgrade(copyFromWebSocketListener)\n  }\n\n  val body = getBody()\n  if (body != null) result.body(body)\n\n  for (pushPromise in pushPromises) {\n    result.addPush(pushPromise.wrap())\n  }\n\n  result.settings(settings)\n  result.status(status)\n  result.headers(headers)\n  result.trailers(trailers)\n\n  when (socketPolicy) {\n    SocketPolicy.EXPECT_CONTINUE, SocketPolicy.CONTINUE_ALWAYS -> {\n      result.add100Continue()\n    }\n\n    SocketPolicy.UPGRADE_TO_SSL_AT_END -> {\n      result.inTunnel()\n    }\n\n    SocketPolicy.SHUTDOWN_SERVER_AFTER_RESPONSE -> {\n      result.shutdownServer(true)\n    }\n\n    SocketPolicy.KEEP_OPEN -> {\n      Unit\n    }\n\n    SocketPolicy.DISCONNECT_AT_END -> {\n      result.onResponseEnd(ShutdownConnection)\n    }\n\n    SocketPolicy.DISCONNECT_AT_START -> {\n      result.onRequestStart(CloseSocket())\n    }\n\n    SocketPolicy.DISCONNECT_AFTER_REQUEST -> {\n      result.onResponseStart(CloseSocket())\n    }\n\n    SocketPolicy.DISCONNECT_DURING_REQUEST_BODY -> {\n      result.onRequestBody(CloseSocket())\n    }\n\n    SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY -> {\n      result.onResponseBody(CloseSocket())\n    }\n\n    SocketPolicy.DO_NOT_READ_REQUEST_BODY -> {\n      result.doNotReadRequestBody()\n    }\n\n    SocketPolicy.FAIL_HANDSHAKE -> {\n      result.failHandshake()\n    }\n\n    SocketPolicy.SHUTDOWN_INPUT_AT_END -> {\n      result.onResponseEnd(\n        CloseSocket(\n          closeSocket = false,\n          shutdownInput = true,\n        ),\n      )\n    }\n\n    SocketPolicy.SHUTDOWN_OUTPUT_AT_END -> {\n      result.onResponseEnd(\n        CloseSocket(\n          closeSocket = false,\n          shutdownOutput = true,\n        ),\n      )\n    }\n\n    SocketPolicy.STALL_SOCKET_AT_START -> {\n      result.onRequestStart(SocketEffect.Stall)\n    }\n\n    SocketPolicy.NO_RESPONSE -> {\n      result.onResponseStart(SocketEffect.Stall)\n    }\n\n    SocketPolicy.RESET_STREAM_AT_START -> {\n      result.onRequestStart(CloseStream(http2ErrorCode))\n    }\n  }\n\n  result.throttleBody(throttleBytesPerPeriod, getThrottlePeriod(MILLISECONDS), MILLISECONDS)\n  result.bodyDelay(getBodyDelay(MILLISECONDS), MILLISECONDS)\n  result.headersDelay(getHeadersDelay(MILLISECONDS), MILLISECONDS)\n  return result.build()\n}\n\nprivate fun PushPromise.wrap(): mockwebserver3.PushPromise =\n  mockwebserver3.PushPromise(\n    method = method,\n    path = path,\n    headers = headers,\n    response = response.wrap(),\n  )\n\ninternal fun mockwebserver3.RecordedRequest.unwrap(): RecordedRequest =\n  RecordedRequest(\n    requestLine = requestLine,\n    headers = headers,\n    chunkSizes = chunkSizes ?: listOf(),\n    bodySize = bodySize,\n    body = Buffer().write(body ?: ByteString.EMPTY),\n    sequenceNumber = exchangeIndex,\n    failure = failure,\n    method = method,\n    path = target,\n    handshake = handshake,\n    requestUrl = url,\n  )\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/kotlin/okhttp3/mockwebserver/Dispatcher.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nabstract class Dispatcher {\n  @Throws(InterruptedException::class)\n  abstract fun dispatch(request: RecordedRequest): MockResponse\n\n  open fun peek(): MockResponse = MockResponse().apply { this.socketPolicy = SocketPolicy.KEEP_OPEN }\n\n  open fun shutdown() {}\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/kotlin/okhttp3/mockwebserver/MockResponse.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.mockwebserver\n\nimport java.util.concurrent.TimeUnit\nimport okhttp3.Headers\nimport okhttp3.WebSocketListener\nimport okhttp3.internal.addHeaderLenient\nimport okhttp3.internal.http2.Settings\nimport okio.Buffer\n\nclass MockResponse : Cloneable {\n  @set:JvmName(\"status\")\n  var status: String = \"\"\n\n  private var headersBuilder = Headers.Builder()\n  private var trailersBuilder = Headers.Builder()\n\n  @set:JvmName(\"headers\")\n  var headers: Headers\n    get() = headersBuilder.build()\n    set(value) {\n      this.headersBuilder = value.newBuilder()\n    }\n\n  @set:JvmName(\"trailers\")\n  var trailers: Headers\n    get() = trailersBuilder.build()\n    set(value) {\n      this.trailersBuilder = value.newBuilder()\n    }\n\n  private var body: Buffer? = null\n\n  var throttleBytesPerPeriod: Long = Long.MAX_VALUE\n    private set\n  private var throttlePeriodAmount = 1L\n  private var throttlePeriodUnit = TimeUnit.SECONDS\n\n  @set:JvmName(\"socketPolicy\")\n  var socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN\n\n  @set:JvmName(\"http2ErrorCode\")\n  var http2ErrorCode: Int = -1\n\n  private var bodyDelayAmount = 0L\n  private var bodyDelayUnit = TimeUnit.MILLISECONDS\n\n  private var headersDelayAmount = 0L\n  private var headersDelayUnit = TimeUnit.MILLISECONDS\n\n  private var promises = mutableListOf<PushPromise>()\n  var settings: Settings = Settings()\n    private set\n  var webSocketListener: WebSocketListener? = null\n    private set\n\n  val pushPromises: List<PushPromise>\n    get() = promises\n\n  init {\n    setResponseCode(200)\n    setHeader(\"Content-Length\", 0L)\n  }\n\n  public override fun clone(): MockResponse {\n    val result = super.clone() as MockResponse\n    result.headersBuilder = headersBuilder.build().newBuilder()\n    result.promises = promises.toMutableList()\n    return result\n  }\n\n  @JvmName(\"-deprecated_getStatus\")\n  @Deprecated(\n    message = \"moved to var\",\n    replaceWith = ReplaceWith(expression = \"status\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun getStatus(): String = status\n\n  fun setStatus(status: String) =\n    apply {\n      this.status = status\n    }\n\n  fun setResponseCode(code: Int): MockResponse {\n    val reason =\n      when (code) {\n        in 100..199 -> \"Informational\"\n        in 200..299 -> \"OK\"\n        in 300..399 -> \"Redirection\"\n        in 400..499 -> \"Client Error\"\n        in 500..599 -> \"Server Error\"\n        else -> \"Mock Response\"\n      }\n    return apply { status = \"HTTP/1.1 $code $reason\" }\n  }\n\n  fun clearHeaders() =\n    apply {\n      headersBuilder = Headers.Builder()\n    }\n\n  fun addHeader(header: String) =\n    apply {\n      headersBuilder.add(header)\n    }\n\n  fun addHeader(\n    name: String,\n    value: Any,\n  ) = apply {\n    headersBuilder.add(name, value.toString())\n  }\n\n  fun addHeaderLenient(\n    name: String,\n    value: Any,\n  ) = apply {\n    addHeaderLenient(headersBuilder, name, value.toString())\n  }\n\n  fun setHeader(\n    name: String,\n    value: Any,\n  ) = apply {\n    removeHeader(name)\n    addHeader(name, value)\n  }\n\n  fun removeHeader(name: String) =\n    apply {\n      headersBuilder.removeAll(name)\n    }\n\n  fun getBody(): Buffer? = body?.clone()\n\n  fun setBody(body: Buffer) =\n    apply {\n      setHeader(\"Content-Length\", body.size)\n      this.body = body.clone() // Defensive copy.\n    }\n\n  fun setBody(body: String): MockResponse = setBody(Buffer().writeUtf8(body))\n\n  fun setChunkedBody(\n    body: Buffer,\n    maxChunkSize: Int,\n  ) = apply {\n    removeHeader(\"Content-Length\")\n    headersBuilder.add(CHUNKED_BODY_HEADER)\n\n    val bytesOut = Buffer()\n    while (!body.exhausted()) {\n      val chunkSize = minOf(body.size, maxChunkSize.toLong())\n      bytesOut.writeHexadecimalUnsignedLong(chunkSize)\n      bytesOut.writeUtf8(\"\\r\\n\")\n      bytesOut.write(body, chunkSize)\n      bytesOut.writeUtf8(\"\\r\\n\")\n    }\n    bytesOut.writeUtf8(\"0\\r\\n\") // Last chunk. Trailers follow!\n    this.body = bytesOut\n  }\n\n  fun setChunkedBody(\n    body: String,\n    maxChunkSize: Int,\n  ): MockResponse = setChunkedBody(Buffer().writeUtf8(body), maxChunkSize)\n\n  @JvmName(\"-deprecated_getHeaders\")\n  @Deprecated(\n    message = \"moved to var\",\n    replaceWith = ReplaceWith(expression = \"headers\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun getHeaders(): Headers = headers\n\n  fun setHeaders(headers: Headers) = apply { this.headers = headers }\n\n  @JvmName(\"-deprecated_getTrailers\")\n  @Deprecated(\n    message = \"moved to var\",\n    replaceWith = ReplaceWith(expression = \"trailers\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun getTrailers(): Headers = trailers\n\n  fun setTrailers(trailers: Headers) = apply { this.trailers = trailers }\n\n  @JvmName(\"-deprecated_getSocketPolicy\")\n  @Deprecated(\n    message = \"moved to var\",\n    replaceWith = ReplaceWith(expression = \"socketPolicy\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun getSocketPolicy(): SocketPolicy = socketPolicy\n\n  fun setSocketPolicy(socketPolicy: SocketPolicy) =\n    apply {\n      this.socketPolicy = socketPolicy\n    }\n\n  @JvmName(\"-deprecated_getHttp2ErrorCode\")\n  @Deprecated(\n    message = \"moved to var\",\n    replaceWith = ReplaceWith(expression = \"http2ErrorCode\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun getHttp2ErrorCode(): Int = http2ErrorCode\n\n  fun setHttp2ErrorCode(http2ErrorCode: Int) =\n    apply {\n      this.http2ErrorCode = http2ErrorCode\n    }\n\n  fun throttleBody(\n    bytesPerPeriod: Long,\n    period: Long,\n    unit: TimeUnit,\n  ) = apply {\n    throttleBytesPerPeriod = bytesPerPeriod\n    throttlePeriodAmount = period\n    throttlePeriodUnit = unit\n  }\n\n  fun getThrottlePeriod(unit: TimeUnit): Long = unit.convert(throttlePeriodAmount, throttlePeriodUnit)\n\n  fun setBodyDelay(\n    delay: Long,\n    unit: TimeUnit,\n  ) = apply {\n    bodyDelayAmount = delay\n    bodyDelayUnit = unit\n  }\n\n  fun getBodyDelay(unit: TimeUnit): Long = unit.convert(bodyDelayAmount, bodyDelayUnit)\n\n  fun setHeadersDelay(\n    delay: Long,\n    unit: TimeUnit,\n  ) = apply {\n    headersDelayAmount = delay\n    headersDelayUnit = unit\n  }\n\n  fun getHeadersDelay(unit: TimeUnit): Long = unit.convert(headersDelayAmount, headersDelayUnit)\n\n  fun withPush(promise: PushPromise) =\n    apply {\n      promises.add(promise)\n    }\n\n  fun withSettings(settings: Settings) =\n    apply {\n      this.settings = settings\n    }\n\n  fun withWebSocketUpgrade(listener: WebSocketListener) =\n    apply {\n      status = \"HTTP/1.1 101 Switching Protocols\"\n      setHeader(\"Connection\", \"Upgrade\")\n      setHeader(\"Upgrade\", \"websocket\")\n      body = null\n      webSocketListener = listener\n    }\n\n  override fun toString(): String = status\n\n  companion object {\n    private const val CHUNKED_BODY_HEADER = \"Transfer-encoding: chunked\"\n  }\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/kotlin/okhttp3/mockwebserver/MockWebServer.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.Proxy\nimport java.util.concurrent.TimeUnit\nimport java.util.logging.Level\nimport java.util.logging.Logger\nimport javax.net.ServerSocketFactory\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.HttpUrl\nimport okhttp3.Protocol\nimport org.junit.rules.ExternalResource\n\nclass MockWebServer :\n  ExternalResource(),\n  Closeable {\n  val delegate = mockwebserver3.MockWebServer()\n\n  val requestCount: Int by delegate::requestCount\n\n  var bodyLimit: Long by delegate::bodyLimit\n\n  var serverSocketFactory: ServerSocketFactory? by delegate::serverSocketFactory\n\n  var dispatcher: Dispatcher = QueueDispatcher()\n    set(value) {\n      field = value\n      delegate.dispatcher = value.wrap()\n    }\n\n  val port: Int\n    get() {\n      before() // This implicitly starts the delegate.\n      return delegate.port\n    }\n\n  val hostName: String\n    get() {\n      before() // This implicitly starts the delegate.\n      return delegate.hostName\n    }\n\n  var protocolNegotiationEnabled: Boolean by delegate::protocolNegotiationEnabled\n\n  @get:JvmName(\"protocols\")\n  var protocols: List<Protocol> by delegate::protocols\n\n  init {\n    delegate.dispatcher = dispatcher.wrap()\n  }\n\n  private var started: Boolean = false\n\n  @Synchronized override fun before() {\n    if (started) return\n    try {\n      start()\n    } catch (e: IOException) {\n      throw RuntimeException(e)\n    }\n  }\n\n  @JvmName(\"-deprecated_port\")\n  fun getPort(): Int = port\n\n  fun toProxyAddress(): Proxy {\n    before() // This implicitly starts the delegate.\n    return delegate.proxyAddress\n  }\n\n  @JvmName(\"-deprecated_serverSocketFactory\")\n  fun setServerSocketFactory(serverSocketFactory: ServerSocketFactory) {\n    delegate.serverSocketFactory = serverSocketFactory\n  }\n\n  fun url(path: String): HttpUrl {\n    before() // This implicitly starts the delegate.\n    return delegate.url(path)\n  }\n\n  @JvmName(\"-deprecated_bodyLimit\")\n  fun setBodyLimit(bodyLimit: Long) {\n    delegate.bodyLimit = bodyLimit\n  }\n\n  @JvmName(\"-deprecated_protocolNegotiationEnabled\")\n  fun setProtocolNegotiationEnabled(protocolNegotiationEnabled: Boolean) {\n    delegate.protocolNegotiationEnabled = protocolNegotiationEnabled\n  }\n\n  @JvmName(\"-deprecated_protocols\")\n  fun setProtocols(protocols: List<Protocol>) {\n    delegate.protocols = protocols\n  }\n\n  @JvmName(\"-deprecated_protocols\")\n  fun protocols(): List<Protocol> = delegate.protocols\n\n  fun useHttps(\n    sslSocketFactory: SSLSocketFactory,\n    tunnelProxy: Boolean,\n  ) {\n    delegate.useHttps(sslSocketFactory)\n  }\n\n  fun noClientAuth() {\n    delegate.noClientAuth()\n  }\n\n  fun requestClientAuth() {\n    delegate.requestClientAuth()\n  }\n\n  fun requireClientAuth() {\n    delegate.requireClientAuth()\n  }\n\n  @Throws(InterruptedException::class)\n  fun takeRequest(): RecordedRequest = delegate.takeRequest().unwrap()\n\n  @Throws(InterruptedException::class)\n  fun takeRequest(\n    timeout: Long,\n    unit: TimeUnit,\n  ): RecordedRequest? = delegate.takeRequest(timeout, unit)?.unwrap()\n\n  @JvmName(\"-deprecated_requestCount\")\n  fun getRequestCount(): Int = delegate.requestCount\n\n  fun enqueue(response: MockResponse) {\n    delegate.enqueue(response.wrap())\n  }\n\n  @Throws(IOException::class)\n  @JvmOverloads\n  fun start(port: Int = 0) {\n    started = true\n    delegate.start(port)\n  }\n\n  @Throws(IOException::class)\n  fun start(\n    inetAddress: InetAddress,\n    port: Int,\n  ) {\n    started = true\n    delegate.start(inetAddress, port)\n  }\n\n  @Synchronized\n  @Throws(IOException::class)\n  fun shutdown() {\n    delegate.close()\n  }\n\n  @Synchronized override fun after() {\n    try {\n      shutdown()\n    } catch (e: IOException) {\n      logger.log(Level.WARNING, \"MockWebServer shutdown failed\", e)\n    }\n  }\n\n  override fun toString(): String = delegate.toString()\n\n  @Throws(IOException::class)\n  override fun close() = delegate.close()\n\n  companion object {\n    private val logger = Logger.getLogger(MockWebServer::class.java.name)\n  }\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/kotlin/okhttp3/mockwebserver/PushPromise.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nimport okhttp3.Headers\n\nclass PushPromise(\n  @get:JvmName(\"method\") val method: String,\n  @get:JvmName(\"path\") val path: String,\n  @get:JvmName(\"headers\") val headers: Headers,\n  @get:JvmName(\"response\") val response: MockResponse,\n) {\n  @JvmName(\"-deprecated_method\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"method\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun method(): String = method\n\n  @JvmName(\"-deprecated_path\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"path\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun path(): String = path\n\n  @JvmName(\"-deprecated_headers\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"headers\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun headers(): Headers = headers\n\n  @JvmName(\"-deprecated_response\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"response\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun response(): MockResponse = response\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/kotlin/okhttp3/mockwebserver/QueueDispatcher.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nimport mockwebserver3.QueueDispatcher\n\nclass QueueDispatcher : Dispatcher() {\n  internal val delegate = QueueDispatcher()\n\n  @Throws(InterruptedException::class)\n  override fun dispatch(request: RecordedRequest): MockResponse = throw UnsupportedOperationException(\"unexpected call\")\n\n  override fun peek(): MockResponse = throw UnsupportedOperationException(\"unexpected call\")\n\n  fun enqueueResponse(response: MockResponse) {\n    delegate.enqueue(response.wrap())\n  }\n\n  override fun shutdown() {\n    delegate.close()\n  }\n\n  fun setFailFast(failFast: Boolean) {\n    delegate.setFailFast(failFast)\n  }\n\n  fun setFailFast(failFastResponse: MockResponse?) {\n    delegate.setFailFast(failFastResponse?.wrap())\n  }\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/kotlin/okhttp3/mockwebserver/RecordedRequest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nimport java.io.IOException\nimport java.net.Inet6Address\nimport java.net.Socket\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Handshake\nimport okhttp3.Handshake.Companion.handshake\nimport okhttp3.Headers\nimport okhttp3.HttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okhttp3.TlsVersion\nimport okio.Buffer\n\nclass RecordedRequest {\n  val requestLine: String\n  val headers: Headers\n  val chunkSizes: List<Int>\n  val bodySize: Long\n  val body: Buffer\n  val sequenceNumber: Int\n  val failure: IOException?\n  val method: String?\n  val path: String?\n  val handshake: Handshake?\n  val requestUrl: HttpUrl?\n\n  @get:JvmName(\"-deprecated_utf8Body\")\n  @Deprecated(\n    message = \"Use body.readUtf8()\",\n    replaceWith = ReplaceWith(\"body.readUtf8()\"),\n    level = DeprecationLevel.ERROR,\n  )\n  val utf8Body: String\n    get() = body.readUtf8()\n\n  val tlsVersion: TlsVersion?\n    get() = handshake?.tlsVersion\n\n  internal constructor(\n    requestLine: String,\n    headers: Headers,\n    chunkSizes: List<Int>,\n    bodySize: Long,\n    body: Buffer,\n    sequenceNumber: Int,\n    failure: IOException?,\n    method: String?,\n    path: String?,\n    handshake: Handshake?,\n    requestUrl: HttpUrl?,\n  ) {\n    this.requestLine = requestLine\n    this.headers = headers\n    this.chunkSizes = chunkSizes\n    this.bodySize = bodySize\n    this.body = body\n    this.sequenceNumber = sequenceNumber\n    this.failure = failure\n    this.method = method\n    this.path = path\n    this.handshake = handshake\n    this.requestUrl = requestUrl\n  }\n\n  @JvmOverloads\n  constructor(\n    requestLine: String,\n    headers: Headers,\n    chunkSizes: List<Int>,\n    bodySize: Long,\n    body: Buffer,\n    sequenceNumber: Int,\n    socket: Socket,\n    failure: IOException? = null,\n  ) {\n    this.requestLine = requestLine\n    this.headers = headers\n    this.chunkSizes = chunkSizes\n    this.bodySize = bodySize\n    this.body = body\n    this.sequenceNumber = sequenceNumber\n    this.failure = failure\n\n    if (socket is SSLSocket) {\n      try {\n        this.handshake = socket.session.handshake()\n      } catch (e: IOException) {\n        throw IllegalArgumentException(e)\n      }\n    } else {\n      this.handshake = null\n    }\n\n    if (requestLine.isNotEmpty()) {\n      val methodEnd = requestLine.indexOf(' ')\n      val pathEnd = requestLine.indexOf(' ', methodEnd + 1)\n      this.method = requestLine.substring(0, methodEnd)\n      var path = requestLine.substring(methodEnd + 1, pathEnd)\n      if (!path.startsWith(\"/\")) {\n        path = \"/\"\n      }\n      this.path = path\n\n      val scheme = if (socket is SSLSocket) \"https\" else \"http\"\n      val inetAddress = socket.localAddress\n\n      var hostname = inetAddress.hostName\n      if (inetAddress is Inet6Address && hostname.contains(':')) {\n        // hostname is likely some form representing the IPv6 bytes\n        // 2001:0db8:85a3:0000:0000:8a2e:0370:7334\n        // 2001:db8:85a3::8a2e:370:7334\n        // ::1\n        hostname = \"[$hostname]\"\n      }\n\n      val localPort = socket.localPort\n      // Allow null in failure case to allow for testing bad requests\n      this.requestUrl = \"$scheme://$hostname:$localPort$path\".toHttpUrlOrNull()\n    } else {\n      this.requestUrl = null\n      this.method = null\n      this.path = null\n    }\n  }\n\n  @Deprecated(\n    message = \"Use body.readUtf8()\",\n    replaceWith = ReplaceWith(\"body.readUtf8()\"),\n    level = DeprecationLevel.WARNING,\n  )\n  fun getUtf8Body(): String = body.readUtf8()\n\n  fun getHeader(name: String): String? = headers.values(name).firstOrNull()\n\n  override fun toString(): String = requestLine\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/main/kotlin/okhttp3/mockwebserver/SocketPolicy.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nenum class SocketPolicy {\n  SHUTDOWN_SERVER_AFTER_RESPONSE,\n  KEEP_OPEN,\n  DISCONNECT_AT_END,\n  UPGRADE_TO_SSL_AT_END,\n  DISCONNECT_AT_START,\n  DISCONNECT_AFTER_REQUEST,\n  DISCONNECT_DURING_REQUEST_BODY,\n  DISCONNECT_DURING_RESPONSE_BODY,\n  DO_NOT_READ_REQUEST_BODY,\n  FAIL_HANDSHAKE,\n  SHUTDOWN_INPUT_AT_END,\n  SHUTDOWN_OUTPUT_AT_END,\n  STALL_SOCKET_AT_START,\n  NO_RESPONSE,\n  RESET_STREAM_AT_START,\n  EXPECT_CONTINUE,\n  CONTINUE_ALWAYS,\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/test/java/okhttp3/mockwebserver/KotlinSourceModernTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nimport java.net.InetAddress\nimport java.net.Proxy\nimport java.net.Socket\nimport java.util.concurrent.TimeUnit\nimport javax.net.ServerSocketFactory\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.Handshake\nimport okhttp3.Headers\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl\nimport okhttp3.Protocol\nimport okhttp3.TlsVersion\nimport okhttp3.WebSocketListener\nimport okhttp3.internal.http2.Settings\nimport okio.Buffer\nimport org.junit.Ignore\nimport org.junit.Test\n\n/**\n * Access every type, function, and property from Kotlin to defend against unexpected regressions in\n * modern 4.0.x kotlin source-compatibility.\n */\n@Suppress(\n  \"ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE\",\n  \"UNUSED_ANONYMOUS_PARAMETER\",\n  \"UNUSED_VALUE\",\n  \"UNUSED_VARIABLE\",\n  \"VARIABLE_WITH_REDUNDANT_INITIALIZER\",\n  \"RedundantLambdaArrow\",\n  \"RedundantExplicitType\",\n  \"IMPLICIT_NOTHING_AS_TYPE_PARAMETER\",\n)\nclass KotlinSourceModernTest {\n  @Test @Ignore\n  fun dispatcherFromMockWebServer() {\n    val dispatcher =\n      object : Dispatcher() {\n        override fun dispatch(request: RecordedRequest): MockResponse = TODO()\n\n        override fun peek(): MockResponse = TODO()\n\n        override fun shutdown() = TODO()\n      }\n  }\n\n  @Test @Ignore\n  fun mockResponse() {\n    var mockResponse: MockResponse = MockResponse()\n    var status: String = mockResponse.status\n    status = mockResponse.status\n    mockResponse.status = \"\"\n    mockResponse = mockResponse.setResponseCode(0)\n    var headers: Headers = mockResponse.headers\n    var trailers: Headers = mockResponse.trailers\n    mockResponse = mockResponse.clearHeaders()\n    mockResponse = mockResponse.addHeader(\"\")\n    mockResponse = mockResponse.addHeader(\"\", \"\")\n    mockResponse = mockResponse.addHeaderLenient(\"\", Any())\n    mockResponse = mockResponse.setHeader(\"\", Any())\n    mockResponse.headers = headersOf()\n    mockResponse.trailers = headersOf()\n    mockResponse = mockResponse.removeHeader(\"\")\n    var body: Buffer? = mockResponse.getBody()\n    mockResponse = mockResponse.setBody(Buffer())\n    mockResponse = mockResponse.setChunkedBody(Buffer(), 0)\n    mockResponse = mockResponse.setChunkedBody(\"\", 0)\n    var socketPolicy: SocketPolicy = mockResponse.socketPolicy\n    mockResponse.socketPolicy = SocketPolicy.KEEP_OPEN\n    var http2ErrorCode: Int = mockResponse.http2ErrorCode\n    mockResponse.http2ErrorCode = 0\n    mockResponse = mockResponse.throttleBody(0L, 0L, TimeUnit.SECONDS)\n    var throttleBytesPerPeriod: Long = mockResponse.throttleBytesPerPeriod\n    throttleBytesPerPeriod = mockResponse.throttleBytesPerPeriod\n    var throttlePeriod: Long = mockResponse.getThrottlePeriod(TimeUnit.SECONDS)\n    mockResponse = mockResponse.setBodyDelay(0L, TimeUnit.SECONDS)\n    val bodyDelay: Long = mockResponse.getBodyDelay(TimeUnit.SECONDS)\n    mockResponse = mockResponse.setHeadersDelay(0L, TimeUnit.SECONDS)\n    val headersDelay: Long = mockResponse.getHeadersDelay(TimeUnit.SECONDS)\n    mockResponse = mockResponse.withPush(PushPromise(\"\", \"\", headersOf(), MockResponse()))\n    var pushPromises: List<PushPromise> = mockResponse.pushPromises\n    pushPromises = mockResponse.pushPromises\n    mockResponse = mockResponse.withSettings(Settings())\n    var settings: Settings = mockResponse.settings\n    settings = mockResponse.settings\n    mockResponse =\n      mockResponse.withWebSocketUpgrade(\n        object : WebSocketListener() {\n        },\n      )\n    var webSocketListener: WebSocketListener? = mockResponse.webSocketListener\n    webSocketListener = mockResponse.webSocketListener\n  }\n\n  @Test @Ignore\n  fun mockWebServer() {\n    val mockWebServer: MockWebServer = MockWebServer()\n    var port: Int = mockWebServer.port\n    var hostName: String = mockWebServer.hostName\n    hostName = mockWebServer.hostName\n    val toProxyAddress: Proxy = mockWebServer.toProxyAddress()\n    mockWebServer.serverSocketFactory = ServerSocketFactory.getDefault()\n    val url: HttpUrl = mockWebServer.url(\"\")\n    mockWebServer.bodyLimit = 0L\n    mockWebServer.protocolNegotiationEnabled = false\n    mockWebServer.protocols = listOf()\n    val protocols: List<Protocol> = mockWebServer.protocols\n    mockWebServer.useHttps(SSLSocketFactory.getDefault() as SSLSocketFactory, false)\n    mockWebServer.noClientAuth()\n    mockWebServer.requestClientAuth()\n    mockWebServer.requireClientAuth()\n    val request: RecordedRequest = mockWebServer.takeRequest()\n    val nullableRequest: RecordedRequest? = mockWebServer.takeRequest(0L, TimeUnit.SECONDS)\n    var requestCount: Int = mockWebServer.requestCount\n    mockWebServer.enqueue(MockResponse())\n    mockWebServer.start()\n    mockWebServer.start(0)\n    mockWebServer.start(InetAddress.getLocalHost(), 0)\n    mockWebServer.shutdown()\n    var dispatcher: Dispatcher = mockWebServer.dispatcher\n    dispatcher = mockWebServer.dispatcher\n    mockWebServer.dispatcher = QueueDispatcher()\n    mockWebServer.dispatcher = QueueDispatcher()\n    mockWebServer.close()\n  }\n\n  @Test @Ignore\n  fun pushPromise() {\n    val pushPromise: PushPromise = PushPromise(\"\", \"\", headersOf(), MockResponse())\n    val method: String = pushPromise.method\n    val path: String = pushPromise.path\n    val headers: Headers = pushPromise.headers\n    val response: MockResponse = pushPromise.response\n  }\n\n  @Test @Ignore\n  fun queueDispatcher() {\n    val queueDispatcher: QueueDispatcher = QueueDispatcher()\n    var mockResponse: MockResponse =\n      queueDispatcher.dispatch(\n        RecordedRequest(\"\", headersOf(), listOf(), 0L, Buffer(), 0, Socket()),\n      )\n    mockResponse = queueDispatcher.peek()\n    queueDispatcher.enqueueResponse(MockResponse())\n    queueDispatcher.shutdown()\n    queueDispatcher.setFailFast(false)\n    queueDispatcher.setFailFast(MockResponse())\n  }\n\n  @Test @Ignore\n  fun recordedRequest() {\n    var recordedRequest: RecordedRequest =\n      RecordedRequest(\n        \"\",\n        headersOf(),\n        listOf(),\n        0L,\n        Buffer(),\n        0,\n        Socket(),\n      )\n    recordedRequest = RecordedRequest(\"\", headersOf(), listOf(), 0L, Buffer(), 0, Socket())\n    var requestUrl: HttpUrl? = recordedRequest.requestUrl\n    var requestLine: String = recordedRequest.requestLine\n    var method: String? = recordedRequest.method\n    var path: String? = recordedRequest.path\n    var headers: Headers = recordedRequest.headers\n    val header: String? = recordedRequest.getHeader(\"\")\n    var chunkSizes: List<Int> = recordedRequest.chunkSizes\n    var bodySize: Long = recordedRequest.bodySize\n    var body: Buffer = recordedRequest.body\n    var utf8Body: String = recordedRequest.body.readUtf8()\n    var sequenceNumber: Int = recordedRequest.sequenceNumber\n    var tlsVersion: TlsVersion? = recordedRequest.tlsVersion\n    var handshake: Handshake? = recordedRequest.handshake\n  }\n\n  @Test @Ignore\n  fun socketPolicy() {\n    val socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN\n  }\n}\n"
  },
  {
    "path": "mockwebserver-deprecated/src/test/java/okhttp3/mockwebserver/MockWebServerTest.kt",
    "content": "/*\n * Copyright (C) 2011 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.mockwebserver\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isBetween\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isGreaterThan\nimport assertk.assertions.isGreaterThanOrEqualTo\nimport assertk.assertions.isNotEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.io.BufferedReader\nimport java.io.Closeable\nimport java.io.IOException\nimport java.io.InputStreamReader\nimport java.net.ConnectException\nimport java.net.HttpURLConnection\nimport java.net.ProtocolException\nimport java.net.SocketTimeoutException\nimport java.nio.charset.StandardCharsets\nimport java.util.Arrays\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicBoolean\nimport javax.net.ssl.HttpsURLConnection\nimport kotlin.test.assertFailsWith\nimport okhttp3.Headers\nimport okhttp3.Protocol\nimport okhttp3.RecordingHostnameVerifier\nimport okhttp3.TestUtil.assumeNotWindows\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junit.runner.Description\nimport org.junit.runners.model.Statement\n\n@Suppress(\"deprecation\")\n@Timeout(30)\n@Tag(\"Slow\")\nclass MockWebServerTest {\n  @RegisterExtension\n  var platform = PlatformRule()\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    server.start()\n  }\n\n  @AfterEach\n  fun tearDown() {\n    server.shutdown()\n  }\n\n  @Test\n  fun defaultMockResponse() {\n    val response = MockResponse()\n    assertThat(headersToList(response)).containsExactly(\"Content-Length: 0\")\n    assertThat(response.status).isEqualTo(\"HTTP/1.1 200 OK\")\n  }\n\n  @Test\n  fun setResponseMockReason() {\n    val reasons =\n      arrayOf(\n        \"Mock Response\",\n        \"Informational\",\n        \"OK\",\n        \"Redirection\",\n        \"Client Error\",\n        \"Server Error\",\n        \"Mock Response\",\n      )\n    for (i in 0..599) {\n      val response = MockResponse().setResponseCode(i)\n      val expectedReason = reasons[i / 100]\n      assertThat(response.status).isEqualTo(\"HTTP/1.1 $i $expectedReason\")\n      assertThat(headersToList(response)).containsExactly(\"Content-Length: 0\")\n    }\n  }\n\n  @Test\n  fun setStatusControlsWholeStatusLine() {\n    val response = MockResponse().setStatus(\"HTTP/1.1 202 That'll do pig\")\n    assertThat(headersToList(response)).containsExactly(\"Content-Length: 0\")\n    assertThat(response.status).isEqualTo(\"HTTP/1.1 202 That'll do pig\")\n  }\n\n  @Test\n  fun setBodyAdjustsHeaders() {\n    val response = MockResponse().setBody(\"ABC\")\n    assertThat(headersToList(response)).containsExactly(\"Content-Length: 3\")\n    assertThat(response.getBody()!!.readUtf8()).isEqualTo(\"ABC\")\n  }\n\n  @Test\n  fun mockResponseAddHeader() {\n    val response =\n      MockResponse()\n        .clearHeaders()\n        .addHeader(\"Cookie: s=square\")\n        .addHeader(\"Cookie\", \"a=android\")\n    assertThat(headersToList(response))\n      .containsExactly(\"Cookie: s=square\", \"Cookie: a=android\")\n  }\n\n  @Test\n  fun mockResponseSetHeader() {\n    val response =\n      MockResponse()\n        .clearHeaders()\n        .addHeader(\"Cookie: s=square\")\n        .addHeader(\"Cookie: a=android\")\n        .addHeader(\"Cookies: delicious\")\n    response.setHeader(\"cookie\", \"r=robot\")\n    assertThat(headersToList(response))\n      .containsExactly(\"Cookies: delicious\", \"cookie: r=robot\")\n  }\n\n  @Test\n  fun mockResponseSetHeaders() {\n    val response =\n      MockResponse()\n        .clearHeaders()\n        .addHeader(\"Cookie: s=square\")\n        .addHeader(\"Cookies: delicious\")\n    response.setHeaders(Headers.Builder().add(\"Cookie\", \"a=android\").build())\n    assertThat(headersToList(response)).containsExactly(\"Cookie: a=android\")\n  }\n\n  @Test\n  fun regularResponse() {\n    server.enqueue(MockResponse().setBody(\"hello world\"))\n    val url = server.url(\"/\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    connection.setRequestProperty(\"Accept-Language\", \"en-US\")\n    val inputStream = connection.inputStream\n    val reader = BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))\n    assertThat(connection.getResponseCode()).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(reader.readLine()).isEqualTo(\"hello world\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    assertThat(request.getHeader(\"Accept-Language\")).isEqualTo(\"en-US\")\n\n    // Server has no more requests.\n    assertThat(server.takeRequest(100, TimeUnit.MILLISECONDS)).isNull()\n  }\n\n  @Test\n  fun redirect() {\n    server.enqueue(\n      MockResponse()\n        .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)\n        .addHeader(\"Location: \" + server.url(\"/new-path\"))\n        .setBody(\"This page has moved!\"),\n    )\n    server.enqueue(MockResponse().setBody(\"This is the new location!\"))\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val inputStream = connection.getInputStream()\n    val reader = BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"This is the new location!\")\n    val first = server.takeRequest()\n    assertThat(first.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    val redirect = server.takeRequest()\n    assertThat(redirect.requestLine).isEqualTo(\"GET /new-path HTTP/1.1\")\n  }\n\n  /**\n   * Test that MockWebServer blocks for a call to enqueue() if a request is made before a mock\n   * response is ready.\n   */\n  @Test\n  fun dispatchBlocksWaitingForEnqueue() {\n    Thread {\n      try {\n        Thread.sleep(1000)\n      } catch (ignored: InterruptedException) {\n      }\n      server.enqueue(MockResponse().setBody(\"enqueued in the background\"))\n    }.start()\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val inputStream = connection.getInputStream()\n    val reader = BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"enqueued in the background\")\n  }\n\n  @Test\n  fun nonHexadecimalChunkSize() {\n    server.enqueue(\n      MockResponse()\n        .setBody(\"G\\r\\nxxxxxxxxxxxxxxxx\\r\\n0\\r\\n\\r\\n\")\n        .clearHeaders()\n        .addHeader(\"Transfer-encoding: chunked\"),\n    )\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val inputStream = connection.getInputStream()\n    try {\n      inputStream.read()\n      fail<Any>()\n    } catch (expected: IOException) {\n    }\n  }\n\n  @Test\n  fun responseTimeout() {\n    server.enqueue(\n      MockResponse()\n        .setBody(\"ABC\")\n        .clearHeaders()\n        .addHeader(\"Content-Length: 4\"),\n    )\n    server.enqueue(MockResponse().setBody(\"DEF\"))\n    val urlConnection = server.url(\"/\").toUrl().openConnection()\n    urlConnection.setReadTimeout(1000)\n    val inputStream = urlConnection.getInputStream()\n    assertThat(inputStream.read()).isEqualTo('A'.code)\n    assertThat(inputStream.read()).isEqualTo('B'.code)\n    assertThat(inputStream.read()).isEqualTo('C'.code)\n    try {\n      inputStream.read() // if Content-Length was accurate, this would return -1 immediately\n      fail<Any>()\n    } catch (expected: SocketTimeoutException) {\n    }\n    val urlConnection2 = server.url(\"/\").toUrl().openConnection()\n    val in2 = urlConnection2.getInputStream()\n    assertThat(in2.read()).isEqualTo('D'.code)\n    assertThat(in2.read()).isEqualTo('E'.code)\n    assertThat(in2.read()).isEqualTo('F'.code)\n    assertThat(in2.read()).isEqualTo(-1)\n    assertThat(server.takeRequest().sequenceNumber).isEqualTo(0)\n    assertThat(server.takeRequest().sequenceNumber).isEqualTo(0)\n  }\n\n  @Disabled(\"Not actually failing where expected\")\n  @Test\n  fun disconnectAtStart() {\n    server.enqueue(\n      MockResponse()\n        .setSocketPolicy(SocketPolicy.DISCONNECT_AT_START),\n    )\n    server.enqueue(MockResponse()) // The jdk's HttpUrlConnection is a bastard.\n    server.enqueue(MockResponse())\n    try {\n      server\n        .url(\"/a\")\n        .toUrl()\n        .openConnection()\n        .getInputStream()\n      fail<Any>()\n    } catch (expected: IOException) {\n    }\n    server\n      .url(\"/b\")\n      .toUrl()\n      .openConnection()\n      .getInputStream() // Should succeed.\n  }\n\n  /**\n   * Throttle the request body by sleeping 500ms after every 3 bytes. With a 6-byte request, this\n   * should yield one sleep for a total delay of 500ms.\n   */\n  @Test\n  fun throttleRequest() {\n    assumeNotWindows()\n    server.enqueue(\n      MockResponse()\n        .throttleBody(3, 500, TimeUnit.MILLISECONDS),\n    )\n    val startNanos = System.nanoTime()\n    val connection = server.url(\"/\").toUrl().openConnection()\n    connection.setDoOutput(true)\n    connection.getOutputStream().write(\"ABCDEF\".toByteArray(StandardCharsets.UTF_8))\n    val inputStream = connection.getInputStream()\n    assertThat(inputStream.read()).isEqualTo(-1)\n    val elapsedNanos = System.nanoTime() - startNanos\n    val elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos)\n    assertThat(elapsedMillis).isBetween(500L, 1000L)\n  }\n\n  /**\n   * Throttle the response body by sleeping 500ms after every 3 bytes. With a 6-byte response, this\n   * should yield one sleep for a total delay of 500ms.\n   */\n  @Test\n  fun throttleResponse() {\n    assumeNotWindows()\n    server.enqueue(\n      MockResponse()\n        .setBody(\"ABCDEF\")\n        .throttleBody(3, 500, TimeUnit.MILLISECONDS),\n    )\n    val startNanos = System.nanoTime()\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val inputStream = connection.getInputStream()\n    assertThat(inputStream.read()).isEqualTo('A'.code)\n    assertThat(inputStream.read()).isEqualTo('B'.code)\n    assertThat(inputStream.read()).isEqualTo('C'.code)\n    assertThat(inputStream.read()).isEqualTo('D'.code)\n    assertThat(inputStream.read()).isEqualTo('E'.code)\n    assertThat(inputStream.read()).isEqualTo('F'.code)\n    assertThat(inputStream.read()).isEqualTo(-1)\n    val elapsedNanos = System.nanoTime() - startNanos\n    val elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos)\n    assertThat(elapsedMillis).isBetween(500L, 1000L)\n  }\n\n  /** Delay the response body by sleeping 1s.  */\n  @Test\n  fun delayResponse() {\n    assumeNotWindows()\n    server.enqueue(\n      MockResponse()\n        .setBody(\"ABCDEF\")\n        .setBodyDelay(1, TimeUnit.SECONDS),\n    )\n    val startNanos = System.nanoTime()\n    val connection = server.url(\"/\").toUrl().openConnection()\n    val inputStream = connection.getInputStream()\n    assertThat(inputStream.read()).isEqualTo('A'.code)\n    val elapsedNanos = System.nanoTime() - startNanos\n    val elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos)\n    assertThat(elapsedMillis).isGreaterThanOrEqualTo(1000L)\n    inputStream.close()\n  }\n\n  @Test\n  fun disconnectRequestHalfway() {\n    server.enqueue(MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_DURING_REQUEST_BODY))\n    // Limit the size of the request body that the server holds in memory to an arbitrary\n    // 3.5 MBytes so this test can pass on devices with little memory.\n    server.bodyLimit = 7 * 512 * 1024\n    val connection = server.url(\"/\").toUrl().openConnection() as HttpURLConnection\n    connection.setRequestMethod(\"POST\")\n    connection.setDoOutput(true)\n    connection.setFixedLengthStreamingMode(1024 * 1024 * 1024) // 1 GB\n    connection.connect()\n    val out = connection.outputStream\n    val data = ByteArray(1024 * 1024)\n    var i = 0\n    while (i < 1024) {\n      try {\n        out.write(data)\n        out.flush()\n        if (i == 513) {\n          // pause slightly after halfway to make result more predictable\n          Thread.sleep(100)\n        }\n      } catch (e: IOException) {\n        break\n      }\n      i++\n    }\n    // Halfway +/- 0.5%\n    assertThat(i.toFloat()).isCloseTo(512f, 5f)\n  }\n\n  @Test\n  fun disconnectResponseHalfway() {\n    server.enqueue(\n      MockResponse()\n        .setBody(\"ab\")\n        .setSocketPolicy(SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY),\n    )\n    val connection = server.url(\"/\").toUrl().openConnection()\n    assertThat(connection.getContentLength()).isEqualTo(2)\n    val inputStream = connection.getInputStream()\n    assertThat(inputStream.read()).isEqualTo('a'.code)\n    try {\n      val byteRead = inputStream.read()\n      // OpenJDK behavior: end of stream.\n      assertThat(byteRead).isEqualTo(-1)\n    } catch (e: ProtocolException) {\n      // On Android, HttpURLConnection is implemented by OkHttp v2. OkHttp\n      // treats an incomplete response body as a ProtocolException.\n    } catch (ioe: IOException) {\n      // Change in https://bugs.openjdk.org/browse/JDK-8335135\n      assertThat(ioe.message).isEqualTo(\"Premature EOF\")\n    }\n  }\n\n  private fun headersToList(response: MockResponse): List<String> {\n    val headers = response.headers\n    val size = headers.size\n    val headerList: MutableList<String> = ArrayList(size)\n    for (i in 0 until size) {\n      headerList.add(headers.name(i) + \": \" + headers.value(i))\n    }\n    return headerList\n  }\n\n  @Test\n  fun shutdownWithoutStart() {\n    val server = MockWebServer()\n    server.shutdown()\n  }\n\n  @Test\n  fun closeViaClosable() {\n    val server: Closeable = MockWebServer()\n    server.close()\n  }\n\n  @Test\n  fun shutdownWithoutEnqueue() {\n    val server = MockWebServer()\n    server.start()\n    server.shutdown()\n  }\n\n  @Test\n  fun portImplicitlyStarts() {\n    assertThat(server.port).isGreaterThan(0)\n  }\n\n  @Test\n  fun hostnameImplicitlyStarts() {\n    assertThat(server.hostName).isNotNull()\n  }\n\n  @Test\n  fun toProxyAddressImplicitlyStarts() {\n    assertThat(server.toProxyAddress()).isNotNull()\n  }\n\n  @Test\n  fun differentInstancesGetDifferentPorts() {\n    val other = MockWebServer()\n    assertThat(other.port).isNotEqualTo(server.port)\n    other.shutdown()\n  }\n\n  @Test\n  fun statementStartsAndStops() {\n    val called = AtomicBoolean()\n    val statement =\n      server.apply(\n        object : Statement() {\n          override fun evaluate() {\n            called.set(true)\n            server\n              .url(\"/\")\n              .toUrl()\n              .openConnection()\n              .connect()\n          }\n        },\n        Description.EMPTY,\n      )\n    statement.evaluate()\n    assertThat(called.get()).isTrue()\n    try {\n      server\n        .url(\"/\")\n        .toUrl()\n        .openConnection()\n        .connect()\n      fail<Any>()\n    } catch (expected: ConnectException) {\n    }\n  }\n\n  @Test\n  fun shutdownWhileBlockedDispatching() {\n    // Enqueue a request that'll cause MockWebServer to hang on QueueDispatcher.dispatch().\n    val connection = server.url(\"/\").toUrl().openConnection() as HttpURLConnection\n    connection.setReadTimeout(500)\n    try {\n      connection.getResponseCode()\n      fail<Any>()\n    } catch (expected: SocketTimeoutException) {\n    }\n\n    // Shutting down the server should unblock the dispatcher.\n    server.shutdown()\n  }\n\n  @Test\n  fun requestUrlReconstructed() {\n    server.enqueue(MockResponse().setBody(\"hello world\"))\n    val url = server.url(\"/a/deep/path?key=foo%20bar\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    val inputStream = connection.inputStream\n    val reader = BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))\n    assertThat(connection.getResponseCode()).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(reader.readLine()).isEqualTo(\"hello world\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\n      \"GET /a/deep/path?key=foo%20bar HTTP/1.1\",\n    )\n    assertThat(request.path).isEqualTo(\"/a/deep/path?key=foo%20bar\")\n    val requestUrl = request.requestUrl\n    assertThat(requestUrl!!.scheme).isEqualTo(\"http\")\n    assertThat(requestUrl.host).isEqualTo(server.hostName)\n    assertThat(requestUrl.port).isEqualTo(server.port)\n    assertThat(requestUrl.encodedPath).isEqualTo(\"/a/deep/path\")\n    assertThat(requestUrl.queryParameter(\"key\")).isEqualTo(\"foo bar\")\n  }\n\n  @Test\n  fun shutdownServerAfterRequest() {\n    server.enqueue(MockResponse().setSocketPolicy(SocketPolicy.SHUTDOWN_SERVER_AFTER_RESPONSE))\n    val url = server.url(\"/\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    assertThat(connection.getResponseCode()).isEqualTo(HttpURLConnection.HTTP_OK)\n    val refusedConnection = url.openConnection() as HttpURLConnection\n    assertFailsWith<ConnectException> {\n      refusedConnection.getResponseCode()\n    }.also { expected ->\n      assertThat(expected.message!!).contains(\"refused\")\n    }\n  }\n\n  @Test\n  fun http100Continue() {\n    server.enqueue(MockResponse().setBody(\"response\"))\n    val url = server.url(\"/\").toUrl()\n    val connection = url.openConnection() as HttpURLConnection\n    connection.setDoOutput(true)\n    connection.setRequestProperty(\"Expect\", \"100-Continue\")\n    connection.outputStream.write(\"request\".toByteArray(StandardCharsets.UTF_8))\n    val inputStream = connection.inputStream\n    val reader = BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"response\")\n    val request = server.takeRequest()\n    assertThat(request.body.readUtf8()).isEqualTo(\"request\")\n  }\n\n  @Test\n  fun testH2PriorKnowledgeServerFallback() {\n    try {\n      server.protocols = Arrays.asList(Protocol.H2_PRIOR_KNOWLEDGE, Protocol.HTTP_1_1)\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\n        \"protocols containing h2_prior_knowledge cannot use other protocols: \" +\n          \"[h2_prior_knowledge, http/1.1]\",\n      )\n    }\n  }\n\n  @Test\n  fun testH2PriorKnowledgeServerDuplicates() {\n    try {\n      // Treating this use case as user error\n      server.protocols = listOf(Protocol.H2_PRIOR_KNOWLEDGE, Protocol.H2_PRIOR_KNOWLEDGE)\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\n        \"protocols containing h2_prior_knowledge cannot use other protocols: \" +\n          \"[h2_prior_knowledge, h2_prior_knowledge]\",\n      )\n    }\n  }\n\n  @Test\n  fun testMockWebServerH2PriorKnowledgeProtocol() {\n    server.protocols = Arrays.asList(Protocol.H2_PRIOR_KNOWLEDGE)\n    assertThat(server.protocols.size).isEqualTo(1)\n    assertThat(server.protocols[0]).isEqualTo(Protocol.H2_PRIOR_KNOWLEDGE)\n  }\n\n  @Test\n  fun https() {\n    val handshakeCertificates = platform.localhostHandshakeCertificates()\n    server.useHttps(handshakeCertificates.sslSocketFactory(), false)\n    server.enqueue(MockResponse().setBody(\"abc\"))\n    val url = server.url(\"/\")\n    val connection = url.toUrl().openConnection() as HttpsURLConnection\n    connection.setSSLSocketFactory(handshakeCertificates.sslSocketFactory())\n    connection.setHostnameVerifier(RecordingHostnameVerifier())\n    assertThat(connection.getResponseCode()).isEqualTo(HttpURLConnection.HTTP_OK)\n    val reader = BufferedReader(InputStreamReader(connection.inputStream, StandardCharsets.UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"abc\")\n    val request = server.takeRequest()\n    assertThat(request.requestUrl!!.scheme).isEqualTo(\"https\")\n    val handshake = request.handshake\n    assertThat(handshake!!.tlsVersion).isNotNull()\n    assertThat(handshake.cipherSuite).isNotNull()\n    assertThat(handshake.localPrincipal).isNotNull()\n    assertThat(handshake.localCertificates.size).isEqualTo(1)\n    assertThat(handshake.peerPrincipal).isNull()\n    assertThat(handshake.peerCertificates.size).isEqualTo(0)\n  }\n\n  @Test\n  fun httpsWithClientAuth() {\n    platform.assumeNotBouncyCastle()\n    platform.assumeNotConscrypt()\n    val clientCa =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .build()\n    val serverCa =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .build()\n    val serverCertificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(serverCa)\n        .addSubjectAlternativeName(server.hostName)\n        .build()\n    val serverHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(clientCa.certificate)\n        .heldCertificate(serverCertificate)\n        .build()\n    server.useHttps(serverHandshakeCertificates.sslSocketFactory(), false)\n    server.enqueue(MockResponse().setBody(\"abc\"))\n    server.requestClientAuth()\n    val clientCertificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(clientCa)\n        .build()\n    val clientHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(serverCa.certificate)\n        .heldCertificate(clientCertificate)\n        .build()\n    val url = server.url(\"/\")\n    val connection = url.toUrl().openConnection() as HttpsURLConnection\n    connection.setSSLSocketFactory(clientHandshakeCertificates.sslSocketFactory())\n    connection.setHostnameVerifier(RecordingHostnameVerifier())\n    assertThat(connection.getResponseCode()).isEqualTo(HttpURLConnection.HTTP_OK)\n    val reader =\n      BufferedReader(InputStreamReader(connection.inputStream, StandardCharsets.UTF_8))\n    assertThat(reader.readLine()).isEqualTo(\"abc\")\n    val request = server.takeRequest()\n    assertThat(request.requestUrl!!.scheme).isEqualTo(\"https\")\n    val handshake = request.handshake\n    assertThat(handshake!!.tlsVersion).isNotNull()\n    assertThat(handshake.cipherSuite).isNotNull()\n    assertThat(handshake.localPrincipal).isNotNull()\n    assertThat(handshake.localCertificates.size).isEqualTo(1)\n    assertThat(handshake.peerPrincipal).isNotNull()\n    assertThat(handshake.peerCertificates.size).isEqualTo(1)\n  }\n\n  @Test\n  fun shutdownTwice() {\n    val server2 = MockWebServer()\n    server2.start()\n    server2.shutdown()\n    try {\n      server2.start()\n      fail<Any>()\n    } catch (expected: IllegalStateException) {\n      // expected\n    }\n    server2.shutdown()\n  }\n}\n"
  },
  {
    "path": "mockwebserver-junit4/README.md",
    "content": "MockWebServer for JUnit 4\n=========================\n\nThis module integrates mockwebserver3.MockWebServer with JUnit 4.\n\nTo use, first add this library as a test dependency:\n\n```\ntestImplementation(\"com.squareup.okhttp3:mockwebserver3-junit4:5.3.0\")\n```\n\nThen in tests annotated `@org.junit.Test`, you may declare a field with the `@Rule` annotation:\n\n```\n@Rule public final MockWebServerRule serverRule = new MockWebServerRule();\n```\n\nThe `serverRule` field has a `server` field. It is an instance of `MockWebServer`. That instance\nwill be shut down automatically after the test runs.\n\nFor Kotlin, the `@JvmField` annotation is also necessary:\n\n```\n@JvmField @Rule val serverRule = MockWebServerRule()\n```\n"
  },
  {
    "path": "mockwebserver-junit4/api/mockwebserver3-junit4.api",
    "content": "public final class mockwebserver3/junit4/MockWebServerRule : org/junit/rules/ExternalResource {\n\tpublic fun <init> ()V\n\tpublic final fun getServer ()Lmockwebserver3/MockWebServer;\n}\n\n"
  },
  {
    "path": "mockwebserver-junit4/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyJavaModules(\"mockwebserver3.junit4\")\n\ndependencies {\n  api(projects.okhttp)\n  api(projects.mockwebserver3)\n  api(libs.junit)\n\n  testImplementation(libs.assertk)\n  testImplementation(libs.junit.vintage.engine)\n}\n"
  },
  {
    "path": "mockwebserver-junit4/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule mockwebserver3.junit4 {\n  requires okhttp3;\n  exports mockwebserver3.junit4;\n  requires java.logging;\n}\n"
  },
  {
    "path": "mockwebserver-junit4/src/main/kotlin/mockwebserver3/junit4/MockWebServerRule.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3.junit4\n\nimport java.io.IOException\nimport mockwebserver3.MockWebServer\nimport org.junit.rules.ExternalResource\n\n/**\n * Runs MockWebServer for the duration of a single test method.\n *\n * In Java JUnit 4 tests (ie. tests annotated `@org.junit.Test`), use this by defining a field with\n * the `@Rule` annotation:\n *\n * ```java\n * @Rule public final MockWebServerRule serverRule = new MockWebServerRule();\n * ```\n *\n * For Kotlin the `@JvmField` annotation is also necessary:\n *\n * ```kotlin\n * @JvmField @Rule val serverRule = MockWebServerRule()\n * ```\n */\nclass MockWebServerRule : ExternalResource() {\n  val server: MockWebServer = MockWebServer()\n\n  override fun before() {\n    try {\n      server.start()\n    } catch (e: IOException) {\n      throw RuntimeException(e)\n    }\n  }\n\n  override fun after() {\n    server.close()\n  }\n}\n"
  },
  {
    "path": "mockwebserver-junit4/src/test/java/mockwebserver3/junit4/MockWebServerRuleTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3.junit4\n\nimport assertk.assertThat\nimport assertk.assertions.isTrue\nimport java.net.ConnectException\nimport java.util.concurrent.atomic.AtomicBoolean\nimport org.junit.Assert.fail\nimport org.junit.Test\nimport org.junit.runner.Description\nimport org.junit.runners.model.Statement\n\nclass MockWebServerRuleTest {\n  @Test fun statementStartsAndStops() {\n    val rule = MockWebServerRule()\n    val called = AtomicBoolean()\n    val statement: Statement =\n      rule.apply(\n        object : Statement() {\n          override fun evaluate() {\n            called.set(true)\n            rule.server\n              .url(\"/\")\n              .toUrl()\n              .openConnection()\n              .connect()\n          }\n        },\n        Description.EMPTY,\n      )\n    statement.evaluate()\n    assertThat(called.get()).isTrue()\n    try {\n      rule.server\n        .url(\"/\")\n        .toUrl()\n        .openConnection()\n        .connect()\n      fail()\n    } catch (expected: ConnectException) {\n    }\n  }\n}\n"
  },
  {
    "path": "mockwebserver-junit5/README.md",
    "content": "MockWebServer for JUnit 5\n=========================\n\nThis module integrates mockwebserver3.MockWebServer with JUnit 5.\n\nTo use, first add this library as a test dependency:\n\n```\ntestImplementation(\"com.squareup.okhttp3:mockwebserver3-junit5:5.3.0\")\n```\n\nAnnotate fields in test classes with `@StartStop`. The server will be started and shut down\nautomatically.\n\n```\nclass MyTest {\n\n  @StartStop\n  public final MockWebServer server = new MockWebServer();\n\n  @Test\n  void test() {\n    ...\n  }\n}\n```\n\nRequirements\n------------\n\nMockWebServer's JUnit 5 integration works on Android 7.0+ (API level 24+) and Java 8+. Note that\nthis is above OkHttp's core requirements.\n\n"
  },
  {
    "path": "mockwebserver-junit5/api/mockwebserver3-junit5.api",
    "content": "public abstract interface annotation class mockwebserver3/junit5/StartStop : java/lang/annotation/Annotation {\n}\n\n"
  },
  {
    "path": "mockwebserver-junit5/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyJavaModules(\"mockwebserver3.junit5\")\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n  api(projects.mockwebserver3)\n  api(libs.junit.jupiter.api)\n  compileOnly(libs.animalsniffer.annotations)\n\n  testRuntimeOnly(libs.junit.jupiter.engine)\n  testImplementation(libs.kotlin.junit5)\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(libs.assertk)\n}\n"
  },
  {
    "path": "mockwebserver-junit5/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule mockwebserver3.junit5 {\n  requires okhttp3;\n  opens mockwebserver3.junit5.internal;\n}\n"
  },
  {
    "path": "mockwebserver-junit5/src/main/kotlin/mockwebserver3/junit5/StartStop.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3.junit5\n\nimport mockwebserver3.junit5.internal.StartStopExtension\nimport org.junit.jupiter.api.extension.ExtendWith\n\n/**\n * Runs MockWebServer for the duration of a test method or test class.\n *\n * In Java JUnit 5 tests (ie. tests annotated `@org.junit.jupiter.api.Test`), use this by defining a\n * field with the `@StartStop` annotation:\n *\n * ```java\n * @StartStop public final MockWebServer server = new MockWebServer();\n * ```\n *\n * Or for Kotlin:\n *\n * ```kotlin\n * @StartStop val server = MockWebServer()\n * ```\n */\n@Target(AnnotationTarget.FIELD)\n@Retention(AnnotationRetention.RUNTIME)\n@ExtendWith(StartStopExtension::class)\nannotation class StartStop\n"
  },
  {
    "path": "mockwebserver-junit5/src/main/kotlin/mockwebserver3/junit5/internal/StartStopExtension.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3.junit5.internal\n\nimport java.lang.reflect.Modifier\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.internal.SuppressSignatureCheck\nimport org.junit.jupiter.api.extension.BeforeAllCallback\nimport org.junit.jupiter.api.extension.BeforeEachCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace\nimport org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields\n\n/** Implements the policy specified by [StartStop]. */\n@SuppressSignatureCheck\ninternal class StartStopExtension :\n  BeforeEachCallback,\n  BeforeAllCallback {\n  override fun beforeAll(context: ExtensionContext) {\n    val store = context.getStore(Namespace.create(StartStop::class.java))\n\n    val staticFields =\n      findAnnotatedFields(\n        context.requiredTestClass,\n        StartStop::class.java,\n      ) { Modifier.isStatic(it.modifiers) }\n\n    for (field in staticFields) {\n      field.setAccessible(true)\n      val server = field.get(null) as? MockWebServer ?: continue\n\n      // Put the instance in the store, so JUnit closes it for us in afterAll.\n      store.put(field, server)\n\n      server.start()\n    }\n  }\n\n  override fun beforeEach(context: ExtensionContext) {\n    // Requires API 24\n    val testInstance = context.testInstance.get()\n    val store = context.getStore(Namespace.create(StartStop::class.java))\n\n    val instanceFields =\n      findAnnotatedFields(\n        context.requiredTestClass,\n        StartStop::class.java,\n      ) { !Modifier.isStatic(it.modifiers) }\n\n    for (field in instanceFields) {\n      field.setAccessible(true)\n      val server = field.get(testInstance) as? MockWebServer ?: continue\n\n      // Put the instance in the store, so JUnit closes it for us in afterEach.\n      store.put(field, server)\n\n      server.start()\n    }\n  }\n}\n"
  },
  {
    "path": "mockwebserver-junit5/src/test/java/mockwebserver3/junit5/StartStopTest.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage mockwebserver3.junit5\n\nimport assertk.assertThat\nimport assertk.assertions.isFalse\nimport assertk.assertions.isTrue\nimport java.util.concurrent.CopyOnWriteArrayList\nimport mockwebserver3.Dispatcher\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.RecordedRequest\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.AfterAllCallback\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass StartStopTest {\n  private val dispatcherA = ClosableDispatcher()\n\n  @StartStop val serverA =\n    MockWebServer().apply {\n      dispatcher = dispatcherA\n    }\n\n  private val dispatcherB = ClosableDispatcher()\n\n  @StartStop val serverB =\n    MockWebServer().apply {\n      dispatcher = dispatcherB\n    }\n\n  /** This one won't start because it isn't annotated. */\n  private val dispatcherC = ClosableDispatcher()\n  val serverC =\n    MockWebServer().apply {\n      dispatcher = dispatcherC\n    }\n\n  @Test\n  fun happyPath() {\n    testInstances += this\n\n    assertThat(serverA.started).isTrue()\n    assertThat(serverB.started).isTrue()\n    assertThat(serverC.started).isFalse()\n\n    assertThat(serverD.started).isTrue()\n    assertThat(serverE.started).isTrue()\n    assertThat(serverF.started).isFalse()\n  }\n\n  private companion object {\n    val testInstances = CopyOnWriteArrayList<StartStopTest>()\n\n    private val dispatcherD = ClosableDispatcher()\n\n    @StartStop @JvmStatic\n    val serverD =\n      MockWebServer().apply {\n        dispatcher = dispatcherD\n      }\n\n    private val dispatcherE = ClosableDispatcher()\n\n    @StartStop @JvmStatic\n    val serverE =\n      MockWebServer().apply {\n        dispatcher = dispatcherE\n      }\n\n    private val dispatcherF = ClosableDispatcher()\n\n    @JvmStatic val serverF =\n      MockWebServer().apply {\n        dispatcher = dispatcherF\n      }\n\n    @JvmStatic\n    @RegisterExtension\n    val checkClosed =\n      AfterAllCallback {\n        for (test in testInstances) {\n          assertThat(test.dispatcherA.closed).isTrue()\n          assertThat(test.dispatcherB.closed).isTrue()\n          assertThat(test.dispatcherC.closed).isFalse() // Never started.\n        }\n        testInstances.clear()\n\n        // No assertion that serverC and serverD are closed, because the MockWebServerExtension\n        // runs after this callback.\n        if (false) {\n          assertThat(dispatcherD.closed).isTrue()\n          assertThat(dispatcherE.closed).isTrue()\n          assertThat(dispatcherF.closed).isFalse() // Never started.\n        }\n      }\n  }\n\n  class ClosableDispatcher : Dispatcher() {\n    var closed = false\n\n    override fun dispatch(request: RecordedRequest) = MockResponse()\n\n    override fun close() {\n      closed = true\n    }\n  }\n}\n"
  },
  {
    "path": "module-tests/build.gradle.kts",
    "content": "import okhttp3.buildsupport.testJavaVersion\n\nplugins {\n  id(\"okhttp.base-conventions\")\n  id(\"java\")\n  id(\"application\")\n  alias(libs.plugins.jlink)\n  alias(libs.plugins.extra.java.module.info)\n}\n\ndependencies {\n  implementation(projects.okhttp)\n  implementation(projects.loggingInterceptor)\n\n  // Force version 26.0.2-1 which is a proper JPMS module, unlike transitive 13.0\n  implementation(libs.jetbrains.annotations)\n\n  testImplementation(projects.okhttp)\n  testImplementation(projects.loggingInterceptor)\n  testImplementation(projects.mockwebserver3)\n  testImplementation(projects.mockwebserver3Junit5)\n\n  testImplementation(libs.junit.jupiter.api)\n  testRuntimeOnly(libs.junit.jupiter.engine)\n  testRuntimeOnly(libs.junit.platform.launcher)\n}\n\napplication {\n  mainClass = \"okhttp3.modules.Main\"\n  mainModule = \"okhttp3.modules\"\n}\n\njlinkApplication {\n  stripDebug = true\n  stripJavaDebugAttributes = true\n  compress.set(\"zip-9\")\n  addModules.addAll(\"jdk.crypto.ec\", \"java.logging\")\n  vm.set(\"server\")\n}\n\nextraJavaModuleInfo {\n  module(\"com.squareup.okio:okio-jvm\", \"okio\") {\n    exportAllPackages()\n    requires(\"kotlin.stdlib\")\n    requires(\"java.logging\")\n  }\n  module(\"com.squareup.okio:okio\", \"okio\") {\n    exportAllPackages()\n  }\n}\n\n// Exclude dokka from all configurations\n// to attempt to avoid https://github.com/gradlex-org/extra-java-module-info/issues/221\nconfigurations.all {\n  exclude(group = \"org.jetbrains.dokka\")\n}\n\n\nval testJavaVersion = project.testJavaVersion\n\ntasks.withType<Test> {\n  useJUnitPlatform()\n\n  enabled = testJavaVersion > 8\n\n  javaLauncher.set(javaToolchains.launcherFor {\n    languageVersion.set(JavaLanguageVersion.of(testJavaVersion))\n  })\n}\n\njava {\n  sourceCompatibility = JavaVersion.VERSION_11\n  targetCompatibility = JavaVersion.VERSION_11\n  toolchain {\n    languageVersion.set(JavaLanguageVersion.of(21))\n  }\n}\n"
  },
  {
    "path": "module-tests/src/main/java/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.modules {\n  requires okhttp3;\n  requires okhttp3.logging;\n  requires jdk.crypto.ec;\n  exports okhttp3.modules;\n}\n"
  },
  {
    "path": "module-tests/src/main/java/okhttp3/modules/Main.java",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.modules;\n\nimport okhttp3.Call;\nimport okhttp3.HttpUrl;\n\nimport java.io.IOException;\n\npublic class Main {\n  public static void main(String[] args) throws IOException {\n    Call call = OkHttpCaller.callOkHttp(HttpUrl.get(\"https://square.com/robots.txt\"));\n\n    System.out.println(call.execute().body().string());\n  }\n}\n"
  },
  {
    "path": "module-tests/src/main/java/okhttp3/modules/OkHttpCaller.java",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.modules;\n\nimport okhttp3.Call;\nimport okhttp3.HttpUrl;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.logging.HttpLoggingInterceptor;\nimport okhttp3.logging.LoggingEventListener;\n\n/**\n * Just checking compilation works\n */\npublic class OkHttpCaller {\n  public static Call callOkHttp(HttpUrl url) {\n    OkHttpClient client = new OkHttpClient\n      .Builder()\n      .eventListenerFactory(new LoggingEventListener.Factory(HttpLoggingInterceptor.Logger.DEFAULT))\n      .build();\n    return client.newCall(new Request.Builder().url(url).build());\n  }\n}\n"
  },
  {
    "path": "module-tests/src/test/java/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.modules.test {\n  requires okhttp3;\n  requires okhttp3.logging;\n  requires mockwebserver3;\n  requires mockwebserver3.junit5;\n  requires jdk.crypto.ec;\n  requires org.junit.jupiter.api;\n  requires okhttp3.modules;\n  opens okhttp3.modules.test to org.junit.platform.commons;\n}\n"
  },
  {
    "path": "module-tests/src/test/java/okhttp3/modules/test/JavaModuleTest.java",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.modules.test;\n\nimport mockwebserver3.MockResponse;\nimport mockwebserver3.MockWebServer;\nimport okhttp3.Call;\nimport okhttp3.Headers;\nimport okhttp3.HttpUrl;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Response;\nimport okhttp3.logging.HttpLoggingInterceptor;\nimport okhttp3.modules.OkHttpCaller;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nclass JavaModuleTest {\n  @Test\n  public void testVisibility() {\n    // Just check we can run code that depends on OkHttp types\n    OkHttpCaller.callOkHttp(HttpUrl.get(\"https://square.com/robots.txt\"));\n  }\n\n  @Test\n  public void testMockWebServer() throws IOException {\n    MockWebServer server = new MockWebServer();\n    server.enqueue(new MockResponse(200, Headers.of(), \"Hello, Java9!\"));\n    server.start();\n\n    // Just check we can run code that depends on OkHttp types\n    Call call = OkHttpCaller.callOkHttp(server.url(\"/\"));\n\n    try (Response response = call.execute();) {\n      System.out.println(response.body().string());\n    }\n  }\n\n  @Test\n  public void testModules() {\n    Module okHttpModule = OkHttpClient.class.getModule();\n    assertEquals(\"okhttp3\", okHttpModule.getName());\n    assertTrue(okHttpModule.getPackages().contains(\"okhttp3\"));\n\n    Module loggingInterceptorModule = HttpLoggingInterceptor.class.getModule();\n    assertEquals(\"okhttp3.logging\", loggingInterceptorModule.getName());\n    assertTrue(loggingInterceptorModule.getPackages().contains(\"okhttp3.logging\"));\n  }\n}\n"
  },
  {
    "path": "native-image-tests/README.md",
    "content": "Native Image Tests\n==================\n\nThis executes OkHttp's test suite inside a Graalvm image.\n\nExecute\n-------\n\nThe native image runs JUnit 5 tests in the project.\n\n```\n./gradlew -PgraalBuild=true --info native-image-tests:nativeTest\n```\n\n"
  },
  {
    "path": "native-image-tests/build.gradle.kts",
    "content": "import org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nplugins {\n  id(\"org.graalvm.buildtools.native\")\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\ntasks.withType<KotlinCompile> {\n  compilerOptions {\n    jvmTarget.set(JvmTarget.JVM_17)\n  }\n}\n\ntasks.withType<JavaCompile> {\n  sourceCompatibility = JvmTarget.JVM_17.target\n  targetCompatibility = JvmTarget.JVM_17.target\n}\n\n// TODO reenable other tests\n// https://github.com/square/okhttp/issues/8901\n// sourceSets {\n//  test {\n//    java.srcDirs(\n//      \"../okhttp-brotli/src/test/java\",\n//      \"../okhttp-dnsoverhttps/src/test/java\",\n//      \"../okhttp-logging-interceptor/src/test/java\",\n//      \"../okhttp-sse/src/test/java\",\n//    )\n//  }\n// }\n\ndependencies {\n  implementation(projects.okhttp)\n\n  testImplementation(projects.mockwebserver3Junit5)\n  testImplementation(libs.assertk)\n  testRuntimeOnly(libs.junit.jupiter.engine)\n  testImplementation(libs.kotlin.junit5)\n  testImplementation(libs.junit.jupiter.params)\n}\n\ngraalvmNative {\n  testSupport = true\n\n  binaries {\n    named(\"test\") {\n      buildArgs.add(\"--strict-image-heap\")\n\n      // speed up development testing\n      buildArgs.add(\"-Ob\")\n    }\n  }\n}\n"
  },
  {
    "path": "native-image-tests/src/test/kotlin/okhttp3/nativeimage/PublicSuffixDatabaseTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.nativeimage\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport org.junit.jupiter.api.Test\n\nclass PublicSuffixDatabaseTest {\n  @Test\n  fun testResourcesLoaded() {\n    val url = \"https://api.twitter.com\".toHttpUrl()\n\n    assertThat(url.topPrivateDomain()).isEqualTo(\"twitter.com\")\n  }\n}\n"
  },
  {
    "path": "native-image-tests/src/test/kotlin/okhttp3/nativeimage/SampleTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.nativeimage\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport org.junit.jupiter.api.Test\n\nclass SampleTest {\n  private val server = MockWebServer()\n\n  private val client = OkHttpClient()\n\n  @Test\n  fun passingTest() {\n    assertThat(\"hello\").isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun testMockWebServer() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.start()\n\n    client.newCall(Request(url = server.url(\"/\"))).execute().use {\n      assertThat(it.body.string()).isEqualTo(\"abc\")\n    }\n  }\n\n  @Test\n  fun testExternalSite() {\n    client.newCall(Request(url = \"https://google.com/robots.txt\".toHttpUrl())).execute().use {\n      assertThat(it.code).isEqualTo(200)\n    }\n  }\n}\n"
  },
  {
    "path": "native-image-tests/src/test/kotlin/okhttp3/nativeimage/WithArgumentSourceTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.nativeimage\n\nimport assertk.assertThat\nimport assertk.assertions.isGreaterThan\nimport java.util.stream.Stream\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.Arguments\nimport org.junit.jupiter.params.provider.ArgumentsProvider\nimport org.junit.jupiter.params.provider.ArgumentsSource\nimport org.junit.jupiter.params.support.ParameterDeclarations\n\n/**\n * This enforces us having the params classes on the classpath to workaround\n * https://github.com/graalvm/native-build-tools/issues/745\n */\nclass WithArgumentSourceTest {\n  @ParameterizedTest\n  @ArgumentsSource(FakeArgumentsProvider::class)\n  fun passingTest(value: Int) {\n    assertThat(value).isGreaterThan(0)\n  }\n}\n\ninternal class FakeArgumentsProvider : ArgumentsProvider {\n  override fun provideArguments(\n    parameters: ParameterDeclarations?,\n    context: ExtensionContext?,\n  ): Stream<out Arguments> = listOf(Arguments.of(1), Arguments.of(2)).stream()\n}\n"
  },
  {
    "path": "native-image-tests/src/test/resources/META-INF/native-image/okhttp/nit/resource-config.json",
    "content": "{\n  \"resources\": [\n    {\"pattern\": \"web-platform-test-urltestdata.txt\"}\n  ]\n}\n"
  },
  {
    "path": "okcurl/README.md",
    "content": "OkCurl\n======\n\n_A curl for the next-generation web._\n\nOkCurl is an OkHttp-backed curl clone which allows you to test OkHttp's HTTP engine (including\nHTTP/2) against web servers.\n\nTo run locally, make sure you have GRAALVM_HOME set and run\n\n```bash\n./okcurl\n```\n"
  },
  {
    "path": "okcurl/build.gradle.kts",
    "content": "import kotlinx.validation.ApiValidationExtension\nimport okhttp3.buildsupport.testJavaVersion\nimport org.graalvm.buildtools.gradle.dsl.GraalVMExtension\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\nimport ru.vyarus.gradle.plugin.animalsniffer.AnimalSnifferExtension\n\nplugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n  id(\"com.gradleup.shadow\")\n}\n\ntasks.withType<KotlinCompile> {\n  compilerOptions {\n    jvmTarget.set(JvmTarget.JVM_17)\n  }\n}\n\ntasks.withType<JavaCompile> {\n  sourceCompatibility = JvmTarget.JVM_17.target\n  targetCompatibility = JvmTarget.JVM_17.target\n}\n\nval copyResourcesTemplates =\n  tasks.register<Copy>(\"copyResourcesTemplates\") {\n    from(\"src/main/resources-templates\")\n    into(layout.buildDirectory.dir(\"generated/resources-templates\"))\n    expand(\"projectVersion\" to \"${project.version}\")\n    filteringCharset = Charsets.UTF_8.toString()\n  }\n\nconfigure<JavaPluginExtension> {\n  sourceSets.getByName(\"main\").resources.srcDir(copyResourcesTemplates.get().outputs)\n}\n\ndependencies {\n  api(projects.okhttp)\n  api(projects.loggingInterceptor)\n  api(libs.square.okio)\n  implementation(libs.clikt)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(projects.mockwebserver3)\n  testImplementation(projects.mockwebserver3Junit5)\n  testImplementation(libs.junit)\n  testImplementation(libs.assertk)\n  testImplementation(kotlin(\"test\"))\n}\n\nval jvmSignature = configurations.getByName(\"jvmSignature\")\nconfigure<AnimalSnifferExtension> {\n  // Only check JVM\n  signatures = jvmSignature\n}\n\nconfigure<ApiValidationExtension> {\n  validationDisabled = true\n}\n\ntasks.jar {\n  manifest {\n    attributes(\"Main-Class\" to \"okhttp3.curl.MainCommandLineKt\")\n  }\n}\n\ntasks.shadowJar {\n  mergeServiceFiles()\n}\n\ntasks.withType<Test> {\n  val javaVersion = project.testJavaVersion\n  onlyIf(\"native build requires Java 17\") {\n    javaVersion > 17\n  }\n}\n\napply(plugin = \"org.graalvm.buildtools.native\")\n\nconfigure<GraalVMExtension> {\n  binaries {\n    named(\"main\") {\n      imageName = \"okcurl\"\n      mainClass = \"okhttp3.curl.MainCommandLineKt\"\n      if (System.getProperty(\"os.name\").lowercase().contains(\"windows\")) {\n        // windows requires a slightly different approach for some things\n      } else {\n        buildArgs(\"--no-fallback\")\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okcurl/okcurl",
    "content": "#!/bin/sh -e\n\n../gradlew -q --console plain nativeBuild\n\n./build/graal/okcurl \"$@\"\n"
  },
  {
    "path": "okcurl/src/main/kotlin/okhttp3/curl/Main.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.curl\n\nimport com.github.ajalt.clikt.core.CliktCommand\nimport com.github.ajalt.clikt.core.Context\nimport com.github.ajalt.clikt.parameters.arguments.argument\nimport com.github.ajalt.clikt.parameters.arguments.help\nimport com.github.ajalt.clikt.parameters.options.default\nimport com.github.ajalt.clikt.parameters.options.flag\nimport com.github.ajalt.clikt.parameters.options.help\nimport com.github.ajalt.clikt.parameters.options.multiple\nimport com.github.ajalt.clikt.parameters.options.option\nimport com.github.ajalt.clikt.parameters.types.int\nimport java.security.cert.X509Certificate\nimport java.util.Properties\nimport java.util.concurrent.TimeUnit.SECONDS\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManager\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Call\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.curl.internal.commonCreateRequest\nimport okhttp3.curl.internal.commonRun\nimport okhttp3.curl.logging.LoggingUtil\nimport okhttp3.internal.platform.Platform\nimport okhttp3.logging.HttpLoggingInterceptor\nimport okhttp3.logging.LoggingEventListener\n\nclass Main : CliktCommand(name = NAME) {\n  override val printHelpOnEmptyArgs = true\n\n  override fun help(context: Context): String = \"A curl for the next-generation web.\"\n\n  val method: String? by option(\"-X\", \"--request\").help(\"Specify request command to use\")\n\n  val data: String? by option(\"-d\", \"--data\").help(\"HTTP POST data\")\n\n  val headers: List<String>? by option(\"-H\", \"--header\").help(\"Custom header to pass to server\").multiple()\n\n  val userAgent: String by option(\n    \"-A\",\n    \"--user-agent\",\n  ).help(\n    \"User-Agent to send to server\",\n  ).default(NAME + \"/\" + versionString())\n\n  val connectTimeout: Int by option(\n    \"--connect-timeout\",\n  ).help(\n    \"Maximum time allowed for connection (seconds)\",\n  ).int()\n    .default(DEFAULT_TIMEOUT)\n\n  val readTimeout: Int by option(\"--read-timeout\")\n    .help(\"Maximum time allowed for reading data (seconds)\")\n    .int()\n    .default(DEFAULT_TIMEOUT)\n\n  val callTimeout: Int by option(\n    \"--call-timeout\",\n  ).help(\n    \"Maximum time allowed for the entire call (seconds)\",\n  ).int()\n    .default(DEFAULT_TIMEOUT)\n\n  val followRedirects: Boolean by option(\"-L\", \"--location\").help(\"Follow redirects\").flag()\n\n  val allowInsecure: Boolean by option(\"-k\", \"--insecure\").help(\"Allow connections to SSL sites without certs\").flag()\n\n  val showHeaders: Boolean by option(\"-i\", \"--include\").help(\"Include protocol headers in the output\").flag()\n\n  val showHttp2Frames: Boolean by option(\"--frames\").help(\"Log HTTP/2 frames to STDERR\").flag()\n\n  val referer: String? by option(\"-e\", \"--referer\").help(\"Referer URL\")\n\n  val verbose: Boolean by option(\"-v\", \"--verbose\").help(\"Makes $NAME verbose during the operation\").flag()\n\n  val sslDebug: Boolean by option(\"--sslDebug\").help(\"Output SSL Debug\").flag()\n\n  val url: String? by argument(name = \"url\").help(\"Remote resource URL\")\n\n  var client: Call.Factory? = null\n\n  override fun run() {\n    LoggingUtil.configureLogging(debug = verbose, showHttp2Frames = showHttp2Frames, sslDebug = sslDebug)\n\n    commonRun()\n  }\n\n  fun createRequest(): Request = commonCreateRequest()\n\n  fun createClient(): Call.Factory {\n    val builder = OkHttpClient.Builder()\n    builder.followSslRedirects(followRedirects)\n    if (connectTimeout != DEFAULT_TIMEOUT) {\n      builder.connectTimeout(connectTimeout.toLong(), SECONDS)\n    }\n    if (readTimeout != DEFAULT_TIMEOUT) {\n      builder.readTimeout(readTimeout.toLong(), SECONDS)\n    }\n    if (callTimeout != DEFAULT_TIMEOUT) {\n      builder.callTimeout(callTimeout.toLong(), SECONDS)\n    }\n    if (allowInsecure) {\n      val trustManager = createInsecureTrustManager()\n      val sslSocketFactory = createInsecureSslSocketFactory(trustManager)\n      builder.sslSocketFactory(sslSocketFactory, trustManager)\n      builder.hostnameVerifier(createInsecureHostnameVerifier())\n    }\n    if (verbose) {\n      val logger = HttpLoggingInterceptor.Logger(::println)\n      builder.eventListenerFactory(LoggingEventListener.Factory(logger))\n    }\n    return builder.build()\n  }\n\n  fun close() {\n    val okHttpClient = client as OkHttpClient\n    okHttpClient.connectionPool.evictAll() // Close any persistent connections.\n    okHttpClient.dispatcher.executorService.shutdownNow()\n  }\n\n  companion object {\n    internal const val NAME = \"okcurl\"\n    internal const val DEFAULT_TIMEOUT = -1\n\n    private fun versionString(): String? {\n      val prop = Properties()\n      Main::class.java.getResourceAsStream(\"/okcurl-version.properties\")?.use {\n        prop.load(it)\n      }\n      return prop.getProperty(\"version\", \"dev\")\n    }\n\n    @Suppress(\"TrustAllX509TrustManager\", \"CustomX509TrustManager\")\n    private fun createInsecureTrustManager(): X509TrustManager =\n      object : X509TrustManager {\n        override fun checkClientTrusted(\n          chain: Array<X509Certificate>,\n          authType: String,\n        ) {\n        }\n\n        override fun checkServerTrusted(\n          chain: Array<X509Certificate>,\n          authType: String,\n        ) {\n        }\n\n        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()\n      }\n\n    private fun createInsecureSslSocketFactory(trustManager: TrustManager): SSLSocketFactory =\n      Platform\n        .get()\n        .newSSLContext()\n        .apply {\n          init(null, arrayOf(trustManager), null)\n        }.socketFactory\n\n    private fun createInsecureHostnameVerifier(): HostnameVerifier = HostnameVerifier { _, _ -> true }\n  }\n}\n"
  },
  {
    "path": "okcurl/src/main/kotlin/okhttp3/curl/MainCommandLine.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.curl\n\nimport com.github.ajalt.clikt.core.main\nimport kotlin.system.exitProcess\n\nfun main(args: Array<String>) {\n  Main().main(args)\n  exitProcess(0)\n}\n"
  },
  {
    "path": "okcurl/src/main/kotlin/okhttp3/curl/internal/-MainCommon.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.curl.internal\n\nimport java.io.IOException\nimport okhttp3.MediaType\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.curl.Main\nimport okhttp3.internal.http.StatusLine\nimport okio.sink\n\ninternal fun Main.commonCreateRequest(): Request {\n  val request = Request.Builder()\n\n  val requestMethod = method ?: if (data != null) \"POST\" else \"GET\"\n\n  val url = url ?: throw IOException(\"No url provided\")\n\n  request.url(url)\n\n  data?.let {\n    request.method(requestMethod, it.toRequestBody(mediaType()))\n  }\n\n  for (header in headers.orEmpty()) {\n    val parts = header.split(':', limit = 2)\n    if (!isSpecialHeader(parts[0])) {\n      request.header(parts[0], parts[1])\n    }\n  }\n  referer?.let {\n    request.header(\"Referer\", it)\n  }\n  request.header(\"User-Agent\", userAgent)\n\n  return request.build()\n}\n\nprivate fun Main.mediaType(): MediaType? {\n  val mimeType =\n    headers?.let {\n      for (header in it) {\n        val parts = header.split(':', limit = 2)\n        if (\"Content-Type\".equals(parts[0], ignoreCase = true)) {\n          return@let parts[1].trim()\n        }\n      }\n      return@let null\n    } ?: \"application/x-www-form-urlencoded\"\n\n  return mimeType.toMediaTypeOrNull()\n}\n\nprivate fun isSpecialHeader(s: String): Boolean = s.equals(\"Content-Type\", ignoreCase = true)\n\nfun Main.commonRun() {\n  client = createClient()\n  val request = createRequest()\n\n  try {\n    val response = client!!.newCall(request).execute()\n    if (showHeaders) {\n      println(StatusLine.get(response))\n      val headers = response.headers\n      for ((name, value) in headers) {\n        println(\"$name: $value\")\n      }\n      println()\n    }\n\n    // Stream the response to the System.out as it is returned from the server.\n    val out = System.out.sink()\n    val source = response.body.source()\n    while (!source.exhausted()) {\n      out.write(source.buffer, source.buffer.size)\n      out.flush()\n    }\n\n    response.body.close()\n  } catch (e: IOException) {\n    e.printStackTrace()\n  } finally {\n    close()\n  }\n}\n"
  },
  {
    "path": "okcurl/src/main/kotlin/okhttp3/curl/logging/LoggingUtil.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.curl.logging\n\nimport java.util.logging.ConsoleHandler\nimport java.util.logging.Level\nimport java.util.logging.LogManager\nimport java.util.logging.LogRecord\nimport java.util.logging.Logger\nimport okhttp3.internal.http2.Http2\n\nclass LoggingUtil {\n  companion object {\n    private val activeLoggers = mutableListOf<Logger>()\n\n    fun configureLogging(\n      debug: Boolean,\n      showHttp2Frames: Boolean,\n      sslDebug: Boolean,\n    ) {\n      if (debug || showHttp2Frames || sslDebug) {\n        if (sslDebug) {\n          System.setProperty(\"javax.net.debug\", \"\")\n        }\n        LogManager.getLogManager().reset()\n        val handler =\n          object : ConsoleHandler() {\n            override fun publish(record: LogRecord) {\n              super.publish(record)\n\n              val parameters = record.parameters\n              if (sslDebug && record.loggerName == \"javax.net.ssl\" && parameters != null) {\n                System.err.println(parameters[0])\n              }\n            }\n          }\n\n        if (debug) {\n          handler.level = Level.ALL\n          handler.formatter = OneLineLogFormat()\n          val activeLogger = getLogger(\"\")\n          activeLogger.addHandler(handler)\n          activeLogger.level = Level.ALL\n\n          getLogger(\"jdk.event.security\").level = Level.INFO\n          getLogger(\"org.conscrypt\").level = Level.INFO\n        } else {\n          if (showHttp2Frames) {\n            val activeLogger = getLogger(Http2::class.java.name)\n            activeLogger.level = Level.FINE\n            handler.level = Level.FINE\n            handler.formatter = MessageFormatter\n            activeLogger.addHandler(handler)\n          }\n\n          if (sslDebug) {\n            val activeLogger = getLogger(\"javax.net.ssl\")\n\n            activeLogger.level = Level.FINEST\n            handler.level = Level.FINEST\n            handler.formatter = MessageFormatter\n            activeLogger.addHandler(handler)\n          }\n        }\n      }\n    }\n\n    fun getLogger(name: String): Logger {\n      val logger = Logger.getLogger(name)\n      activeLoggers.add(logger)\n      return logger\n    }\n  }\n}\n"
  },
  {
    "path": "okcurl/src/main/kotlin/okhttp3/curl/logging/MessageFormatter.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.curl.logging\n\nimport java.util.logging.LogRecord\nimport java.util.logging.SimpleFormatter\n\nobject MessageFormatter : SimpleFormatter() {\n  override fun format(record: LogRecord): String = String.format(\"%s%n\", record.message)\n}\n"
  },
  {
    "path": "okcurl/src/main/kotlin/okhttp3/curl/logging/OneLineLogFormat.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.curl.logging\n\nimport java.io.PrintWriter\nimport java.io.StringWriter\nimport java.time.Instant\nimport java.time.ZoneOffset\nimport java.time.format.DateTimeFormatterBuilder\nimport java.time.temporal.ChronoField.HOUR_OF_DAY\nimport java.time.temporal.ChronoField.MINUTE_OF_HOUR\nimport java.time.temporal.ChronoField.NANO_OF_SECOND\nimport java.time.temporal.ChronoField.SECOND_OF_MINUTE\nimport java.util.logging.Formatter\nimport java.util.logging.LogRecord\n\n/**\n * Is Java8 Data and Time really this bad, or is writing this on a plane from just javadocs a bad\n * idea?\n *\n * Why so much construction?\n */\nclass OneLineLogFormat : Formatter() {\n  private val d =\n    DateTimeFormatterBuilder()\n      .appendValue(HOUR_OF_DAY, 2)\n      .appendLiteral(':')\n      .appendValue(MINUTE_OF_HOUR, 2)\n      .optionalStart()\n      .appendLiteral(':')\n      .appendValue(SECOND_OF_MINUTE, 2)\n      .optionalStart()\n      .appendFraction(NANO_OF_SECOND, 3, 3, true)\n      .toFormatter()\n\n  private val offset = ZoneOffset.systemDefault()\n\n  override fun format(record: LogRecord): String {\n    val message = formatMessage(record)\n\n    val time = Instant.ofEpochMilli(record.millis).atZone(offset)\n\n    return if (record.thrown != null) {\n      val sw = StringWriter(4096)\n      val pw = PrintWriter(sw)\n      record.thrown.printStackTrace(pw)\n      String.format(\"%s\\t%s%n%s%n\", time.format(d), message, sw.toString())\n    } else {\n      String.format(\"%s\\t%s%n\", time.format(d), message)\n    }\n  }\n}\n"
  },
  {
    "path": "okcurl/src/main/resources/META-INF/native-image/okhttp3/okcurl/reflect-config.json",
    "content": "[\n]\n"
  },
  {
    "path": "okcurl/src/main/resources/META-INF/native-image/okhttp3/okcurl/resource-config.json",
    "content": "{\n  \"resources\": [\n    {\"pattern\": \"okcurl-version.properties\"}\n  ]\n}\n"
  },
  {
    "path": "okcurl/src/main/resources-templates/okcurl-version.properties",
    "content": "version=${projectVersion}\n"
  },
  {
    "path": "okcurl/src/test/kotlin/okhttp3/curl/MainTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.curl\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.startsWith\nimport com.github.ajalt.clikt.core.parse\nimport java.io.IOException\nimport kotlin.test.Test\nimport okhttp3.RequestBody\nimport okio.Buffer\n\nclass MainTest {\n  @Test\n  fun simple() {\n    val request = fromArgs(\"http://example.com\").createRequest()\n    assertThat(request.method).isEqualTo(\"GET\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com/\")\n    assertThat(request.body).isNull()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun put() {\n    val request = fromArgs(\"-X\", \"PUT\", \"-d\", \"foo\", \"http://example.com\").createRequest()\n    assertThat(request.method).isEqualTo(\"PUT\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com/\")\n    assertThat(request.body!!.contentLength()).isEqualTo(3)\n  }\n\n  @Test\n  fun dataPost() {\n    val request = fromArgs(\"-d\", \"foo\", \"http://example.com\").createRequest()\n    val body = request.body\n    assertThat(request.method).isEqualTo(\"POST\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com/\")\n    assertThat(body!!.contentType().toString()).isEqualTo(\n      \"application/x-www-form-urlencoded; charset=utf-8\",\n    )\n    assertThat(bodyAsString(body)).isEqualTo(\"foo\")\n  }\n\n  @Test\n  fun dataPut() {\n    val request = fromArgs(\"-d\", \"foo\", \"-X\", \"PUT\", \"http://example.com\").createRequest()\n    val body = request.body\n    assertThat(request.method).isEqualTo(\"PUT\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com/\")\n    assertThat(body!!.contentType().toString()).isEqualTo(\n      \"application/x-www-form-urlencoded; charset=utf-8\",\n    )\n    assertThat(bodyAsString(body)).isEqualTo(\"foo\")\n  }\n\n  @Test\n  fun contentTypeHeader() {\n    val request =\n      fromArgs(\n        \"-d\",\n        \"foo\",\n        \"-H\",\n        \"Content-Type: application/json\",\n        \"http://example.com\",\n      ).createRequest()\n    val body = request.body\n    assertThat(request.method).isEqualTo(\"POST\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com/\")\n    assertThat(body!!.contentType().toString())\n      .isEqualTo(\"application/json; charset=utf-8\")\n    assertThat(bodyAsString(body)).isEqualTo(\"foo\")\n  }\n\n  @Test\n  fun referer() {\n    val request = fromArgs(\"-e\", \"foo\", \"http://example.com\").createRequest()\n    assertThat(request.method).isEqualTo(\"GET\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com/\")\n    assertThat(request.header(\"Referer\")).isEqualTo(\"foo\")\n    assertThat(request.body).isNull()\n  }\n\n  @Test\n  fun userAgent() {\n    val request = fromArgs(\"-A\", \"foo\", \"http://example.com\").createRequest()\n    assertThat(request.method).isEqualTo(\"GET\")\n    assertThat(request.url.toString()).isEqualTo(\"http://example.com/\")\n    assertThat(request.header(\"User-Agent\")).isEqualTo(\"foo\")\n    assertThat(request.body).isNull()\n  }\n\n  @Test\n  fun defaultUserAgent() {\n    val request = fromArgs(\"http://example.com\").createRequest()\n    assertThat(request.header(\"User-Agent\")!!).startsWith(\"okcurl/\")\n  }\n\n  @Test\n  fun headerSplitWithDate() {\n    val request =\n      fromArgs(\n        \"-H\",\n        \"If-Modified-Since: Mon, 18 Aug 2014 15:16:06 GMT\",\n        \"http://example.com\",\n      ).createRequest()\n    assertThat(request.header(\"If-Modified-Since\")).isEqualTo(\n      \"Mon, 18 Aug 2014 15:16:06 GMT\",\n    )\n  }\n\n  companion object {\n    fun fromArgs(vararg args: String): Main =\n      Main().apply {\n        parse(args.toList())\n      }\n\n    private fun bodyAsString(body: RequestBody?): String =\n      try {\n        val buffer = Buffer()\n        body!!.writeTo(buffer)\n        buffer.readString(body.contentType()!!.charset()!!)\n      } catch (e: IOException) {\n        throw RuntimeException(e)\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp/Module.md",
    "content": "# Module okhttp\n\nAn HTTP+HTTP/2 client for Android and Java applications.\n"
  },
  {
    "path": "okhttp/api/android/okhttp.api",
    "content": "public final class okhttp3/Address {\n\tpublic final fun -deprecated_certificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic final fun -deprecated_connectionSpecs ()Ljava/util/List;\n\tpublic final fun -deprecated_dns ()Lokhttp3/Dns;\n\tpublic final fun -deprecated_hostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic final fun -deprecated_protocols ()Ljava/util/List;\n\tpublic final fun -deprecated_proxy ()Ljava/net/Proxy;\n\tpublic final fun -deprecated_proxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic final fun -deprecated_proxySelector ()Ljava/net/ProxySelector;\n\tpublic final fun -deprecated_socketFactory ()Ljavax/net/SocketFactory;\n\tpublic final fun -deprecated_sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic final fun -deprecated_url ()Lokhttp3/HttpUrl;\n\tpublic fun <init> (Ljava/lang/String;ILokhttp3/Dns;Ljavax/net/SocketFactory;Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/HostnameVerifier;Lokhttp3/CertificatePinner;Lokhttp3/Authenticator;Ljava/net/Proxy;Ljava/util/List;Ljava/util/List;Ljava/net/ProxySelector;)V\n\tpublic final fun certificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic final fun connectionSpecs ()Ljava/util/List;\n\tpublic final fun dns ()Lokhttp3/Dns;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic fun hashCode ()I\n\tpublic final fun hostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic final fun protocols ()Ljava/util/List;\n\tpublic final fun proxy ()Ljava/net/Proxy;\n\tpublic final fun proxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic final fun proxySelector ()Ljava/net/ProxySelector;\n\tpublic final fun socketFactory ()Ljavax/net/SocketFactory;\n\tpublic final fun sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun url ()Lokhttp3/HttpUrl;\n}\n\npublic abstract interface class okhttp3/Authenticator {\n\tpublic static final field Companion Lokhttp3/Authenticator$Companion;\n\tpublic static final field JAVA_NET_AUTHENTICATOR Lokhttp3/Authenticator;\n\tpublic static final field NONE Lokhttp3/Authenticator;\n\tpublic abstract fun authenticate (Lokhttp3/Route;Lokhttp3/Response;)Lokhttp3/Request;\n}\n\npublic final class okhttp3/Authenticator$Companion {\n}\n\npublic final class okhttp3/Cache : java/io/Closeable, java/io/Flushable {\n\tpublic static final field Companion Lokhttp3/Cache$Companion;\n\tpublic final fun -deprecated_directory ()Ljava/io/File;\n\tpublic fun <init> (Ljava/io/File;J)V\n\tpublic fun <init> (Lokio/FileSystem;Lokio/Path;J)V\n\tpublic fun close ()V\n\tpublic final fun delete ()V\n\tpublic final fun directory ()Ljava/io/File;\n\tpublic final fun directoryPath ()Lokio/Path;\n\tpublic final fun evictAll ()V\n\tpublic fun flush ()V\n\tpublic final fun hitCount ()I\n\tpublic final fun initialize ()V\n\tpublic final fun isClosed ()Z\n\tpublic static final fun key (Lokhttp3/HttpUrl;)Ljava/lang/String;\n\tpublic final fun maxSize ()J\n\tpublic final fun networkCount ()I\n\tpublic final fun requestCount ()I\n\tpublic final fun size ()J\n\tpublic final fun urls ()Ljava/util/Iterator;\n\tpublic final fun writeAbortCount ()I\n\tpublic final fun writeSuccessCount ()I\n}\n\npublic final class okhttp3/Cache$Companion {\n\tpublic final fun hasVaryAll (Lokhttp3/Response;)Z\n\tpublic final fun key (Lokhttp3/HttpUrl;)Ljava/lang/String;\n\tpublic final fun varyHeaders (Lokhttp3/Response;)Lokhttp3/Headers;\n\tpublic final fun varyMatches (Lokhttp3/Response;Lokhttp3/Headers;Lokhttp3/Request;)Z\n}\n\npublic final class okhttp3/CacheControl {\n\tpublic static final field Companion Lokhttp3/CacheControl$Companion;\n\tpublic static final field FORCE_CACHE Lokhttp3/CacheControl;\n\tpublic static final field FORCE_NETWORK Lokhttp3/CacheControl;\n\tpublic final fun -deprecated_immutable ()Z\n\tpublic final fun -deprecated_maxAgeSeconds ()I\n\tpublic final fun -deprecated_maxStaleSeconds ()I\n\tpublic final fun -deprecated_minFreshSeconds ()I\n\tpublic final fun -deprecated_mustRevalidate ()Z\n\tpublic final fun -deprecated_noCache ()Z\n\tpublic final fun -deprecated_noStore ()Z\n\tpublic final fun -deprecated_noTransform ()Z\n\tpublic final fun -deprecated_onlyIfCached ()Z\n\tpublic final fun -deprecated_sMaxAgeSeconds ()I\n\tpublic final fun immutable ()Z\n\tpublic final fun isPrivate ()Z\n\tpublic final fun isPublic ()Z\n\tpublic final fun maxAgeSeconds ()I\n\tpublic final fun maxStaleSeconds ()I\n\tpublic final fun minFreshSeconds ()I\n\tpublic final fun mustRevalidate ()Z\n\tpublic final fun noCache ()Z\n\tpublic final fun noStore ()Z\n\tpublic final fun noTransform ()Z\n\tpublic final fun onlyIfCached ()Z\n\tpublic static final fun parse (Lokhttp3/Headers;)Lokhttp3/CacheControl;\n\tpublic final fun sMaxAgeSeconds ()I\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/CacheControl$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun build ()Lokhttp3/CacheControl;\n\tpublic final fun immutable ()Lokhttp3/CacheControl$Builder;\n\tpublic final fun maxAge (ILjava/util/concurrent/TimeUnit;)Lokhttp3/CacheControl$Builder;\n\tpublic final fun maxAge-LRDsOJo (J)Lokhttp3/CacheControl$Builder;\n\tpublic final fun maxStale (ILjava/util/concurrent/TimeUnit;)Lokhttp3/CacheControl$Builder;\n\tpublic final fun maxStale-LRDsOJo (J)Lokhttp3/CacheControl$Builder;\n\tpublic final fun minFresh (ILjava/util/concurrent/TimeUnit;)Lokhttp3/CacheControl$Builder;\n\tpublic final fun minFresh-LRDsOJo (J)Lokhttp3/CacheControl$Builder;\n\tpublic final fun noCache ()Lokhttp3/CacheControl$Builder;\n\tpublic final fun noStore ()Lokhttp3/CacheControl$Builder;\n\tpublic final fun noTransform ()Lokhttp3/CacheControl$Builder;\n\tpublic final fun onlyIfCached ()Lokhttp3/CacheControl$Builder;\n}\n\npublic final class okhttp3/CacheControl$Companion {\n\tpublic final fun parse (Lokhttp3/Headers;)Lokhttp3/CacheControl;\n}\n\npublic abstract interface class okhttp3/Call : java/lang/Cloneable {\n\tpublic abstract fun addEventListener (Lokhttp3/EventListener;)V\n\tpublic abstract fun cancel ()V\n\tpublic abstract fun clone ()Lokhttp3/Call;\n\tpublic abstract fun enqueue (Lokhttp3/Callback;)V\n\tpublic abstract fun execute ()Lokhttp3/Response;\n\tpublic abstract fun isCanceled ()Z\n\tpublic abstract fun isExecuted ()Z\n\tpublic abstract fun request ()Lokhttp3/Request;\n\tpublic abstract fun tag (Ljava/lang/Class;)Ljava/lang/Object;\n\tpublic abstract fun tag (Ljava/lang/Class;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;\n\tpublic abstract fun tag (Lkotlin/reflect/KClass;)Ljava/lang/Object;\n\tpublic abstract fun tag (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;\n\tpublic abstract fun timeout ()Lokio/Timeout;\n}\n\npublic abstract interface class okhttp3/Call$Factory {\n\tpublic abstract fun newCall (Lokhttp3/Request;)Lokhttp3/Call;\n}\n\npublic abstract interface class okhttp3/Callback {\n\tpublic abstract fun onFailure (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic abstract fun onResponse (Lokhttp3/Call;Lokhttp3/Response;)V\n}\n\npublic final class okhttp3/CertificatePinner {\n\tpublic static final field Companion Lokhttp3/CertificatePinner$Companion;\n\tpublic static final field DEFAULT Lokhttp3/CertificatePinner;\n\tpublic final fun check (Ljava/lang/String;Ljava/util/List;)V\n\tpublic final fun check (Ljava/lang/String;[Ljava/security/cert/Certificate;)V\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun findMatchingPins (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun getPins ()Ljava/util/Set;\n\tpublic fun hashCode ()I\n\tpublic static final fun pin (Ljava/security/cert/Certificate;)Ljava/lang/String;\n\tpublic static final fun sha1Hash (Ljava/security/cert/X509Certificate;)Lokio/ByteString;\n\tpublic static final fun sha256Hash (Ljava/security/cert/X509Certificate;)Lokio/ByteString;\n}\n\npublic final class okhttp3/CertificatePinner$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun add (Ljava/lang/String;[Ljava/lang/String;)Lokhttp3/CertificatePinner$Builder;\n\tpublic final fun build ()Lokhttp3/CertificatePinner;\n\tpublic final fun getPins ()Ljava/util/List;\n}\n\npublic final class okhttp3/CertificatePinner$Companion {\n\tpublic final fun pin (Ljava/security/cert/Certificate;)Ljava/lang/String;\n\tpublic final fun sha1Hash (Ljava/security/cert/X509Certificate;)Lokio/ByteString;\n\tpublic final fun sha256Hash (Ljava/security/cert/X509Certificate;)Lokio/ByteString;\n}\n\npublic final class okhttp3/CertificatePinner$Pin {\n\tpublic fun <init> (Ljava/lang/String;Ljava/lang/String;)V\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun getHash ()Lokio/ByteString;\n\tpublic final fun getHashAlgorithm ()Ljava/lang/String;\n\tpublic final fun getPattern ()Ljava/lang/String;\n\tpublic fun hashCode ()I\n\tpublic final fun matchesCertificate (Ljava/security/cert/X509Certificate;)Z\n\tpublic final fun matchesHostname (Ljava/lang/String;)Z\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/Challenge {\n\tpublic final fun -deprecated_authParams ()Ljava/util/Map;\n\tpublic final fun -deprecated_charset ()Ljava/nio/charset/Charset;\n\tpublic final fun -deprecated_realm ()Ljava/lang/String;\n\tpublic final fun -deprecated_scheme ()Ljava/lang/String;\n\tpublic fun <init> (Ljava/lang/String;Ljava/lang/String;)V\n\tpublic fun <init> (Ljava/lang/String;Ljava/util/Map;)V\n\tpublic final fun authParams ()Ljava/util/Map;\n\tpublic final fun charset ()Ljava/nio/charset/Charset;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic fun hashCode ()I\n\tpublic final fun realm ()Ljava/lang/String;\n\tpublic final fun scheme ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun withCharset (Ljava/nio/charset/Charset;)Lokhttp3/Challenge;\n}\n\npublic final class okhttp3/CipherSuite {\n\tpublic static final field Companion Lokhttp3/CipherSuite$Companion;\n\tpublic static final field TLS_AES_128_CCM_8_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_AES_128_CCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_256_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_RC4_128_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_EMPTY_RENEGOTIATION_INFO_SCSV Lokhttp3/CipherSuite;\n\tpublic static final field TLS_FALLBACK_SCSV Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_EXPORT_WITH_RC4_40_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_EXPORT_WITH_RC4_40_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_3DES_EDE_CBC_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_DES_CBC_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_RC4_128_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_PSK_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_PSK_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_PSK_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_PSK_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_EXPORT_WITH_DES40_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_EXPORT_WITH_RC4_40_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_256_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_CAMELLIA_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_CAMELLIA_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_NULL_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_NULL_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_RC4_128_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_SEED_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic final fun -deprecated_javaName ()Ljava/lang/String;\n\tpublic synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic static final fun forJavaName (Ljava/lang/String;)Lokhttp3/CipherSuite;\n\tpublic final fun javaName ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/CipherSuite$Companion {\n\tpublic final fun forJavaName (Ljava/lang/String;)Lokhttp3/CipherSuite;\n}\n\npublic class okhttp3/CompressionInterceptor : okhttp3/Interceptor {\n\tpublic fun <init> ([Lokhttp3/CompressionInterceptor$DecompressionAlgorithm;)V\n\tpublic final fun getAlgorithms ()[Lokhttp3/CompressionInterceptor$DecompressionAlgorithm;\n\tpublic fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\n}\n\npublic abstract interface class okhttp3/CompressionInterceptor$DecompressionAlgorithm {\n\tpublic abstract fun decompress (Lokio/BufferedSource;)Lokio/Source;\n\tpublic abstract fun getEncoding ()Ljava/lang/String;\n}\n\npublic abstract interface class okhttp3/Connection {\n\tpublic abstract fun handshake ()Lokhttp3/Handshake;\n\tpublic abstract fun protocol ()Lokhttp3/Protocol;\n\tpublic abstract fun route ()Lokhttp3/Route;\n\tpublic abstract fun socket ()Ljava/net/Socket;\n}\n\npublic final class okhttp3/ConnectionPool {\n\tpublic fun <init> ()V\n\tpublic fun <init> (IJLjava/util/concurrent/TimeUnit;)V\n\tpublic final fun connectionCount ()I\n\tpublic final fun evictAll ()V\n\tpublic final fun idleConnectionCount ()I\n}\n\npublic final class okhttp3/ConnectionSpec {\n\tpublic static final field CLEARTEXT Lokhttp3/ConnectionSpec;\n\tpublic static final field COMPATIBLE_TLS Lokhttp3/ConnectionSpec;\n\tpublic static final field Companion Lokhttp3/ConnectionSpec$Companion;\n\tpublic static final field MODERN_TLS Lokhttp3/ConnectionSpec;\n\tpublic static final field RESTRICTED_TLS Lokhttp3/ConnectionSpec;\n\tpublic final fun -deprecated_cipherSuites ()Ljava/util/List;\n\tpublic final fun -deprecated_supportsTlsExtensions ()Z\n\tpublic final fun -deprecated_tlsVersions ()Ljava/util/List;\n\tpublic final fun cipherSuites ()Ljava/util/List;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic fun hashCode ()I\n\tpublic final fun isCompatible (Ljavax/net/ssl/SSLSocket;)Z\n\tpublic final fun isTls ()Z\n\tpublic final fun supportsTlsExtensions ()Z\n\tpublic final fun tlsVersions ()Ljava/util/List;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/ConnectionSpec$Builder {\n\tpublic fun <init> (Lokhttp3/ConnectionSpec;)V\n\tpublic final fun allEnabledCipherSuites ()Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun allEnabledTlsVersions ()Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun build ()Lokhttp3/ConnectionSpec;\n\tpublic final fun cipherSuites ([Ljava/lang/String;)Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun cipherSuites ([Lokhttp3/CipherSuite;)Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun supportsTlsExtensions (Z)Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun tlsVersions ([Ljava/lang/String;)Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun tlsVersions ([Lokhttp3/TlsVersion;)Lokhttp3/ConnectionSpec$Builder;\n}\n\npublic final class okhttp3/ConnectionSpec$Companion {\n}\n\npublic final class okhttp3/Cookie {\n\tpublic static final field Companion Lokhttp3/Cookie$Companion;\n\tpublic final fun -deprecated_domain ()Ljava/lang/String;\n\tpublic final fun -deprecated_expiresAt ()J\n\tpublic final fun -deprecated_hostOnly ()Z\n\tpublic final fun -deprecated_httpOnly ()Z\n\tpublic final fun -deprecated_name ()Ljava/lang/String;\n\tpublic final fun -deprecated_path ()Ljava/lang/String;\n\tpublic final fun -deprecated_persistent ()Z\n\tpublic final fun -deprecated_secure ()Z\n\tpublic final fun -deprecated_value ()Ljava/lang/String;\n\tpublic synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;ZZZZLjava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun domain ()Ljava/lang/String;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun expiresAt ()J\n\tpublic fun hashCode ()I\n\tpublic final fun hostOnly ()Z\n\tpublic final fun httpOnly ()Z\n\tpublic final fun matches (Lokhttp3/HttpUrl;)Z\n\tpublic final fun name ()Ljava/lang/String;\n\tpublic final fun newBuilder ()Lokhttp3/Cookie$Builder;\n\tpublic static final fun parse (Lokhttp3/HttpUrl;Ljava/lang/String;)Lokhttp3/Cookie;\n\tpublic static final fun parseAll (Lokhttp3/HttpUrl;Lokhttp3/Headers;)Ljava/util/List;\n\tpublic final fun path ()Ljava/lang/String;\n\tpublic final fun persistent ()Z\n\tpublic final fun sameSite ()Ljava/lang/String;\n\tpublic final fun secure ()Z\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun value ()Ljava/lang/String;\n}\n\npublic final class okhttp3/Cookie$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun build ()Lokhttp3/Cookie;\n\tpublic final fun domain (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun expiresAt (J)Lokhttp3/Cookie$Builder;\n\tpublic final fun hostOnlyDomain (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun httpOnly ()Lokhttp3/Cookie$Builder;\n\tpublic final fun name (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun path (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun sameSite (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun secure ()Lokhttp3/Cookie$Builder;\n\tpublic final fun value (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n}\n\npublic final class okhttp3/Cookie$Companion {\n\tpublic final fun parse (Lokhttp3/HttpUrl;Ljava/lang/String;)Lokhttp3/Cookie;\n\tpublic final fun parseAll (Lokhttp3/HttpUrl;Lokhttp3/Headers;)Ljava/util/List;\n}\n\npublic abstract interface class okhttp3/CookieJar {\n\tpublic static final field Companion Lokhttp3/CookieJar$Companion;\n\tpublic static final field NO_COOKIES Lokhttp3/CookieJar;\n\tpublic abstract fun loadForRequest (Lokhttp3/HttpUrl;)Ljava/util/List;\n\tpublic abstract fun saveFromResponse (Lokhttp3/HttpUrl;Ljava/util/List;)V\n}\n\npublic final class okhttp3/CookieJar$Companion {\n}\n\npublic final class okhttp3/Credentials {\n\tpublic static final field INSTANCE Lokhttp3/Credentials;\n\tpublic static final fun basic (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n\tpublic static final fun basic (Ljava/lang/String;Ljava/lang/String;Ljava/nio/charset/Charset;)Ljava/lang/String;\n\tpublic static synthetic fun basic$default (Ljava/lang/String;Ljava/lang/String;Ljava/nio/charset/Charset;ILjava/lang/Object;)Ljava/lang/String;\n}\n\npublic final class okhttp3/Dispatcher {\n\tpublic final fun -deprecated_executorService ()Ljava/util/concurrent/ExecutorService;\n\tpublic fun <init> ()V\n\tpublic fun <init> (Ljava/util/concurrent/ExecutorService;)V\n\tpublic final fun cancelAll ()V\n\tpublic final fun executorService ()Ljava/util/concurrent/ExecutorService;\n\tpublic final fun getIdleCallback ()Ljava/lang/Runnable;\n\tpublic final fun getMaxRequests ()I\n\tpublic final fun getMaxRequestsPerHost ()I\n\tpublic final fun queuedCalls ()Ljava/util/List;\n\tpublic final fun queuedCallsCount ()I\n\tpublic final fun runningCalls ()Ljava/util/List;\n\tpublic final fun runningCallsCount ()I\n\tpublic final fun setIdleCallback (Ljava/lang/Runnable;)V\n\tpublic final fun setMaxRequests (I)V\n\tpublic final fun setMaxRequestsPerHost (I)V\n}\n\npublic abstract interface class okhttp3/Dns {\n\tpublic static final field Companion Lokhttp3/Dns$Companion;\n\tpublic static final field SYSTEM Lokhttp3/Dns;\n\tpublic abstract fun lookup (Ljava/lang/String;)Ljava/util/List;\n}\n\npublic final class okhttp3/Dns$Companion {\n}\n\npublic abstract class okhttp3/EventListener {\n\tpublic static final field Companion Lokhttp3/EventListener$Companion;\n\tpublic static final field NONE Lokhttp3/EventListener;\n\tpublic fun <init> ()V\n\tpublic fun cacheConditionalHit (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun cacheHit (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun cacheMiss (Lokhttp3/Call;)V\n\tpublic fun callEnd (Lokhttp3/Call;)V\n\tpublic fun callFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun callStart (Lokhttp3/Call;)V\n\tpublic fun canceled (Lokhttp3/Call;)V\n\tpublic fun connectEnd (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;)V\n\tpublic fun connectFailed (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;Ljava/io/IOException;)V\n\tpublic fun connectStart (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;)V\n\tpublic fun connectionAcquired (Lokhttp3/Call;Lokhttp3/Connection;)V\n\tpublic fun connectionReleased (Lokhttp3/Call;Lokhttp3/Connection;)V\n\tpublic fun dispatcherQueueEnd (Lokhttp3/Call;Lokhttp3/Dispatcher;)V\n\tpublic fun dispatcherQueueStart (Lokhttp3/Call;Lokhttp3/Dispatcher;)V\n\tpublic fun dnsEnd (Lokhttp3/Call;Ljava/lang/String;Ljava/util/List;)V\n\tpublic fun dnsStart (Lokhttp3/Call;Ljava/lang/String;)V\n\tpublic fun followUpDecision (Lokhttp3/Call;Lokhttp3/Response;Lokhttp3/Request;)V\n\tpublic final fun plus (Lokhttp3/EventListener;)Lokhttp3/EventListener;\n\tpublic fun proxySelectEnd (Lokhttp3/Call;Lokhttp3/HttpUrl;Ljava/util/List;)V\n\tpublic fun proxySelectStart (Lokhttp3/Call;Lokhttp3/HttpUrl;)V\n\tpublic fun requestBodyEnd (Lokhttp3/Call;J)V\n\tpublic fun requestBodyStart (Lokhttp3/Call;)V\n\tpublic fun requestFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun requestHeadersEnd (Lokhttp3/Call;Lokhttp3/Request;)V\n\tpublic fun requestHeadersStart (Lokhttp3/Call;)V\n\tpublic fun responseBodyEnd (Lokhttp3/Call;J)V\n\tpublic fun responseBodyStart (Lokhttp3/Call;)V\n\tpublic fun responseFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun responseHeadersEnd (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun responseHeadersStart (Lokhttp3/Call;)V\n\tpublic fun retryDecision (Lokhttp3/Call;Ljava/io/IOException;Z)V\n\tpublic fun satisfactionFailure (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun secureConnectEnd (Lokhttp3/Call;Lokhttp3/Handshake;)V\n\tpublic fun secureConnectStart (Lokhttp3/Call;)V\n}\n\npublic final class okhttp3/EventListener$Companion {\n}\n\npublic abstract interface class okhttp3/EventListener$Factory {\n\tpublic abstract fun create (Lokhttp3/Call;)Lokhttp3/EventListener;\n}\n\npublic final class okhttp3/FormBody : okhttp3/RequestBody {\n\tpublic static final field Companion Lokhttp3/FormBody$Companion;\n\tpublic final fun -deprecated_size ()I\n\tpublic fun contentLength ()J\n\tpublic fun contentType ()Lokhttp3/MediaType;\n\tpublic final fun encodedName (I)Ljava/lang/String;\n\tpublic final fun encodedValue (I)Ljava/lang/String;\n\tpublic final fun name (I)Ljava/lang/String;\n\tpublic final fun size ()I\n\tpublic final fun value (I)Ljava/lang/String;\n\tpublic fun writeTo (Lokio/BufferedSink;)V\n}\n\npublic final class okhttp3/FormBody$Builder {\n\tpublic fun <init> ()V\n\tpublic fun <init> (Ljava/nio/charset/Charset;)V\n\tpublic synthetic fun <init> (Ljava/nio/charset/Charset;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun add (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/FormBody$Builder;\n\tpublic final fun addEncoded (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/FormBody$Builder;\n\tpublic final fun build ()Lokhttp3/FormBody;\n}\n\npublic final class okhttp3/FormBody$Companion {\n}\n\npublic final class okhttp3/Gzip : okhttp3/CompressionInterceptor$DecompressionAlgorithm {\n\tpublic static final field INSTANCE Lokhttp3/Gzip;\n\tpublic fun decompress (Lokio/BufferedSource;)Lokio/Source;\n\tpublic fun getEncoding ()Ljava/lang/String;\n}\n\npublic final class okhttp3/Handshake {\n\tpublic static final field Companion Lokhttp3/Handshake$Companion;\n\tpublic final fun -deprecated_cipherSuite ()Lokhttp3/CipherSuite;\n\tpublic final fun -deprecated_localCertificates ()Ljava/util/List;\n\tpublic final fun -deprecated_localPrincipal ()Ljava/security/Principal;\n\tpublic final fun -deprecated_peerCertificates ()Ljava/util/List;\n\tpublic final fun -deprecated_peerPrincipal ()Ljava/security/Principal;\n\tpublic final fun -deprecated_tlsVersion ()Lokhttp3/TlsVersion;\n\tpublic final fun cipherSuite ()Lokhttp3/CipherSuite;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic static final fun get (Ljavax/net/ssl/SSLSession;)Lokhttp3/Handshake;\n\tpublic static final fun get (Lokhttp3/TlsVersion;Lokhttp3/CipherSuite;Ljava/util/List;Ljava/util/List;)Lokhttp3/Handshake;\n\tpublic fun hashCode ()I\n\tpublic final fun localCertificates ()Ljava/util/List;\n\tpublic final fun localPrincipal ()Ljava/security/Principal;\n\tpublic final fun peerCertificates ()Ljava/util/List;\n\tpublic final fun peerPrincipal ()Ljava/security/Principal;\n\tpublic final fun tlsVersion ()Lokhttp3/TlsVersion;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/Handshake$Companion {\n\tpublic final fun -deprecated_get (Ljavax/net/ssl/SSLSession;)Lokhttp3/Handshake;\n\tpublic final fun get (Ljavax/net/ssl/SSLSession;)Lokhttp3/Handshake;\n\tpublic final fun get (Lokhttp3/TlsVersion;Lokhttp3/CipherSuite;Ljava/util/List;Ljava/util/List;)Lokhttp3/Handshake;\n}\n\npublic final class okhttp3/Headers : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {\n\tpublic static final field Companion Lokhttp3/Headers$Companion;\n\tpublic static final field EMPTY Lokhttp3/Headers;\n\tpublic final fun -deprecated_size ()I\n\tpublic final fun byteCount ()J\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun get (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun getDate (Ljava/lang/String;)Ljava/util/Date;\n\tpublic final fun getInstant (Ljava/lang/String;)Ljava/time/Instant;\n\tpublic fun hashCode ()I\n\tpublic fun iterator ()Ljava/util/Iterator;\n\tpublic final fun name (I)Ljava/lang/String;\n\tpublic final fun names ()Ljava/util/Set;\n\tpublic final fun newBuilder ()Lokhttp3/Headers$Builder;\n\tpublic static final fun of (Ljava/util/Map;)Lokhttp3/Headers;\n\tpublic static final fun of ([Ljava/lang/String;)Lokhttp3/Headers;\n\tpublic final fun size ()I\n\tpublic final fun toMultimap ()Ljava/util/Map;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun value (I)Ljava/lang/String;\n\tpublic final fun values (Ljava/lang/String;)Ljava/util/List;\n}\n\npublic final class okhttp3/Headers$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun add (Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun add (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun add (Ljava/lang/String;Ljava/time/Instant;)Lokhttp3/Headers$Builder;\n\tpublic final fun add (Ljava/lang/String;Ljava/util/Date;)Lokhttp3/Headers$Builder;\n\tpublic final fun addAll (Lokhttp3/Headers;)Lokhttp3/Headers$Builder;\n\tpublic final fun addUnsafeNonAscii (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun build ()Lokhttp3/Headers;\n\tpublic final fun get (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun removeAll (Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun set (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun set (Ljava/lang/String;Ljava/time/Instant;)Lokhttp3/Headers$Builder;\n\tpublic final fun set (Ljava/lang/String;Ljava/util/Date;)Lokhttp3/Headers$Builder;\n}\n\npublic final class okhttp3/Headers$Companion {\n\tpublic final fun -deprecated_of (Ljava/util/Map;)Lokhttp3/Headers;\n\tpublic final fun -deprecated_of ([Ljava/lang/String;)Lokhttp3/Headers;\n\tpublic final fun of (Ljava/util/Map;)Lokhttp3/Headers;\n\tpublic final fun of ([Ljava/lang/String;)Lokhttp3/Headers;\n}\n\npublic final class okhttp3/HttpUrl {\n\tpublic static final field Companion Lokhttp3/HttpUrl$Companion;\n\tpublic final fun -deprecated_encodedFragment ()Ljava/lang/String;\n\tpublic final fun -deprecated_encodedPassword ()Ljava/lang/String;\n\tpublic final fun -deprecated_encodedPath ()Ljava/lang/String;\n\tpublic final fun -deprecated_encodedPathSegments ()Ljava/util/List;\n\tpublic final fun -deprecated_encodedQuery ()Ljava/lang/String;\n\tpublic final fun -deprecated_encodedUsername ()Ljava/lang/String;\n\tpublic final fun -deprecated_fragment ()Ljava/lang/String;\n\tpublic final fun -deprecated_host ()Ljava/lang/String;\n\tpublic final fun -deprecated_password ()Ljava/lang/String;\n\tpublic final fun -deprecated_pathSegments ()Ljava/util/List;\n\tpublic final fun -deprecated_pathSize ()I\n\tpublic final fun -deprecated_port ()I\n\tpublic final fun -deprecated_query ()Ljava/lang/String;\n\tpublic final fun -deprecated_queryParameterNames ()Ljava/util/Set;\n\tpublic final fun -deprecated_querySize ()I\n\tpublic final fun -deprecated_scheme ()Ljava/lang/String;\n\tpublic final fun -deprecated_uri ()Ljava/net/URI;\n\tpublic final fun -deprecated_url ()Ljava/net/URL;\n\tpublic final fun -deprecated_username ()Ljava/lang/String;\n\tpublic synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic static final fun defaultPort (Ljava/lang/String;)I\n\tpublic final fun encodedFragment ()Ljava/lang/String;\n\tpublic final fun encodedPassword ()Ljava/lang/String;\n\tpublic final fun encodedPath ()Ljava/lang/String;\n\tpublic final fun encodedPathSegments ()Ljava/util/List;\n\tpublic final fun encodedQuery ()Ljava/lang/String;\n\tpublic final fun encodedUsername ()Ljava/lang/String;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun fragment ()Ljava/lang/String;\n\tpublic static final fun get (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic static final fun get (Ljava/net/URI;)Lokhttp3/HttpUrl;\n\tpublic static final fun get (Ljava/net/URL;)Lokhttp3/HttpUrl;\n\tpublic fun hashCode ()I\n\tpublic final fun host ()Ljava/lang/String;\n\tpublic final fun isHttps ()Z\n\tpublic final fun newBuilder ()Lokhttp3/HttpUrl$Builder;\n\tpublic final fun newBuilder (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic static final fun parse (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun password ()Ljava/lang/String;\n\tpublic final fun pathSegments ()Ljava/util/List;\n\tpublic final fun pathSize ()I\n\tpublic final fun port ()I\n\tpublic final fun query ()Ljava/lang/String;\n\tpublic final fun queryParameter (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun queryParameterName (I)Ljava/lang/String;\n\tpublic final fun queryParameterNames ()Ljava/util/Set;\n\tpublic final fun queryParameterValue (I)Ljava/lang/String;\n\tpublic final fun queryParameterValues (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun querySize ()I\n\tpublic final fun redact ()Ljava/lang/String;\n\tpublic final fun resolve (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun scheme ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun topPrivateDomain ()Ljava/lang/String;\n\tpublic final fun uri ()Ljava/net/URI;\n\tpublic final fun url ()Ljava/net/URL;\n\tpublic final fun username ()Ljava/lang/String;\n}\n\npublic final class okhttp3/HttpUrl$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun addEncodedPathSegment (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addEncodedPathSegments (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addEncodedQueryParameter (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addPathSegment (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addPathSegments (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addQueryParameter (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun build ()Lokhttp3/HttpUrl;\n\tpublic final fun encodedFragment (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun encodedPassword (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun encodedPath (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun encodedQuery (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun encodedUsername (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun fragment (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun host (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun password (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun port (I)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun query (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun removeAllEncodedQueryParameters (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun removeAllQueryParameters (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun removePathSegment (I)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun scheme (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun setEncodedPathSegment (ILjava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun setEncodedQueryParameter (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun setPathSegment (ILjava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun setQueryParameter (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun username (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n}\n\npublic final class okhttp3/HttpUrl$Companion {\n\tpublic final fun -deprecated_get (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun -deprecated_get (Ljava/net/URI;)Lokhttp3/HttpUrl;\n\tpublic final fun -deprecated_get (Ljava/net/URL;)Lokhttp3/HttpUrl;\n\tpublic final fun -deprecated_parse (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun defaultPort (Ljava/lang/String;)I\n\tpublic final fun get (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun get (Ljava/net/URI;)Lokhttp3/HttpUrl;\n\tpublic final fun get (Ljava/net/URL;)Lokhttp3/HttpUrl;\n\tpublic final fun parse (Ljava/lang/String;)Lokhttp3/HttpUrl;\n}\n\npublic abstract interface class okhttp3/Interceptor {\n\tpublic static final field Companion Lokhttp3/Interceptor$Companion;\n\tpublic abstract fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\n}\n\npublic abstract interface class okhttp3/Interceptor$Chain {\n\tpublic abstract fun call ()Lokhttp3/Call;\n\tpublic abstract fun connectTimeoutMillis ()I\n\tpublic abstract fun connection ()Lokhttp3/Connection;\n\tpublic abstract fun getAuthenticator ()Lokhttp3/Authenticator;\n\tpublic abstract fun getCache ()Lokhttp3/Cache;\n\tpublic abstract fun getCertificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic abstract fun getConnectionPool ()Lokhttp3/ConnectionPool;\n\tpublic abstract fun getCookieJar ()Lokhttp3/CookieJar;\n\tpublic abstract fun getDns ()Lokhttp3/Dns;\n\tpublic abstract fun getEventListener ()Lokhttp3/EventListener;\n\tpublic abstract fun getFollowRedirects ()Z\n\tpublic abstract fun getFollowSslRedirects ()Z\n\tpublic abstract fun getHostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic abstract fun getProxy ()Ljava/net/Proxy;\n\tpublic abstract fun getProxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic abstract fun getProxySelector ()Ljava/net/ProxySelector;\n\tpublic abstract fun getRetryOnConnectionFailure ()Z\n\tpublic abstract fun getSocketFactory ()Ljavax/net/SocketFactory;\n\tpublic abstract fun getSslSocketFactoryOrNull ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic abstract fun getX509TrustManagerOrNull ()Ljavax/net/ssl/X509TrustManager;\n\tpublic abstract fun proceed (Lokhttp3/Request;)Lokhttp3/Response;\n\tpublic abstract fun readTimeoutMillis ()I\n\tpublic abstract fun request ()Lokhttp3/Request;\n\tpublic abstract fun withAuthenticator (Lokhttp3/Authenticator;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withCache (Lokhttp3/Cache;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withCertificatePinner (Lokhttp3/CertificatePinner;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withConnectTimeout (ILjava/util/concurrent/TimeUnit;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withConnectionPool (Lokhttp3/ConnectionPool;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withCookieJar (Lokhttp3/CookieJar;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withDns (Lokhttp3/Dns;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withHostnameVerifier (Ljavax/net/ssl/HostnameVerifier;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withProxy (Ljava/net/Proxy;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withProxyAuthenticator (Lokhttp3/Authenticator;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withProxySelector (Ljava/net/ProxySelector;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withReadTimeout (ILjava/util/concurrent/TimeUnit;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withRetryOnConnectionFailure (Z)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withSocketFactory (Ljavax/net/SocketFactory;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withSslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withWriteTimeout (ILjava/util/concurrent/TimeUnit;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun writeTimeoutMillis ()I\n}\n\npublic final class okhttp3/Interceptor$Companion {\n\tpublic final fun invoke (Lkotlin/jvm/functions/Function1;)Lokhttp3/Interceptor;\n}\n\npublic final class okhttp3/MediaType {\n\tpublic static final field Companion Lokhttp3/MediaType$Companion;\n\tpublic final fun -deprecated_subtype ()Ljava/lang/String;\n\tpublic final fun -deprecated_type ()Ljava/lang/String;\n\tpublic final fun charset ()Ljava/nio/charset/Charset;\n\tpublic final fun charset (Ljava/nio/charset/Charset;)Ljava/nio/charset/Charset;\n\tpublic static synthetic fun charset$default (Lokhttp3/MediaType;Ljava/nio/charset/Charset;ILjava/lang/Object;)Ljava/nio/charset/Charset;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic static final fun get (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic fun hashCode ()I\n\tpublic final fun parameter (Ljava/lang/String;)Ljava/lang/String;\n\tpublic static final fun parse (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic final fun subtype ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun type ()Ljava/lang/String;\n}\n\npublic final class okhttp3/MediaType$Companion {\n\tpublic final fun -deprecated_get (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic final fun -deprecated_parse (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic final fun get (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic final fun parse (Ljava/lang/String;)Lokhttp3/MediaType;\n}\n\npublic final class okhttp3/MultipartBody : okhttp3/RequestBody {\n\tpublic static final field ALTERNATIVE Lokhttp3/MediaType;\n\tpublic static final field Companion Lokhttp3/MultipartBody$Companion;\n\tpublic static final field DIGEST Lokhttp3/MediaType;\n\tpublic static final field FORM Lokhttp3/MediaType;\n\tpublic static final field MIXED Lokhttp3/MediaType;\n\tpublic static final field PARALLEL Lokhttp3/MediaType;\n\tpublic final fun -deprecated_boundary ()Ljava/lang/String;\n\tpublic final fun -deprecated_parts ()Ljava/util/List;\n\tpublic final fun -deprecated_size ()I\n\tpublic final fun -deprecated_type ()Lokhttp3/MediaType;\n\tpublic final fun boundary ()Ljava/lang/String;\n\tpublic fun contentLength ()J\n\tpublic fun contentType ()Lokhttp3/MediaType;\n\tpublic fun isOneShot ()Z\n\tpublic final fun part (I)Lokhttp3/MultipartBody$Part;\n\tpublic final fun parts ()Ljava/util/List;\n\tpublic final fun size ()I\n\tpublic final fun type ()Lokhttp3/MediaType;\n\tpublic fun writeTo (Lokio/BufferedSink;)V\n}\n\npublic final class okhttp3/MultipartBody$Builder {\n\tpublic fun <init> ()V\n\tpublic fun <init> (Ljava/lang/String;)V\n\tpublic synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun addFormDataPart (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun addFormDataPart (Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun addPart (Lokhttp3/Headers;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun addPart (Lokhttp3/MultipartBody$Part;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun addPart (Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun build ()Lokhttp3/MultipartBody;\n\tpublic final fun setType (Lokhttp3/MediaType;)Lokhttp3/MultipartBody$Builder;\n}\n\npublic final class okhttp3/MultipartBody$Companion {\n}\n\npublic final class okhttp3/MultipartBody$Part {\n\tpublic static final field Companion Lokhttp3/MultipartBody$Part$Companion;\n\tpublic final fun -deprecated_body ()Lokhttp3/RequestBody;\n\tpublic final fun -deprecated_headers ()Lokhttp3/Headers;\n\tpublic synthetic fun <init> (Lokhttp3/Headers;Lokhttp3/RequestBody;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun body ()Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/Headers;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic static final fun create (Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic static final fun createFormData (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/MultipartBody$Part;\n\tpublic static final fun createFormData (Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic final fun headers ()Lokhttp3/Headers;\n}\n\npublic final class okhttp3/MultipartBody$Part$Companion {\n\tpublic final fun create (Lokhttp3/Headers;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic final fun create (Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic final fun createFormData (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/MultipartBody$Part;\n\tpublic final fun createFormData (Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n}\n\npublic final class okhttp3/MultipartReader : java/io/Closeable {\n\tpublic fun <init> (Lokhttp3/ResponseBody;)V\n\tpublic fun <init> (Lokio/BufferedSource;Ljava/lang/String;)V\n\tpublic final fun boundary ()Ljava/lang/String;\n\tpublic fun close ()V\n\tpublic final fun nextPart ()Lokhttp3/MultipartReader$Part;\n}\n\npublic final class okhttp3/MultipartReader$Part : java/io/Closeable {\n\tpublic fun <init> (Lokhttp3/Headers;Lokio/BufferedSource;)V\n\tpublic final fun body ()Lokio/BufferedSource;\n\tpublic fun close ()V\n\tpublic final fun headers ()Lokhttp3/Headers;\n}\n\npublic final class okhttp3/OkHttp {\n\tpublic static final field INSTANCE Lokhttp3/OkHttp;\n\tpublic static final field VERSION Ljava/lang/String;\n\tpublic final fun initialize (Landroid/content/Context;)V\n}\n\npublic class okhttp3/OkHttpClient : okhttp3/Call$Factory, okhttp3/WebSocket$Factory {\n\tpublic static final field Companion Lokhttp3/OkHttpClient$Companion;\n\tpublic final fun -deprecated_authenticator ()Lokhttp3/Authenticator;\n\tpublic final fun -deprecated_cache ()Lokhttp3/Cache;\n\tpublic final fun -deprecated_callTimeoutMillis ()I\n\tpublic final fun -deprecated_certificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic final fun -deprecated_connectTimeoutMillis ()I\n\tpublic final fun -deprecated_connectionPool ()Lokhttp3/ConnectionPool;\n\tpublic final fun -deprecated_connectionSpecs ()Ljava/util/List;\n\tpublic final fun -deprecated_cookieJar ()Lokhttp3/CookieJar;\n\tpublic final fun -deprecated_dispatcher ()Lokhttp3/Dispatcher;\n\tpublic final fun -deprecated_dns ()Lokhttp3/Dns;\n\tpublic final fun -deprecated_eventListenerFactory ()Lokhttp3/EventListener$Factory;\n\tpublic final fun -deprecated_followRedirects ()Z\n\tpublic final fun -deprecated_followSslRedirects ()Z\n\tpublic final fun -deprecated_hostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic final fun -deprecated_interceptors ()Ljava/util/List;\n\tpublic final fun -deprecated_networkInterceptors ()Ljava/util/List;\n\tpublic final fun -deprecated_pingIntervalMillis ()I\n\tpublic final fun -deprecated_protocols ()Ljava/util/List;\n\tpublic final fun -deprecated_proxy ()Ljava/net/Proxy;\n\tpublic final fun -deprecated_proxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic final fun -deprecated_proxySelector ()Ljava/net/ProxySelector;\n\tpublic final fun -deprecated_readTimeoutMillis ()I\n\tpublic final fun -deprecated_retryOnConnectionFailure ()Z\n\tpublic final fun -deprecated_socketFactory ()Ljavax/net/SocketFactory;\n\tpublic final fun -deprecated_sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic final fun -deprecated_writeTimeoutMillis ()I\n\tpublic fun <init> ()V\n\tpublic final fun address (Lokhttp3/HttpUrl;)Lokhttp3/Address;\n\tpublic final fun authenticator ()Lokhttp3/Authenticator;\n\tpublic final fun cache ()Lokhttp3/Cache;\n\tpublic final fun callTimeoutMillis ()I\n\tpublic final fun certificateChainCleaner ()Lokhttp3/internal/tls/CertificateChainCleaner;\n\tpublic final fun certificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic final fun connectTimeoutMillis ()I\n\tpublic final fun connectionPool ()Lokhttp3/ConnectionPool;\n\tpublic final fun connectionSpecs ()Ljava/util/List;\n\tpublic final fun cookieJar ()Lokhttp3/CookieJar;\n\tpublic final fun dispatcher ()Lokhttp3/Dispatcher;\n\tpublic final fun dns ()Lokhttp3/Dns;\n\tpublic final fun eventListenerFactory ()Lokhttp3/EventListener$Factory;\n\tpublic final fun fastFallback ()Z\n\tpublic final fun followRedirects ()Z\n\tpublic final fun followSslRedirects ()Z\n\tpublic final fun hostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic final fun interceptors ()Ljava/util/List;\n\tpublic final fun minWebSocketMessageToCompress ()J\n\tpublic final fun networkInterceptors ()Ljava/util/List;\n\tpublic fun newBuilder ()Lokhttp3/OkHttpClient$Builder;\n\tpublic fun newCall (Lokhttp3/Request;)Lokhttp3/Call;\n\tpublic fun newWebSocket (Lokhttp3/Request;Lokhttp3/WebSocketListener;)Lokhttp3/WebSocket;\n\tpublic final fun pingIntervalMillis ()I\n\tpublic final fun protocols ()Ljava/util/List;\n\tpublic final fun proxy ()Ljava/net/Proxy;\n\tpublic final fun proxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic final fun proxySelector ()Ljava/net/ProxySelector;\n\tpublic final fun readTimeoutMillis ()I\n\tpublic final fun retryOnConnectionFailure ()Z\n\tpublic final fun socketFactory ()Ljavax/net/SocketFactory;\n\tpublic final fun sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic final fun webSocketCloseTimeout ()I\n\tpublic final fun writeTimeoutMillis ()I\n\tpublic final fun x509TrustManager ()Ljavax/net/ssl/X509TrustManager;\n}\n\npublic final class okhttp3/OkHttpClient$Builder {\n\tpublic final fun -addInterceptor (Lkotlin/jvm/functions/Function1;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun -addNetworkInterceptor (Lkotlin/jvm/functions/Function1;)Lokhttp3/OkHttpClient$Builder;\n\tpublic fun <init> ()V\n\tpublic final fun addInterceptor (Lokhttp3/Interceptor;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun addNetworkInterceptor (Lokhttp3/Interceptor;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun authenticator (Lokhttp3/Authenticator;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun build ()Lokhttp3/OkHttpClient;\n\tpublic final fun cache (Lokhttp3/Cache;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun callTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun callTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun callTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun certificatePinner (Lokhttp3/CertificatePinner;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectionPool (Lokhttp3/ConnectionPool;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectionSpecs (Ljava/util/List;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun cookieJar (Lokhttp3/CookieJar;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun dispatcher (Lokhttp3/Dispatcher;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun dns (Lokhttp3/Dns;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun eventListener (Lokhttp3/EventListener;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun eventListenerFactory (Lokhttp3/EventListener$Factory;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun fastFallback (Z)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun followRedirects (Z)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun followSslRedirects (Z)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun hostnameVerifier (Ljavax/net/ssl/HostnameVerifier;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun interceptors ()Ljava/util/List;\n\tpublic final fun minWebSocketMessageToCompress (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun networkInterceptors ()Ljava/util/List;\n\tpublic final fun pingInterval (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun pingInterval (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun pingInterval-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun protocols (Ljava/util/List;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun proxy (Ljava/net/Proxy;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun proxyAuthenticator (Lokhttp3/Authenticator;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun proxySelector (Ljava/net/ProxySelector;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun readTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun readTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun readTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun retryOnConnectionFailure (Z)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun socketFactory (Ljavax/net/SocketFactory;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun sslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun sslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun webSocketCloseTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun webSocketCloseTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun webSocketCloseTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun writeTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun writeTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun writeTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n}\n\npublic final class okhttp3/OkHttpClient$Companion {\n}\n\npublic final class okhttp3/Protocol : java/lang/Enum {\n\tpublic static final field Companion Lokhttp3/Protocol$Companion;\n\tpublic static final field H2_PRIOR_KNOWLEDGE Lokhttp3/Protocol;\n\tpublic static final field HTTP_1_0 Lokhttp3/Protocol;\n\tpublic static final field HTTP_1_1 Lokhttp3/Protocol;\n\tpublic static final field HTTP_2 Lokhttp3/Protocol;\n\tpublic static final field HTTP_3 Lokhttp3/Protocol;\n\tpublic static final field QUIC Lokhttp3/Protocol;\n\tpublic static final field SPDY_3 Lokhttp3/Protocol;\n\tpublic static final fun get (Ljava/lang/String;)Lokhttp3/Protocol;\n\tpublic static fun getEntries ()Lkotlin/enums/EnumEntries;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic static fun valueOf (Ljava/lang/String;)Lokhttp3/Protocol;\n\tpublic static fun values ()[Lokhttp3/Protocol;\n}\n\npublic final class okhttp3/Protocol$Companion {\n\tpublic final fun get (Ljava/lang/String;)Lokhttp3/Protocol;\n}\n\npublic final class okhttp3/Request {\n\tpublic final fun -deprecated_body ()Lokhttp3/RequestBody;\n\tpublic final fun -deprecated_cacheControl ()Lokhttp3/CacheControl;\n\tpublic final fun -deprecated_headers ()Lokhttp3/Headers;\n\tpublic final fun -deprecated_method ()Ljava/lang/String;\n\tpublic final fun -deprecated_url ()Lokhttp3/HttpUrl;\n\tpublic fun <init> (Lokhttp3/HttpUrl;Lokhttp3/Headers;Ljava/lang/String;Lokhttp3/RequestBody;)V\n\tpublic synthetic fun <init> (Lokhttp3/HttpUrl;Lokhttp3/Headers;Ljava/lang/String;Lokhttp3/RequestBody;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun body ()Lokhttp3/RequestBody;\n\tpublic final fun cacheControl ()Lokhttp3/CacheControl;\n\tpublic final fun cacheUrlOverride ()Lokhttp3/HttpUrl;\n\tpublic final fun header (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun headers ()Lokhttp3/Headers;\n\tpublic final fun headers (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun isHttps ()Z\n\tpublic final fun method ()Ljava/lang/String;\n\tpublic final fun newBuilder ()Lokhttp3/Request$Builder;\n\tpublic final fun tag ()Ljava/lang/Object;\n\tpublic final fun tag (Ljava/lang/Class;)Ljava/lang/Object;\n\tpublic final fun tag (Lkotlin/reflect/KClass;)Ljava/lang/Object;\n\tpublic final fun toCurl ()Ljava/lang/String;\n\tpublic final fun toCurl (Z)Ljava/lang/String;\n\tpublic static synthetic fun toCurl$default (Lokhttp3/Request;ZILjava/lang/Object;)Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun url ()Lokhttp3/HttpUrl;\n}\n\npublic class okhttp3/Request$Builder {\n\tpublic fun <init> ()V\n\tpublic fun addHeader (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder;\n\tpublic fun build ()Lokhttp3/Request;\n\tpublic fun cacheControl (Lokhttp3/CacheControl;)Lokhttp3/Request$Builder;\n\tpublic final fun cacheUrlOverride (Lokhttp3/HttpUrl;)Lokhttp3/Request$Builder;\n\tpublic final fun delete ()Lokhttp3/Request$Builder;\n\tpublic fun delete (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic static synthetic fun delete$default (Lokhttp3/Request$Builder;Lokhttp3/RequestBody;ILjava/lang/Object;)Lokhttp3/Request$Builder;\n\tpublic fun get ()Lokhttp3/Request$Builder;\n\tpublic final fun gzip ()Lokhttp3/Request$Builder;\n\tpublic fun head ()Lokhttp3/Request$Builder;\n\tpublic fun header (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder;\n\tpublic fun headers (Lokhttp3/Headers;)Lokhttp3/Request$Builder;\n\tpublic fun method (Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun patch (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun post (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun put (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun query (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun removeHeader (Ljava/lang/String;)Lokhttp3/Request$Builder;\n\tpublic fun tag (Ljava/lang/Class;Ljava/lang/Object;)Lokhttp3/Request$Builder;\n\tpublic fun tag (Ljava/lang/Object;)Lokhttp3/Request$Builder;\n\tpublic final fun tag (Lkotlin/reflect/KClass;Ljava/lang/Object;)Lokhttp3/Request$Builder;\n\tpublic fun url (Ljava/lang/String;)Lokhttp3/Request$Builder;\n\tpublic fun url (Ljava/net/URL;)Lokhttp3/Request$Builder;\n\tpublic fun url (Lokhttp3/HttpUrl;)Lokhttp3/Request$Builder;\n}\n\npublic abstract class okhttp3/RequestBody {\n\tpublic static final field Companion Lokhttp3/RequestBody$Companion;\n\tpublic static final field EMPTY Lokhttp3/RequestBody;\n\tpublic fun <init> ()V\n\tpublic fun contentLength ()J\n\tpublic abstract fun contentType ()Lokhttp3/MediaType;\n\tpublic static final fun create (Ljava/io/File;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Ljava/io/FileDescriptor;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Ljava/io/File;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Lokio/ByteString;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;[B)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;[BI)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;[BII)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokio/ByteString;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokio/Path;Lokio/FileSystem;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create ([B)Lokhttp3/RequestBody;\n\tpublic static final fun create ([BLokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create ([BLokhttp3/MediaType;I)Lokhttp3/RequestBody;\n\tpublic static final fun create ([BLokhttp3/MediaType;II)Lokhttp3/RequestBody;\n\tpublic fun isDuplex ()Z\n\tpublic fun isOneShot ()Z\n\tpublic final fun sha256 ()Lokio/ByteString;\n\tpublic abstract fun writeTo (Lokio/BufferedSink;)V\n}\n\npublic final class okhttp3/RequestBody$Companion {\n\tpublic final fun create (Ljava/io/File;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create (Ljava/io/FileDescriptor;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create (Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;Ljava/io/File;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;Lokio/ByteString;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;[B)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;[BI)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;[BII)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokio/ByteString;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokio/Path;Lokio/FileSystem;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create ([B)Lokhttp3/RequestBody;\n\tpublic final fun create ([BLokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create ([BLokhttp3/MediaType;I)Lokhttp3/RequestBody;\n\tpublic final fun create ([BLokhttp3/MediaType;II)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Ljava/io/File;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Ljava/io/FileDescriptor;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Ljava/lang/String;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Lokhttp3/MediaType;[BIIILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Lokio/ByteString;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Lokio/Path;Lokio/FileSystem;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;[BLokhttp3/MediaType;IIILjava/lang/Object;)Lokhttp3/RequestBody;\n}\n\npublic final class okhttp3/Response : java/io/Closeable {\n\tpublic final fun -deprecated_body ()Lokhttp3/ResponseBody;\n\tpublic final fun -deprecated_cacheControl ()Lokhttp3/CacheControl;\n\tpublic final fun -deprecated_cacheResponse ()Lokhttp3/Response;\n\tpublic final fun -deprecated_code ()I\n\tpublic final fun -deprecated_handshake ()Lokhttp3/Handshake;\n\tpublic final fun -deprecated_headers ()Lokhttp3/Headers;\n\tpublic final fun -deprecated_message ()Ljava/lang/String;\n\tpublic final fun -deprecated_networkResponse ()Lokhttp3/Response;\n\tpublic final fun -deprecated_priorResponse ()Lokhttp3/Response;\n\tpublic final fun -deprecated_protocol ()Lokhttp3/Protocol;\n\tpublic final fun -deprecated_receivedResponseAtMillis ()J\n\tpublic final fun -deprecated_request ()Lokhttp3/Request;\n\tpublic final fun -deprecated_sentRequestAtMillis ()J\n\tpublic final fun body ()Lokhttp3/ResponseBody;\n\tpublic final fun cacheControl ()Lokhttp3/CacheControl;\n\tpublic final fun cacheResponse ()Lokhttp3/Response;\n\tpublic final fun challenges ()Ljava/util/List;\n\tpublic fun close ()V\n\tpublic final fun code ()I\n\tpublic final fun handshake ()Lokhttp3/Handshake;\n\tpublic final fun header (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun header (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n\tpublic static synthetic fun header$default (Lokhttp3/Response;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String;\n\tpublic final fun headers ()Lokhttp3/Headers;\n\tpublic final fun headers (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun isRedirect ()Z\n\tpublic final fun isSuccessful ()Z\n\tpublic final fun message ()Ljava/lang/String;\n\tpublic final fun networkResponse ()Lokhttp3/Response;\n\tpublic final fun newBuilder ()Lokhttp3/Response$Builder;\n\tpublic final fun peekBody (J)Lokhttp3/ResponseBody;\n\tpublic final fun peekTrailers ()Lokhttp3/Headers;\n\tpublic final fun priorResponse ()Lokhttp3/Response;\n\tpublic final fun protocol ()Lokhttp3/Protocol;\n\tpublic final fun receivedResponseAtMillis ()J\n\tpublic final fun request ()Lokhttp3/Request;\n\tpublic final fun sentRequestAtMillis ()J\n\tpublic final fun socket ()Lokio/Socket;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun trailers ()Lokhttp3/Headers;\n}\n\npublic class okhttp3/Response$Builder {\n\tpublic fun <init> ()V\n\tpublic fun addHeader (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Response$Builder;\n\tpublic fun body (Lokhttp3/ResponseBody;)Lokhttp3/Response$Builder;\n\tpublic fun build ()Lokhttp3/Response;\n\tpublic fun cacheResponse (Lokhttp3/Response;)Lokhttp3/Response$Builder;\n\tpublic fun code (I)Lokhttp3/Response$Builder;\n\tpublic fun handshake (Lokhttp3/Handshake;)Lokhttp3/Response$Builder;\n\tpublic fun header (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Response$Builder;\n\tpublic fun headers (Lokhttp3/Headers;)Lokhttp3/Response$Builder;\n\tpublic fun message (Ljava/lang/String;)Lokhttp3/Response$Builder;\n\tpublic fun networkResponse (Lokhttp3/Response;)Lokhttp3/Response$Builder;\n\tpublic fun priorResponse (Lokhttp3/Response;)Lokhttp3/Response$Builder;\n\tpublic fun protocol (Lokhttp3/Protocol;)Lokhttp3/Response$Builder;\n\tpublic fun receivedResponseAtMillis (J)Lokhttp3/Response$Builder;\n\tpublic fun removeHeader (Ljava/lang/String;)Lokhttp3/Response$Builder;\n\tpublic fun request (Lokhttp3/Request;)Lokhttp3/Response$Builder;\n\tpublic fun sentRequestAtMillis (J)Lokhttp3/Response$Builder;\n\tpublic fun socket (Lokio/Socket;)Lokhttp3/Response$Builder;\n\tpublic fun trailers (Lokhttp3/TrailersSource;)Lokhttp3/Response$Builder;\n}\n\npublic abstract class okhttp3/ResponseBody : java/io/Closeable {\n\tpublic static final field Companion Lokhttp3/ResponseBody$Companion;\n\tpublic static final field EMPTY Lokhttp3/ResponseBody;\n\tpublic fun <init> ()V\n\tpublic final fun byteStream ()Ljava/io/InputStream;\n\tpublic final fun byteString ()Lokio/ByteString;\n\tpublic final fun bytes ()[B\n\tpublic final fun charStream ()Ljava/io/Reader;\n\tpublic fun close ()V\n\tpublic abstract fun contentLength ()J\n\tpublic abstract fun contentType ()Lokhttp3/MediaType;\n\tpublic static final fun create (Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokhttp3/MediaType;JLokio/BufferedSource;)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Lokio/ByteString;)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokhttp3/MediaType;[B)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokio/BufferedSource;Lokhttp3/MediaType;J)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokio/ByteString;Lokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic static final fun create ([BLokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic abstract fun source ()Lokio/BufferedSource;\n\tpublic final fun string ()Ljava/lang/String;\n}\n\npublic final class okhttp3/ResponseBody$Companion {\n\tpublic final fun create (Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokhttp3/MediaType;JLokio/BufferedSource;)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokhttp3/MediaType;Lokio/ByteString;)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokhttp3/MediaType;[B)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokio/BufferedSource;Lokhttp3/MediaType;J)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokio/ByteString;Lokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic final fun create ([BLokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic static synthetic fun create$default (Lokhttp3/ResponseBody$Companion;Ljava/lang/String;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/ResponseBody;\n\tpublic static synthetic fun create$default (Lokhttp3/ResponseBody$Companion;Lokio/BufferedSource;Lokhttp3/MediaType;JILjava/lang/Object;)Lokhttp3/ResponseBody;\n\tpublic static synthetic fun create$default (Lokhttp3/ResponseBody$Companion;Lokio/ByteString;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/ResponseBody;\n\tpublic static synthetic fun create$default (Lokhttp3/ResponseBody$Companion;[BLokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/ResponseBody;\n}\n\npublic final class okhttp3/Route {\n\tpublic final fun -deprecated_address ()Lokhttp3/Address;\n\tpublic final fun -deprecated_proxy ()Ljava/net/Proxy;\n\tpublic final fun -deprecated_socketAddress ()Ljava/net/InetSocketAddress;\n\tpublic fun <init> (Lokhttp3/Address;Ljava/net/Proxy;Ljava/net/InetSocketAddress;)V\n\tpublic final fun address ()Lokhttp3/Address;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic fun hashCode ()I\n\tpublic final fun proxy ()Ljava/net/Proxy;\n\tpublic final fun requiresTunnel ()Z\n\tpublic final fun socketAddress ()Ljava/net/InetSocketAddress;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/TlsVersion : java/lang/Enum {\n\tpublic static final field Companion Lokhttp3/TlsVersion$Companion;\n\tpublic static final field SSL_3_0 Lokhttp3/TlsVersion;\n\tpublic static final field TLS_1_0 Lokhttp3/TlsVersion;\n\tpublic static final field TLS_1_1 Lokhttp3/TlsVersion;\n\tpublic static final field TLS_1_2 Lokhttp3/TlsVersion;\n\tpublic static final field TLS_1_3 Lokhttp3/TlsVersion;\n\tpublic final fun -deprecated_javaName ()Ljava/lang/String;\n\tpublic static final fun forJavaName (Ljava/lang/String;)Lokhttp3/TlsVersion;\n\tpublic static fun getEntries ()Lkotlin/enums/EnumEntries;\n\tpublic final fun javaName ()Ljava/lang/String;\n\tpublic static fun valueOf (Ljava/lang/String;)Lokhttp3/TlsVersion;\n\tpublic static fun values ()[Lokhttp3/TlsVersion;\n}\n\npublic final class okhttp3/TlsVersion$Companion {\n\tpublic final fun forJavaName (Ljava/lang/String;)Lokhttp3/TlsVersion;\n}\n\npublic abstract interface class okhttp3/TrailersSource {\n\tpublic static final field Companion Lokhttp3/TrailersSource$Companion;\n\tpublic static final field EMPTY Lokhttp3/TrailersSource;\n\tpublic abstract fun get ()Lokhttp3/Headers;\n\tpublic fun peek ()Lokhttp3/Headers;\n}\n\npublic final class okhttp3/TrailersSource$Companion {\n}\n\npublic abstract interface class okhttp3/WebSocket {\n\tpublic abstract fun cancel ()V\n\tpublic abstract fun close (ILjava/lang/String;)Z\n\tpublic abstract fun queueSize ()J\n\tpublic abstract fun request ()Lokhttp3/Request;\n\tpublic abstract fun send (Ljava/lang/String;)Z\n\tpublic abstract fun send (Lokio/ByteString;)Z\n}\n\npublic abstract interface class okhttp3/WebSocket$Factory {\n\tpublic abstract fun newWebSocket (Lokhttp3/Request;Lokhttp3/WebSocketListener;)Lokhttp3/WebSocket;\n}\n\npublic abstract class okhttp3/WebSocketListener {\n\tpublic fun <init> ()V\n\tpublic fun onClosed (Lokhttp3/WebSocket;ILjava/lang/String;)V\n\tpublic fun onClosing (Lokhttp3/WebSocket;ILjava/lang/String;)V\n\tpublic fun onFailure (Lokhttp3/WebSocket;Ljava/lang/Throwable;Lokhttp3/Response;)V\n\tpublic fun onMessage (Lokhttp3/WebSocket;Ljava/lang/String;)V\n\tpublic fun onMessage (Lokhttp3/WebSocket;Lokio/ByteString;)V\n\tpublic fun onOpen (Lokhttp3/WebSocket;Lokhttp3/Response;)V\n}\n\n"
  },
  {
    "path": "okhttp/api/jvm/okhttp.api",
    "content": "public final class okhttp3/Address {\n\tpublic final fun -deprecated_certificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic final fun -deprecated_connectionSpecs ()Ljava/util/List;\n\tpublic final fun -deprecated_dns ()Lokhttp3/Dns;\n\tpublic final fun -deprecated_hostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic final fun -deprecated_protocols ()Ljava/util/List;\n\tpublic final fun -deprecated_proxy ()Ljava/net/Proxy;\n\tpublic final fun -deprecated_proxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic final fun -deprecated_proxySelector ()Ljava/net/ProxySelector;\n\tpublic final fun -deprecated_socketFactory ()Ljavax/net/SocketFactory;\n\tpublic final fun -deprecated_sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic final fun -deprecated_url ()Lokhttp3/HttpUrl;\n\tpublic fun <init> (Ljava/lang/String;ILokhttp3/Dns;Ljavax/net/SocketFactory;Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/HostnameVerifier;Lokhttp3/CertificatePinner;Lokhttp3/Authenticator;Ljava/net/Proxy;Ljava/util/List;Ljava/util/List;Ljava/net/ProxySelector;)V\n\tpublic final fun certificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic final fun connectionSpecs ()Ljava/util/List;\n\tpublic final fun dns ()Lokhttp3/Dns;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic fun hashCode ()I\n\tpublic final fun hostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic final fun protocols ()Ljava/util/List;\n\tpublic final fun proxy ()Ljava/net/Proxy;\n\tpublic final fun proxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic final fun proxySelector ()Ljava/net/ProxySelector;\n\tpublic final fun socketFactory ()Ljavax/net/SocketFactory;\n\tpublic final fun sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun url ()Lokhttp3/HttpUrl;\n}\n\npublic abstract interface class okhttp3/Authenticator {\n\tpublic static final field Companion Lokhttp3/Authenticator$Companion;\n\tpublic static final field JAVA_NET_AUTHENTICATOR Lokhttp3/Authenticator;\n\tpublic static final field NONE Lokhttp3/Authenticator;\n\tpublic abstract fun authenticate (Lokhttp3/Route;Lokhttp3/Response;)Lokhttp3/Request;\n}\n\npublic final class okhttp3/Authenticator$Companion {\n}\n\npublic final class okhttp3/Cache : java/io/Closeable, java/io/Flushable {\n\tpublic static final field Companion Lokhttp3/Cache$Companion;\n\tpublic final fun -deprecated_directory ()Ljava/io/File;\n\tpublic fun <init> (Ljava/io/File;J)V\n\tpublic fun <init> (Lokio/FileSystem;Lokio/Path;J)V\n\tpublic fun close ()V\n\tpublic final fun delete ()V\n\tpublic final fun directory ()Ljava/io/File;\n\tpublic final fun directoryPath ()Lokio/Path;\n\tpublic final fun evictAll ()V\n\tpublic fun flush ()V\n\tpublic final fun hitCount ()I\n\tpublic final fun initialize ()V\n\tpublic final fun isClosed ()Z\n\tpublic static final fun key (Lokhttp3/HttpUrl;)Ljava/lang/String;\n\tpublic final fun maxSize ()J\n\tpublic final fun networkCount ()I\n\tpublic final fun requestCount ()I\n\tpublic final fun size ()J\n\tpublic final fun urls ()Ljava/util/Iterator;\n\tpublic final fun writeAbortCount ()I\n\tpublic final fun writeSuccessCount ()I\n}\n\npublic final class okhttp3/Cache$Companion {\n\tpublic final fun hasVaryAll (Lokhttp3/Response;)Z\n\tpublic final fun key (Lokhttp3/HttpUrl;)Ljava/lang/String;\n\tpublic final fun varyHeaders (Lokhttp3/Response;)Lokhttp3/Headers;\n\tpublic final fun varyMatches (Lokhttp3/Response;Lokhttp3/Headers;Lokhttp3/Request;)Z\n}\n\npublic final class okhttp3/CacheControl {\n\tpublic static final field Companion Lokhttp3/CacheControl$Companion;\n\tpublic static final field FORCE_CACHE Lokhttp3/CacheControl;\n\tpublic static final field FORCE_NETWORK Lokhttp3/CacheControl;\n\tpublic final fun -deprecated_immutable ()Z\n\tpublic final fun -deprecated_maxAgeSeconds ()I\n\tpublic final fun -deprecated_maxStaleSeconds ()I\n\tpublic final fun -deprecated_minFreshSeconds ()I\n\tpublic final fun -deprecated_mustRevalidate ()Z\n\tpublic final fun -deprecated_noCache ()Z\n\tpublic final fun -deprecated_noStore ()Z\n\tpublic final fun -deprecated_noTransform ()Z\n\tpublic final fun -deprecated_onlyIfCached ()Z\n\tpublic final fun -deprecated_sMaxAgeSeconds ()I\n\tpublic final fun immutable ()Z\n\tpublic final fun isPrivate ()Z\n\tpublic final fun isPublic ()Z\n\tpublic final fun maxAgeSeconds ()I\n\tpublic final fun maxStaleSeconds ()I\n\tpublic final fun minFreshSeconds ()I\n\tpublic final fun mustRevalidate ()Z\n\tpublic final fun noCache ()Z\n\tpublic final fun noStore ()Z\n\tpublic final fun noTransform ()Z\n\tpublic final fun onlyIfCached ()Z\n\tpublic static final fun parse (Lokhttp3/Headers;)Lokhttp3/CacheControl;\n\tpublic final fun sMaxAgeSeconds ()I\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/CacheControl$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun build ()Lokhttp3/CacheControl;\n\tpublic final fun immutable ()Lokhttp3/CacheControl$Builder;\n\tpublic final fun maxAge (ILjava/util/concurrent/TimeUnit;)Lokhttp3/CacheControl$Builder;\n\tpublic final fun maxAge-LRDsOJo (J)Lokhttp3/CacheControl$Builder;\n\tpublic final fun maxStale (ILjava/util/concurrent/TimeUnit;)Lokhttp3/CacheControl$Builder;\n\tpublic final fun maxStale-LRDsOJo (J)Lokhttp3/CacheControl$Builder;\n\tpublic final fun minFresh (ILjava/util/concurrent/TimeUnit;)Lokhttp3/CacheControl$Builder;\n\tpublic final fun minFresh-LRDsOJo (J)Lokhttp3/CacheControl$Builder;\n\tpublic final fun noCache ()Lokhttp3/CacheControl$Builder;\n\tpublic final fun noStore ()Lokhttp3/CacheControl$Builder;\n\tpublic final fun noTransform ()Lokhttp3/CacheControl$Builder;\n\tpublic final fun onlyIfCached ()Lokhttp3/CacheControl$Builder;\n}\n\npublic final class okhttp3/CacheControl$Companion {\n\tpublic final fun parse (Lokhttp3/Headers;)Lokhttp3/CacheControl;\n}\n\npublic abstract interface class okhttp3/Call : java/lang/Cloneable {\n\tpublic abstract fun addEventListener (Lokhttp3/EventListener;)V\n\tpublic abstract fun cancel ()V\n\tpublic abstract fun clone ()Lokhttp3/Call;\n\tpublic abstract fun enqueue (Lokhttp3/Callback;)V\n\tpublic abstract fun execute ()Lokhttp3/Response;\n\tpublic abstract fun isCanceled ()Z\n\tpublic abstract fun isExecuted ()Z\n\tpublic abstract fun request ()Lokhttp3/Request;\n\tpublic abstract fun tag (Ljava/lang/Class;)Ljava/lang/Object;\n\tpublic abstract fun tag (Ljava/lang/Class;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;\n\tpublic abstract fun tag (Lkotlin/reflect/KClass;)Ljava/lang/Object;\n\tpublic abstract fun tag (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;\n\tpublic abstract fun timeout ()Lokio/Timeout;\n}\n\npublic abstract interface class okhttp3/Call$Factory {\n\tpublic abstract fun newCall (Lokhttp3/Request;)Lokhttp3/Call;\n}\n\npublic abstract interface class okhttp3/Callback {\n\tpublic abstract fun onFailure (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic abstract fun onResponse (Lokhttp3/Call;Lokhttp3/Response;)V\n}\n\npublic final class okhttp3/CertificatePinner {\n\tpublic static final field Companion Lokhttp3/CertificatePinner$Companion;\n\tpublic static final field DEFAULT Lokhttp3/CertificatePinner;\n\tpublic final fun check (Ljava/lang/String;Ljava/util/List;)V\n\tpublic final fun check (Ljava/lang/String;[Ljava/security/cert/Certificate;)V\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun findMatchingPins (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun getPins ()Ljava/util/Set;\n\tpublic fun hashCode ()I\n\tpublic static final fun pin (Ljava/security/cert/Certificate;)Ljava/lang/String;\n\tpublic static final fun sha1Hash (Ljava/security/cert/X509Certificate;)Lokio/ByteString;\n\tpublic static final fun sha256Hash (Ljava/security/cert/X509Certificate;)Lokio/ByteString;\n}\n\npublic final class okhttp3/CertificatePinner$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun add (Ljava/lang/String;[Ljava/lang/String;)Lokhttp3/CertificatePinner$Builder;\n\tpublic final fun build ()Lokhttp3/CertificatePinner;\n\tpublic final fun getPins ()Ljava/util/List;\n}\n\npublic final class okhttp3/CertificatePinner$Companion {\n\tpublic final fun pin (Ljava/security/cert/Certificate;)Ljava/lang/String;\n\tpublic final fun sha1Hash (Ljava/security/cert/X509Certificate;)Lokio/ByteString;\n\tpublic final fun sha256Hash (Ljava/security/cert/X509Certificate;)Lokio/ByteString;\n}\n\npublic final class okhttp3/CertificatePinner$Pin {\n\tpublic fun <init> (Ljava/lang/String;Ljava/lang/String;)V\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun getHash ()Lokio/ByteString;\n\tpublic final fun getHashAlgorithm ()Ljava/lang/String;\n\tpublic final fun getPattern ()Ljava/lang/String;\n\tpublic fun hashCode ()I\n\tpublic final fun matchesCertificate (Ljava/security/cert/X509Certificate;)Z\n\tpublic final fun matchesHostname (Ljava/lang/String;)Z\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/Challenge {\n\tpublic final fun -deprecated_authParams ()Ljava/util/Map;\n\tpublic final fun -deprecated_charset ()Ljava/nio/charset/Charset;\n\tpublic final fun -deprecated_realm ()Ljava/lang/String;\n\tpublic final fun -deprecated_scheme ()Ljava/lang/String;\n\tpublic fun <init> (Ljava/lang/String;Ljava/lang/String;)V\n\tpublic fun <init> (Ljava/lang/String;Ljava/util/Map;)V\n\tpublic final fun authParams ()Ljava/util/Map;\n\tpublic final fun charset ()Ljava/nio/charset/Charset;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic fun hashCode ()I\n\tpublic final fun realm ()Ljava/lang/String;\n\tpublic final fun scheme ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun withCharset (Ljava/nio/charset/Charset;)Lokhttp3/Challenge;\n}\n\npublic final class okhttp3/CipherSuite {\n\tpublic static final field Companion Lokhttp3/CipherSuite$Companion;\n\tpublic static final field TLS_AES_128_CCM_8_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_AES_128_CCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_DSS_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DHE_RSA_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_256_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_DH_anon_WITH_RC4_128_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_ECDSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDHE_RSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_ECDSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_RSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_ECDH_anon_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_EMPTY_RENEGOTIATION_INFO_SCSV Lokhttp3/CipherSuite;\n\tpublic static final field TLS_FALLBACK_SCSV Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_EXPORT_WITH_RC4_40_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_EXPORT_WITH_RC4_40_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_3DES_EDE_CBC_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_DES_CBC_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_RC4_128_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_KRB5_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_PSK_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_PSK_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_PSK_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_PSK_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_EXPORT_WITH_DES40_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_EXPORT_WITH_RC4_40_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_3DES_EDE_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_128_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_128_GCM_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_256_CBC_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_AES_256_GCM_SHA384 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_CAMELLIA_128_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_CAMELLIA_256_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_DES_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_NULL_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_NULL_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_NULL_SHA256 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_RC4_128_MD5 Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_RC4_128_SHA Lokhttp3/CipherSuite;\n\tpublic static final field TLS_RSA_WITH_SEED_CBC_SHA Lokhttp3/CipherSuite;\n\tpublic final fun -deprecated_javaName ()Ljava/lang/String;\n\tpublic synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic static final fun forJavaName (Ljava/lang/String;)Lokhttp3/CipherSuite;\n\tpublic final fun javaName ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/CipherSuite$Companion {\n\tpublic final fun forJavaName (Ljava/lang/String;)Lokhttp3/CipherSuite;\n}\n\npublic class okhttp3/CompressionInterceptor : okhttp3/Interceptor {\n\tpublic fun <init> ([Lokhttp3/CompressionInterceptor$DecompressionAlgorithm;)V\n\tpublic final fun getAlgorithms ()[Lokhttp3/CompressionInterceptor$DecompressionAlgorithm;\n\tpublic fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\n}\n\npublic abstract interface class okhttp3/CompressionInterceptor$DecompressionAlgorithm {\n\tpublic abstract fun decompress (Lokio/BufferedSource;)Lokio/Source;\n\tpublic abstract fun getEncoding ()Ljava/lang/String;\n}\n\npublic abstract interface class okhttp3/Connection {\n\tpublic abstract fun handshake ()Lokhttp3/Handshake;\n\tpublic abstract fun protocol ()Lokhttp3/Protocol;\n\tpublic abstract fun route ()Lokhttp3/Route;\n\tpublic abstract fun socket ()Ljava/net/Socket;\n}\n\npublic final class okhttp3/ConnectionPool {\n\tpublic fun <init> ()V\n\tpublic fun <init> (IJLjava/util/concurrent/TimeUnit;)V\n\tpublic final fun connectionCount ()I\n\tpublic final fun evictAll ()V\n\tpublic final fun idleConnectionCount ()I\n}\n\npublic final class okhttp3/ConnectionSpec {\n\tpublic static final field CLEARTEXT Lokhttp3/ConnectionSpec;\n\tpublic static final field COMPATIBLE_TLS Lokhttp3/ConnectionSpec;\n\tpublic static final field Companion Lokhttp3/ConnectionSpec$Companion;\n\tpublic static final field MODERN_TLS Lokhttp3/ConnectionSpec;\n\tpublic static final field RESTRICTED_TLS Lokhttp3/ConnectionSpec;\n\tpublic final fun -deprecated_cipherSuites ()Ljava/util/List;\n\tpublic final fun -deprecated_supportsTlsExtensions ()Z\n\tpublic final fun -deprecated_tlsVersions ()Ljava/util/List;\n\tpublic final fun cipherSuites ()Ljava/util/List;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic fun hashCode ()I\n\tpublic final fun isCompatible (Ljavax/net/ssl/SSLSocket;)Z\n\tpublic final fun isTls ()Z\n\tpublic final fun supportsTlsExtensions ()Z\n\tpublic final fun tlsVersions ()Ljava/util/List;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/ConnectionSpec$Builder {\n\tpublic fun <init> (Lokhttp3/ConnectionSpec;)V\n\tpublic final fun allEnabledCipherSuites ()Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun allEnabledTlsVersions ()Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun build ()Lokhttp3/ConnectionSpec;\n\tpublic final fun cipherSuites ([Ljava/lang/String;)Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun cipherSuites ([Lokhttp3/CipherSuite;)Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun supportsTlsExtensions (Z)Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun tlsVersions ([Ljava/lang/String;)Lokhttp3/ConnectionSpec$Builder;\n\tpublic final fun tlsVersions ([Lokhttp3/TlsVersion;)Lokhttp3/ConnectionSpec$Builder;\n}\n\npublic final class okhttp3/ConnectionSpec$Companion {\n}\n\npublic final class okhttp3/Cookie {\n\tpublic static final field Companion Lokhttp3/Cookie$Companion;\n\tpublic final fun -deprecated_domain ()Ljava/lang/String;\n\tpublic final fun -deprecated_expiresAt ()J\n\tpublic final fun -deprecated_hostOnly ()Z\n\tpublic final fun -deprecated_httpOnly ()Z\n\tpublic final fun -deprecated_name ()Ljava/lang/String;\n\tpublic final fun -deprecated_path ()Ljava/lang/String;\n\tpublic final fun -deprecated_persistent ()Z\n\tpublic final fun -deprecated_secure ()Z\n\tpublic final fun -deprecated_value ()Ljava/lang/String;\n\tpublic synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;ZZZZLjava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun domain ()Ljava/lang/String;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun expiresAt ()J\n\tpublic fun hashCode ()I\n\tpublic final fun hostOnly ()Z\n\tpublic final fun httpOnly ()Z\n\tpublic final fun matches (Lokhttp3/HttpUrl;)Z\n\tpublic final fun name ()Ljava/lang/String;\n\tpublic final fun newBuilder ()Lokhttp3/Cookie$Builder;\n\tpublic static final fun parse (Lokhttp3/HttpUrl;Ljava/lang/String;)Lokhttp3/Cookie;\n\tpublic static final fun parseAll (Lokhttp3/HttpUrl;Lokhttp3/Headers;)Ljava/util/List;\n\tpublic final fun path ()Ljava/lang/String;\n\tpublic final fun persistent ()Z\n\tpublic final fun sameSite ()Ljava/lang/String;\n\tpublic final fun secure ()Z\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun value ()Ljava/lang/String;\n}\n\npublic final class okhttp3/Cookie$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun build ()Lokhttp3/Cookie;\n\tpublic final fun domain (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun expiresAt (J)Lokhttp3/Cookie$Builder;\n\tpublic final fun hostOnlyDomain (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun httpOnly ()Lokhttp3/Cookie$Builder;\n\tpublic final fun name (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun path (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun sameSite (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n\tpublic final fun secure ()Lokhttp3/Cookie$Builder;\n\tpublic final fun value (Ljava/lang/String;)Lokhttp3/Cookie$Builder;\n}\n\npublic final class okhttp3/Cookie$Companion {\n\tpublic final fun parse (Lokhttp3/HttpUrl;Ljava/lang/String;)Lokhttp3/Cookie;\n\tpublic final fun parseAll (Lokhttp3/HttpUrl;Lokhttp3/Headers;)Ljava/util/List;\n}\n\npublic abstract interface class okhttp3/CookieJar {\n\tpublic static final field Companion Lokhttp3/CookieJar$Companion;\n\tpublic static final field NO_COOKIES Lokhttp3/CookieJar;\n\tpublic abstract fun loadForRequest (Lokhttp3/HttpUrl;)Ljava/util/List;\n\tpublic abstract fun saveFromResponse (Lokhttp3/HttpUrl;Ljava/util/List;)V\n}\n\npublic final class okhttp3/CookieJar$Companion {\n}\n\npublic final class okhttp3/Credentials {\n\tpublic static final field INSTANCE Lokhttp3/Credentials;\n\tpublic static final fun basic (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n\tpublic static final fun basic (Ljava/lang/String;Ljava/lang/String;Ljava/nio/charset/Charset;)Ljava/lang/String;\n\tpublic static synthetic fun basic$default (Ljava/lang/String;Ljava/lang/String;Ljava/nio/charset/Charset;ILjava/lang/Object;)Ljava/lang/String;\n}\n\npublic final class okhttp3/Dispatcher {\n\tpublic final fun -deprecated_executorService ()Ljava/util/concurrent/ExecutorService;\n\tpublic fun <init> ()V\n\tpublic fun <init> (Ljava/util/concurrent/ExecutorService;)V\n\tpublic final fun cancelAll ()V\n\tpublic final fun executorService ()Ljava/util/concurrent/ExecutorService;\n\tpublic final fun getIdleCallback ()Ljava/lang/Runnable;\n\tpublic final fun getMaxRequests ()I\n\tpublic final fun getMaxRequestsPerHost ()I\n\tpublic final fun queuedCalls ()Ljava/util/List;\n\tpublic final fun queuedCallsCount ()I\n\tpublic final fun runningCalls ()Ljava/util/List;\n\tpublic final fun runningCallsCount ()I\n\tpublic final fun setIdleCallback (Ljava/lang/Runnable;)V\n\tpublic final fun setMaxRequests (I)V\n\tpublic final fun setMaxRequestsPerHost (I)V\n}\n\npublic abstract interface class okhttp3/Dns {\n\tpublic static final field Companion Lokhttp3/Dns$Companion;\n\tpublic static final field SYSTEM Lokhttp3/Dns;\n\tpublic abstract fun lookup (Ljava/lang/String;)Ljava/util/List;\n}\n\npublic final class okhttp3/Dns$Companion {\n}\n\npublic abstract class okhttp3/EventListener {\n\tpublic static final field Companion Lokhttp3/EventListener$Companion;\n\tpublic static final field NONE Lokhttp3/EventListener;\n\tpublic fun <init> ()V\n\tpublic fun cacheConditionalHit (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun cacheHit (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun cacheMiss (Lokhttp3/Call;)V\n\tpublic fun callEnd (Lokhttp3/Call;)V\n\tpublic fun callFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun callStart (Lokhttp3/Call;)V\n\tpublic fun canceled (Lokhttp3/Call;)V\n\tpublic fun connectEnd (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;)V\n\tpublic fun connectFailed (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;Ljava/io/IOException;)V\n\tpublic fun connectStart (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;)V\n\tpublic fun connectionAcquired (Lokhttp3/Call;Lokhttp3/Connection;)V\n\tpublic fun connectionReleased (Lokhttp3/Call;Lokhttp3/Connection;)V\n\tpublic fun dispatcherQueueEnd (Lokhttp3/Call;Lokhttp3/Dispatcher;)V\n\tpublic fun dispatcherQueueStart (Lokhttp3/Call;Lokhttp3/Dispatcher;)V\n\tpublic fun dnsEnd (Lokhttp3/Call;Ljava/lang/String;Ljava/util/List;)V\n\tpublic fun dnsStart (Lokhttp3/Call;Ljava/lang/String;)V\n\tpublic fun followUpDecision (Lokhttp3/Call;Lokhttp3/Response;Lokhttp3/Request;)V\n\tpublic final fun plus (Lokhttp3/EventListener;)Lokhttp3/EventListener;\n\tpublic fun proxySelectEnd (Lokhttp3/Call;Lokhttp3/HttpUrl;Ljava/util/List;)V\n\tpublic fun proxySelectStart (Lokhttp3/Call;Lokhttp3/HttpUrl;)V\n\tpublic fun requestBodyEnd (Lokhttp3/Call;J)V\n\tpublic fun requestBodyStart (Lokhttp3/Call;)V\n\tpublic fun requestFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun requestHeadersEnd (Lokhttp3/Call;Lokhttp3/Request;)V\n\tpublic fun requestHeadersStart (Lokhttp3/Call;)V\n\tpublic fun responseBodyEnd (Lokhttp3/Call;J)V\n\tpublic fun responseBodyStart (Lokhttp3/Call;)V\n\tpublic fun responseFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun responseHeadersEnd (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun responseHeadersStart (Lokhttp3/Call;)V\n\tpublic fun retryDecision (Lokhttp3/Call;Ljava/io/IOException;Z)V\n\tpublic fun satisfactionFailure (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun secureConnectEnd (Lokhttp3/Call;Lokhttp3/Handshake;)V\n\tpublic fun secureConnectStart (Lokhttp3/Call;)V\n}\n\npublic final class okhttp3/EventListener$Companion {\n}\n\npublic abstract interface class okhttp3/EventListener$Factory {\n\tpublic abstract fun create (Lokhttp3/Call;)Lokhttp3/EventListener;\n}\n\npublic final class okhttp3/FormBody : okhttp3/RequestBody {\n\tpublic static final field Companion Lokhttp3/FormBody$Companion;\n\tpublic final fun -deprecated_size ()I\n\tpublic fun contentLength ()J\n\tpublic fun contentType ()Lokhttp3/MediaType;\n\tpublic final fun encodedName (I)Ljava/lang/String;\n\tpublic final fun encodedValue (I)Ljava/lang/String;\n\tpublic final fun name (I)Ljava/lang/String;\n\tpublic final fun size ()I\n\tpublic final fun value (I)Ljava/lang/String;\n\tpublic fun writeTo (Lokio/BufferedSink;)V\n}\n\npublic final class okhttp3/FormBody$Builder {\n\tpublic fun <init> ()V\n\tpublic fun <init> (Ljava/nio/charset/Charset;)V\n\tpublic synthetic fun <init> (Ljava/nio/charset/Charset;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun add (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/FormBody$Builder;\n\tpublic final fun addEncoded (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/FormBody$Builder;\n\tpublic final fun build ()Lokhttp3/FormBody;\n}\n\npublic final class okhttp3/FormBody$Companion {\n}\n\npublic final class okhttp3/Gzip : okhttp3/CompressionInterceptor$DecompressionAlgorithm {\n\tpublic static final field INSTANCE Lokhttp3/Gzip;\n\tpublic fun decompress (Lokio/BufferedSource;)Lokio/Source;\n\tpublic fun getEncoding ()Ljava/lang/String;\n}\n\npublic final class okhttp3/Handshake {\n\tpublic static final field Companion Lokhttp3/Handshake$Companion;\n\tpublic final fun -deprecated_cipherSuite ()Lokhttp3/CipherSuite;\n\tpublic final fun -deprecated_localCertificates ()Ljava/util/List;\n\tpublic final fun -deprecated_localPrincipal ()Ljava/security/Principal;\n\tpublic final fun -deprecated_peerCertificates ()Ljava/util/List;\n\tpublic final fun -deprecated_peerPrincipal ()Ljava/security/Principal;\n\tpublic final fun -deprecated_tlsVersion ()Lokhttp3/TlsVersion;\n\tpublic final fun cipherSuite ()Lokhttp3/CipherSuite;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic static final fun get (Ljavax/net/ssl/SSLSession;)Lokhttp3/Handshake;\n\tpublic static final fun get (Lokhttp3/TlsVersion;Lokhttp3/CipherSuite;Ljava/util/List;Ljava/util/List;)Lokhttp3/Handshake;\n\tpublic fun hashCode ()I\n\tpublic final fun localCertificates ()Ljava/util/List;\n\tpublic final fun localPrincipal ()Ljava/security/Principal;\n\tpublic final fun peerCertificates ()Ljava/util/List;\n\tpublic final fun peerPrincipal ()Ljava/security/Principal;\n\tpublic final fun tlsVersion ()Lokhttp3/TlsVersion;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/Handshake$Companion {\n\tpublic final fun -deprecated_get (Ljavax/net/ssl/SSLSession;)Lokhttp3/Handshake;\n\tpublic final fun get (Ljavax/net/ssl/SSLSession;)Lokhttp3/Handshake;\n\tpublic final fun get (Lokhttp3/TlsVersion;Lokhttp3/CipherSuite;Ljava/util/List;Ljava/util/List;)Lokhttp3/Handshake;\n}\n\npublic final class okhttp3/Headers : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {\n\tpublic static final field Companion Lokhttp3/Headers$Companion;\n\tpublic static final field EMPTY Lokhttp3/Headers;\n\tpublic final fun -deprecated_size ()I\n\tpublic final fun byteCount ()J\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun get (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun getDate (Ljava/lang/String;)Ljava/util/Date;\n\tpublic final fun getInstant (Ljava/lang/String;)Ljava/time/Instant;\n\tpublic fun hashCode ()I\n\tpublic fun iterator ()Ljava/util/Iterator;\n\tpublic final fun name (I)Ljava/lang/String;\n\tpublic final fun names ()Ljava/util/Set;\n\tpublic final fun newBuilder ()Lokhttp3/Headers$Builder;\n\tpublic static final fun of (Ljava/util/Map;)Lokhttp3/Headers;\n\tpublic static final fun of ([Ljava/lang/String;)Lokhttp3/Headers;\n\tpublic final fun size ()I\n\tpublic final fun toMultimap ()Ljava/util/Map;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun value (I)Ljava/lang/String;\n\tpublic final fun values (Ljava/lang/String;)Ljava/util/List;\n}\n\npublic final class okhttp3/Headers$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun add (Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun add (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun add (Ljava/lang/String;Ljava/time/Instant;)Lokhttp3/Headers$Builder;\n\tpublic final fun add (Ljava/lang/String;Ljava/util/Date;)Lokhttp3/Headers$Builder;\n\tpublic final fun addAll (Lokhttp3/Headers;)Lokhttp3/Headers$Builder;\n\tpublic final fun addUnsafeNonAscii (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun build ()Lokhttp3/Headers;\n\tpublic final fun get (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun removeAll (Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun set (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Headers$Builder;\n\tpublic final fun set (Ljava/lang/String;Ljava/time/Instant;)Lokhttp3/Headers$Builder;\n\tpublic final fun set (Ljava/lang/String;Ljava/util/Date;)Lokhttp3/Headers$Builder;\n}\n\npublic final class okhttp3/Headers$Companion {\n\tpublic final fun -deprecated_of (Ljava/util/Map;)Lokhttp3/Headers;\n\tpublic final fun -deprecated_of ([Ljava/lang/String;)Lokhttp3/Headers;\n\tpublic final fun of (Ljava/util/Map;)Lokhttp3/Headers;\n\tpublic final fun of ([Ljava/lang/String;)Lokhttp3/Headers;\n}\n\npublic final class okhttp3/HttpUrl {\n\tpublic static final field Companion Lokhttp3/HttpUrl$Companion;\n\tpublic final fun -deprecated_encodedFragment ()Ljava/lang/String;\n\tpublic final fun -deprecated_encodedPassword ()Ljava/lang/String;\n\tpublic final fun -deprecated_encodedPath ()Ljava/lang/String;\n\tpublic final fun -deprecated_encodedPathSegments ()Ljava/util/List;\n\tpublic final fun -deprecated_encodedQuery ()Ljava/lang/String;\n\tpublic final fun -deprecated_encodedUsername ()Ljava/lang/String;\n\tpublic final fun -deprecated_fragment ()Ljava/lang/String;\n\tpublic final fun -deprecated_host ()Ljava/lang/String;\n\tpublic final fun -deprecated_password ()Ljava/lang/String;\n\tpublic final fun -deprecated_pathSegments ()Ljava/util/List;\n\tpublic final fun -deprecated_pathSize ()I\n\tpublic final fun -deprecated_port ()I\n\tpublic final fun -deprecated_query ()Ljava/lang/String;\n\tpublic final fun -deprecated_queryParameterNames ()Ljava/util/Set;\n\tpublic final fun -deprecated_querySize ()I\n\tpublic final fun -deprecated_scheme ()Ljava/lang/String;\n\tpublic final fun -deprecated_uri ()Ljava/net/URI;\n\tpublic final fun -deprecated_url ()Ljava/net/URL;\n\tpublic final fun -deprecated_username ()Ljava/lang/String;\n\tpublic synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic static final fun defaultPort (Ljava/lang/String;)I\n\tpublic final fun encodedFragment ()Ljava/lang/String;\n\tpublic final fun encodedPassword ()Ljava/lang/String;\n\tpublic final fun encodedPath ()Ljava/lang/String;\n\tpublic final fun encodedPathSegments ()Ljava/util/List;\n\tpublic final fun encodedQuery ()Ljava/lang/String;\n\tpublic final fun encodedUsername ()Ljava/lang/String;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic final fun fragment ()Ljava/lang/String;\n\tpublic static final fun get (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic static final fun get (Ljava/net/URI;)Lokhttp3/HttpUrl;\n\tpublic static final fun get (Ljava/net/URL;)Lokhttp3/HttpUrl;\n\tpublic fun hashCode ()I\n\tpublic final fun host ()Ljava/lang/String;\n\tpublic final fun isHttps ()Z\n\tpublic final fun newBuilder ()Lokhttp3/HttpUrl$Builder;\n\tpublic final fun newBuilder (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic static final fun parse (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun password ()Ljava/lang/String;\n\tpublic final fun pathSegments ()Ljava/util/List;\n\tpublic final fun pathSize ()I\n\tpublic final fun port ()I\n\tpublic final fun query ()Ljava/lang/String;\n\tpublic final fun queryParameter (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun queryParameterName (I)Ljava/lang/String;\n\tpublic final fun queryParameterNames ()Ljava/util/Set;\n\tpublic final fun queryParameterValue (I)Ljava/lang/String;\n\tpublic final fun queryParameterValues (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun querySize ()I\n\tpublic final fun redact ()Ljava/lang/String;\n\tpublic final fun resolve (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun scheme ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun topPrivateDomain ()Ljava/lang/String;\n\tpublic final fun uri ()Ljava/net/URI;\n\tpublic final fun url ()Ljava/net/URL;\n\tpublic final fun username ()Ljava/lang/String;\n}\n\npublic final class okhttp3/HttpUrl$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun addEncodedPathSegment (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addEncodedPathSegments (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addEncodedQueryParameter (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addPathSegment (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addPathSegments (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun addQueryParameter (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun build ()Lokhttp3/HttpUrl;\n\tpublic final fun encodedFragment (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun encodedPassword (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun encodedPath (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun encodedQuery (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun encodedUsername (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun fragment (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun host (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun password (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun port (I)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun query (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun removeAllEncodedQueryParameters (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun removeAllQueryParameters (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun removePathSegment (I)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun scheme (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun setEncodedPathSegment (ILjava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun setEncodedQueryParameter (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun setPathSegment (ILjava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic final fun setQueryParameter (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun username (Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\n}\n\npublic final class okhttp3/HttpUrl$Companion {\n\tpublic final fun -deprecated_get (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun -deprecated_get (Ljava/net/URI;)Lokhttp3/HttpUrl;\n\tpublic final fun -deprecated_get (Ljava/net/URL;)Lokhttp3/HttpUrl;\n\tpublic final fun -deprecated_parse (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun defaultPort (Ljava/lang/String;)I\n\tpublic final fun get (Ljava/lang/String;)Lokhttp3/HttpUrl;\n\tpublic final fun get (Ljava/net/URI;)Lokhttp3/HttpUrl;\n\tpublic final fun get (Ljava/net/URL;)Lokhttp3/HttpUrl;\n\tpublic final fun parse (Ljava/lang/String;)Lokhttp3/HttpUrl;\n}\n\npublic abstract interface class okhttp3/Interceptor {\n\tpublic static final field Companion Lokhttp3/Interceptor$Companion;\n\tpublic abstract fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\n}\n\npublic abstract interface class okhttp3/Interceptor$Chain {\n\tpublic abstract fun call ()Lokhttp3/Call;\n\tpublic abstract fun connectTimeoutMillis ()I\n\tpublic abstract fun connection ()Lokhttp3/Connection;\n\tpublic abstract fun getAuthenticator ()Lokhttp3/Authenticator;\n\tpublic abstract fun getCache ()Lokhttp3/Cache;\n\tpublic abstract fun getCertificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic abstract fun getConnectionPool ()Lokhttp3/ConnectionPool;\n\tpublic abstract fun getCookieJar ()Lokhttp3/CookieJar;\n\tpublic abstract fun getDns ()Lokhttp3/Dns;\n\tpublic abstract fun getEventListener ()Lokhttp3/EventListener;\n\tpublic abstract fun getFollowRedirects ()Z\n\tpublic abstract fun getFollowSslRedirects ()Z\n\tpublic abstract fun getHostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic abstract fun getProxy ()Ljava/net/Proxy;\n\tpublic abstract fun getProxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic abstract fun getProxySelector ()Ljava/net/ProxySelector;\n\tpublic abstract fun getRetryOnConnectionFailure ()Z\n\tpublic abstract fun getSocketFactory ()Ljavax/net/SocketFactory;\n\tpublic abstract fun getSslSocketFactoryOrNull ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic abstract fun getX509TrustManagerOrNull ()Ljavax/net/ssl/X509TrustManager;\n\tpublic abstract fun proceed (Lokhttp3/Request;)Lokhttp3/Response;\n\tpublic abstract fun readTimeoutMillis ()I\n\tpublic abstract fun request ()Lokhttp3/Request;\n\tpublic abstract fun withAuthenticator (Lokhttp3/Authenticator;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withCache (Lokhttp3/Cache;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withCertificatePinner (Lokhttp3/CertificatePinner;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withConnectTimeout (ILjava/util/concurrent/TimeUnit;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withConnectionPool (Lokhttp3/ConnectionPool;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withCookieJar (Lokhttp3/CookieJar;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withDns (Lokhttp3/Dns;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withHostnameVerifier (Ljavax/net/ssl/HostnameVerifier;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withProxy (Ljava/net/Proxy;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withProxyAuthenticator (Lokhttp3/Authenticator;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withProxySelector (Ljava/net/ProxySelector;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withReadTimeout (ILjava/util/concurrent/TimeUnit;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withRetryOnConnectionFailure (Z)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withSocketFactory (Ljavax/net/SocketFactory;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withSslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun withWriteTimeout (ILjava/util/concurrent/TimeUnit;)Lokhttp3/Interceptor$Chain;\n\tpublic abstract fun writeTimeoutMillis ()I\n}\n\npublic final class okhttp3/Interceptor$Companion {\n\tpublic final fun invoke (Lkotlin/jvm/functions/Function1;)Lokhttp3/Interceptor;\n}\n\npublic final class okhttp3/MediaType {\n\tpublic static final field Companion Lokhttp3/MediaType$Companion;\n\tpublic final fun -deprecated_subtype ()Ljava/lang/String;\n\tpublic final fun -deprecated_type ()Ljava/lang/String;\n\tpublic final fun charset ()Ljava/nio/charset/Charset;\n\tpublic final fun charset (Ljava/nio/charset/Charset;)Ljava/nio/charset/Charset;\n\tpublic static synthetic fun charset$default (Lokhttp3/MediaType;Ljava/nio/charset/Charset;ILjava/lang/Object;)Ljava/nio/charset/Charset;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic static final fun get (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic fun hashCode ()I\n\tpublic final fun parameter (Ljava/lang/String;)Ljava/lang/String;\n\tpublic static final fun parse (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic final fun subtype ()Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun type ()Ljava/lang/String;\n}\n\npublic final class okhttp3/MediaType$Companion {\n\tpublic final fun -deprecated_get (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic final fun -deprecated_parse (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic final fun get (Ljava/lang/String;)Lokhttp3/MediaType;\n\tpublic final fun parse (Ljava/lang/String;)Lokhttp3/MediaType;\n}\n\npublic final class okhttp3/MultipartBody : okhttp3/RequestBody {\n\tpublic static final field ALTERNATIVE Lokhttp3/MediaType;\n\tpublic static final field Companion Lokhttp3/MultipartBody$Companion;\n\tpublic static final field DIGEST Lokhttp3/MediaType;\n\tpublic static final field FORM Lokhttp3/MediaType;\n\tpublic static final field MIXED Lokhttp3/MediaType;\n\tpublic static final field PARALLEL Lokhttp3/MediaType;\n\tpublic final fun -deprecated_boundary ()Ljava/lang/String;\n\tpublic final fun -deprecated_parts ()Ljava/util/List;\n\tpublic final fun -deprecated_size ()I\n\tpublic final fun -deprecated_type ()Lokhttp3/MediaType;\n\tpublic final fun boundary ()Ljava/lang/String;\n\tpublic fun contentLength ()J\n\tpublic fun contentType ()Lokhttp3/MediaType;\n\tpublic fun isOneShot ()Z\n\tpublic final fun part (I)Lokhttp3/MultipartBody$Part;\n\tpublic final fun parts ()Ljava/util/List;\n\tpublic final fun size ()I\n\tpublic final fun type ()Lokhttp3/MediaType;\n\tpublic fun writeTo (Lokio/BufferedSink;)V\n}\n\npublic final class okhttp3/MultipartBody$Builder {\n\tpublic fun <init> ()V\n\tpublic fun <init> (Ljava/lang/String;)V\n\tpublic synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun addFormDataPart (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun addFormDataPart (Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun addPart (Lokhttp3/Headers;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun addPart (Lokhttp3/MultipartBody$Part;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun addPart (Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Builder;\n\tpublic final fun build ()Lokhttp3/MultipartBody;\n\tpublic final fun setType (Lokhttp3/MediaType;)Lokhttp3/MultipartBody$Builder;\n}\n\npublic final class okhttp3/MultipartBody$Companion {\n}\n\npublic final class okhttp3/MultipartBody$Part {\n\tpublic static final field Companion Lokhttp3/MultipartBody$Part$Companion;\n\tpublic final fun -deprecated_body ()Lokhttp3/RequestBody;\n\tpublic final fun -deprecated_headers ()Lokhttp3/Headers;\n\tpublic synthetic fun <init> (Lokhttp3/Headers;Lokhttp3/RequestBody;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun body ()Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/Headers;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic static final fun create (Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic static final fun createFormData (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/MultipartBody$Part;\n\tpublic static final fun createFormData (Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic final fun headers ()Lokhttp3/Headers;\n}\n\npublic final class okhttp3/MultipartBody$Part$Companion {\n\tpublic final fun create (Lokhttp3/Headers;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic final fun create (Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n\tpublic final fun createFormData (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/MultipartBody$Part;\n\tpublic final fun createFormData (Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/MultipartBody$Part;\n}\n\npublic final class okhttp3/MultipartReader : java/io/Closeable {\n\tpublic fun <init> (Lokhttp3/ResponseBody;)V\n\tpublic fun <init> (Lokio/BufferedSource;Ljava/lang/String;)V\n\tpublic final fun boundary ()Ljava/lang/String;\n\tpublic fun close ()V\n\tpublic final fun nextPart ()Lokhttp3/MultipartReader$Part;\n}\n\npublic final class okhttp3/MultipartReader$Part : java/io/Closeable {\n\tpublic fun <init> (Lokhttp3/Headers;Lokio/BufferedSource;)V\n\tpublic final fun body ()Lokio/BufferedSource;\n\tpublic fun close ()V\n\tpublic final fun headers ()Lokhttp3/Headers;\n}\n\npublic final class okhttp3/OkHttp {\n\tpublic static final field INSTANCE Lokhttp3/OkHttp;\n\tpublic static final field VERSION Ljava/lang/String;\n}\n\npublic class okhttp3/OkHttpClient : okhttp3/Call$Factory, okhttp3/WebSocket$Factory {\n\tpublic static final field Companion Lokhttp3/OkHttpClient$Companion;\n\tpublic final fun -deprecated_authenticator ()Lokhttp3/Authenticator;\n\tpublic final fun -deprecated_cache ()Lokhttp3/Cache;\n\tpublic final fun -deprecated_callTimeoutMillis ()I\n\tpublic final fun -deprecated_certificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic final fun -deprecated_connectTimeoutMillis ()I\n\tpublic final fun -deprecated_connectionPool ()Lokhttp3/ConnectionPool;\n\tpublic final fun -deprecated_connectionSpecs ()Ljava/util/List;\n\tpublic final fun -deprecated_cookieJar ()Lokhttp3/CookieJar;\n\tpublic final fun -deprecated_dispatcher ()Lokhttp3/Dispatcher;\n\tpublic final fun -deprecated_dns ()Lokhttp3/Dns;\n\tpublic final fun -deprecated_eventListenerFactory ()Lokhttp3/EventListener$Factory;\n\tpublic final fun -deprecated_followRedirects ()Z\n\tpublic final fun -deprecated_followSslRedirects ()Z\n\tpublic final fun -deprecated_hostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic final fun -deprecated_interceptors ()Ljava/util/List;\n\tpublic final fun -deprecated_networkInterceptors ()Ljava/util/List;\n\tpublic final fun -deprecated_pingIntervalMillis ()I\n\tpublic final fun -deprecated_protocols ()Ljava/util/List;\n\tpublic final fun -deprecated_proxy ()Ljava/net/Proxy;\n\tpublic final fun -deprecated_proxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic final fun -deprecated_proxySelector ()Ljava/net/ProxySelector;\n\tpublic final fun -deprecated_readTimeoutMillis ()I\n\tpublic final fun -deprecated_retryOnConnectionFailure ()Z\n\tpublic final fun -deprecated_socketFactory ()Ljavax/net/SocketFactory;\n\tpublic final fun -deprecated_sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic final fun -deprecated_writeTimeoutMillis ()I\n\tpublic fun <init> ()V\n\tpublic final fun address (Lokhttp3/HttpUrl;)Lokhttp3/Address;\n\tpublic final fun authenticator ()Lokhttp3/Authenticator;\n\tpublic final fun cache ()Lokhttp3/Cache;\n\tpublic final fun callTimeoutMillis ()I\n\tpublic final fun certificateChainCleaner ()Lokhttp3/internal/tls/CertificateChainCleaner;\n\tpublic final fun certificatePinner ()Lokhttp3/CertificatePinner;\n\tpublic final fun connectTimeoutMillis ()I\n\tpublic final fun connectionPool ()Lokhttp3/ConnectionPool;\n\tpublic final fun connectionSpecs ()Ljava/util/List;\n\tpublic final fun cookieJar ()Lokhttp3/CookieJar;\n\tpublic final fun dispatcher ()Lokhttp3/Dispatcher;\n\tpublic final fun dns ()Lokhttp3/Dns;\n\tpublic final fun eventListenerFactory ()Lokhttp3/EventListener$Factory;\n\tpublic final fun fastFallback ()Z\n\tpublic final fun followRedirects ()Z\n\tpublic final fun followSslRedirects ()Z\n\tpublic final fun hostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;\n\tpublic final fun interceptors ()Ljava/util/List;\n\tpublic final fun minWebSocketMessageToCompress ()J\n\tpublic final fun networkInterceptors ()Ljava/util/List;\n\tpublic fun newBuilder ()Lokhttp3/OkHttpClient$Builder;\n\tpublic fun newCall (Lokhttp3/Request;)Lokhttp3/Call;\n\tpublic fun newWebSocket (Lokhttp3/Request;Lokhttp3/WebSocketListener;)Lokhttp3/WebSocket;\n\tpublic final fun pingIntervalMillis ()I\n\tpublic final fun protocols ()Ljava/util/List;\n\tpublic final fun proxy ()Ljava/net/Proxy;\n\tpublic final fun proxyAuthenticator ()Lokhttp3/Authenticator;\n\tpublic final fun proxySelector ()Ljava/net/ProxySelector;\n\tpublic final fun readTimeoutMillis ()I\n\tpublic final fun retryOnConnectionFailure ()Z\n\tpublic final fun socketFactory ()Ljavax/net/SocketFactory;\n\tpublic final fun sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic final fun webSocketCloseTimeout ()I\n\tpublic final fun writeTimeoutMillis ()I\n\tpublic final fun x509TrustManager ()Ljavax/net/ssl/X509TrustManager;\n}\n\npublic final class okhttp3/OkHttpClient$Builder {\n\tpublic final fun -addInterceptor (Lkotlin/jvm/functions/Function1;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun -addNetworkInterceptor (Lkotlin/jvm/functions/Function1;)Lokhttp3/OkHttpClient$Builder;\n\tpublic fun <init> ()V\n\tpublic final fun addInterceptor (Lokhttp3/Interceptor;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun addNetworkInterceptor (Lokhttp3/Interceptor;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun authenticator (Lokhttp3/Authenticator;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun build ()Lokhttp3/OkHttpClient;\n\tpublic final fun cache (Lokhttp3/Cache;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun callTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun callTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun callTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun certificatePinner (Lokhttp3/CertificatePinner;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectionPool (Lokhttp3/ConnectionPool;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun connectionSpecs (Ljava/util/List;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun cookieJar (Lokhttp3/CookieJar;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun dispatcher (Lokhttp3/Dispatcher;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun dns (Lokhttp3/Dns;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun eventListener (Lokhttp3/EventListener;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun eventListenerFactory (Lokhttp3/EventListener$Factory;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun fastFallback (Z)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun followRedirects (Z)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun followSslRedirects (Z)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun hostnameVerifier (Ljavax/net/ssl/HostnameVerifier;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun interceptors ()Ljava/util/List;\n\tpublic final fun minWebSocketMessageToCompress (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun networkInterceptors ()Ljava/util/List;\n\tpublic final fun pingInterval (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun pingInterval (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun pingInterval-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun protocols (Ljava/util/List;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun proxy (Ljava/net/Proxy;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun proxyAuthenticator (Lokhttp3/Authenticator;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun proxySelector (Ljava/net/ProxySelector;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun readTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun readTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun readTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun retryOnConnectionFailure (Z)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun socketFactory (Ljavax/net/SocketFactory;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun sslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun sslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun webSocketCloseTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun webSocketCloseTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun webSocketCloseTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun writeTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun writeTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;\n\tpublic final fun writeTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;\n}\n\npublic final class okhttp3/OkHttpClient$Companion {\n}\n\npublic final class okhttp3/Protocol : java/lang/Enum {\n\tpublic static final field Companion Lokhttp3/Protocol$Companion;\n\tpublic static final field H2_PRIOR_KNOWLEDGE Lokhttp3/Protocol;\n\tpublic static final field HTTP_1_0 Lokhttp3/Protocol;\n\tpublic static final field HTTP_1_1 Lokhttp3/Protocol;\n\tpublic static final field HTTP_2 Lokhttp3/Protocol;\n\tpublic static final field HTTP_3 Lokhttp3/Protocol;\n\tpublic static final field QUIC Lokhttp3/Protocol;\n\tpublic static final field SPDY_3 Lokhttp3/Protocol;\n\tpublic static final fun get (Ljava/lang/String;)Lokhttp3/Protocol;\n\tpublic static fun getEntries ()Lkotlin/enums/EnumEntries;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic static fun valueOf (Ljava/lang/String;)Lokhttp3/Protocol;\n\tpublic static fun values ()[Lokhttp3/Protocol;\n}\n\npublic final class okhttp3/Protocol$Companion {\n\tpublic final fun get (Ljava/lang/String;)Lokhttp3/Protocol;\n}\n\npublic final class okhttp3/Request {\n\tpublic final fun -deprecated_body ()Lokhttp3/RequestBody;\n\tpublic final fun -deprecated_cacheControl ()Lokhttp3/CacheControl;\n\tpublic final fun -deprecated_headers ()Lokhttp3/Headers;\n\tpublic final fun -deprecated_method ()Ljava/lang/String;\n\tpublic final fun -deprecated_url ()Lokhttp3/HttpUrl;\n\tpublic fun <init> (Lokhttp3/HttpUrl;Lokhttp3/Headers;Ljava/lang/String;Lokhttp3/RequestBody;)V\n\tpublic synthetic fun <init> (Lokhttp3/HttpUrl;Lokhttp3/Headers;Ljava/lang/String;Lokhttp3/RequestBody;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun body ()Lokhttp3/RequestBody;\n\tpublic final fun cacheControl ()Lokhttp3/CacheControl;\n\tpublic final fun cacheUrlOverride ()Lokhttp3/HttpUrl;\n\tpublic final fun header (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun headers ()Lokhttp3/Headers;\n\tpublic final fun headers (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun isHttps ()Z\n\tpublic final fun method ()Ljava/lang/String;\n\tpublic final fun newBuilder ()Lokhttp3/Request$Builder;\n\tpublic final fun tag ()Ljava/lang/Object;\n\tpublic final fun tag (Ljava/lang/Class;)Ljava/lang/Object;\n\tpublic final fun tag (Lkotlin/reflect/KClass;)Ljava/lang/Object;\n\tpublic final fun toCurl ()Ljava/lang/String;\n\tpublic final fun toCurl (Z)Ljava/lang/String;\n\tpublic static synthetic fun toCurl$default (Lokhttp3/Request;ZILjava/lang/Object;)Ljava/lang/String;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun url ()Lokhttp3/HttpUrl;\n}\n\npublic class okhttp3/Request$Builder {\n\tpublic fun <init> ()V\n\tpublic fun addHeader (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder;\n\tpublic fun build ()Lokhttp3/Request;\n\tpublic fun cacheControl (Lokhttp3/CacheControl;)Lokhttp3/Request$Builder;\n\tpublic final fun cacheUrlOverride (Lokhttp3/HttpUrl;)Lokhttp3/Request$Builder;\n\tpublic final fun delete ()Lokhttp3/Request$Builder;\n\tpublic fun delete (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic static synthetic fun delete$default (Lokhttp3/Request$Builder;Lokhttp3/RequestBody;ILjava/lang/Object;)Lokhttp3/Request$Builder;\n\tpublic fun get ()Lokhttp3/Request$Builder;\n\tpublic final fun gzip ()Lokhttp3/Request$Builder;\n\tpublic fun head ()Lokhttp3/Request$Builder;\n\tpublic fun header (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder;\n\tpublic fun headers (Lokhttp3/Headers;)Lokhttp3/Request$Builder;\n\tpublic fun method (Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun patch (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun post (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun put (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun query (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\n\tpublic fun removeHeader (Ljava/lang/String;)Lokhttp3/Request$Builder;\n\tpublic fun tag (Ljava/lang/Class;Ljava/lang/Object;)Lokhttp3/Request$Builder;\n\tpublic fun tag (Ljava/lang/Object;)Lokhttp3/Request$Builder;\n\tpublic final fun tag (Lkotlin/reflect/KClass;Ljava/lang/Object;)Lokhttp3/Request$Builder;\n\tpublic fun url (Ljava/lang/String;)Lokhttp3/Request$Builder;\n\tpublic fun url (Ljava/net/URL;)Lokhttp3/Request$Builder;\n\tpublic fun url (Lokhttp3/HttpUrl;)Lokhttp3/Request$Builder;\n}\n\npublic abstract class okhttp3/RequestBody {\n\tpublic static final field Companion Lokhttp3/RequestBody$Companion;\n\tpublic static final field EMPTY Lokhttp3/RequestBody;\n\tpublic fun <init> ()V\n\tpublic fun contentLength ()J\n\tpublic abstract fun contentType ()Lokhttp3/MediaType;\n\tpublic static final fun create (Ljava/io/File;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Ljava/io/FileDescriptor;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Ljava/io/File;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Lokio/ByteString;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;[B)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;[BI)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokhttp3/MediaType;[BII)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokio/ByteString;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create (Lokio/Path;Lokio/FileSystem;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create ([B)Lokhttp3/RequestBody;\n\tpublic static final fun create ([BLokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic static final fun create ([BLokhttp3/MediaType;I)Lokhttp3/RequestBody;\n\tpublic static final fun create ([BLokhttp3/MediaType;II)Lokhttp3/RequestBody;\n\tpublic fun isDuplex ()Z\n\tpublic fun isOneShot ()Z\n\tpublic final fun sha256 ()Lokio/ByteString;\n\tpublic abstract fun writeTo (Lokio/BufferedSink;)V\n}\n\npublic final class okhttp3/RequestBody$Companion {\n\tpublic final fun create (Ljava/io/File;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create (Ljava/io/FileDescriptor;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create (Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;Ljava/io/File;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;Lokio/ByteString;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;[B)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;[BI)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokhttp3/MediaType;[BII)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokio/ByteString;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create (Lokio/Path;Lokio/FileSystem;Lokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create ([B)Lokhttp3/RequestBody;\n\tpublic final fun create ([BLokhttp3/MediaType;)Lokhttp3/RequestBody;\n\tpublic final fun create ([BLokhttp3/MediaType;I)Lokhttp3/RequestBody;\n\tpublic final fun create ([BLokhttp3/MediaType;II)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Ljava/io/File;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Ljava/io/FileDescriptor;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Ljava/lang/String;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Lokhttp3/MediaType;[BIIILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Lokio/ByteString;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;Lokio/Path;Lokio/FileSystem;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/RequestBody;\n\tpublic static synthetic fun create$default (Lokhttp3/RequestBody$Companion;[BLokhttp3/MediaType;IIILjava/lang/Object;)Lokhttp3/RequestBody;\n}\n\npublic final class okhttp3/Response : java/io/Closeable {\n\tpublic final fun -deprecated_body ()Lokhttp3/ResponseBody;\n\tpublic final fun -deprecated_cacheControl ()Lokhttp3/CacheControl;\n\tpublic final fun -deprecated_cacheResponse ()Lokhttp3/Response;\n\tpublic final fun -deprecated_code ()I\n\tpublic final fun -deprecated_handshake ()Lokhttp3/Handshake;\n\tpublic final fun -deprecated_headers ()Lokhttp3/Headers;\n\tpublic final fun -deprecated_message ()Ljava/lang/String;\n\tpublic final fun -deprecated_networkResponse ()Lokhttp3/Response;\n\tpublic final fun -deprecated_priorResponse ()Lokhttp3/Response;\n\tpublic final fun -deprecated_protocol ()Lokhttp3/Protocol;\n\tpublic final fun -deprecated_receivedResponseAtMillis ()J\n\tpublic final fun -deprecated_request ()Lokhttp3/Request;\n\tpublic final fun -deprecated_sentRequestAtMillis ()J\n\tpublic final fun body ()Lokhttp3/ResponseBody;\n\tpublic final fun cacheControl ()Lokhttp3/CacheControl;\n\tpublic final fun cacheResponse ()Lokhttp3/Response;\n\tpublic final fun challenges ()Ljava/util/List;\n\tpublic fun close ()V\n\tpublic final fun code ()I\n\tpublic final fun handshake ()Lokhttp3/Handshake;\n\tpublic final fun header (Ljava/lang/String;)Ljava/lang/String;\n\tpublic final fun header (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n\tpublic static synthetic fun header$default (Lokhttp3/Response;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String;\n\tpublic final fun headers ()Lokhttp3/Headers;\n\tpublic final fun headers (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun isRedirect ()Z\n\tpublic final fun isSuccessful ()Z\n\tpublic final fun message ()Ljava/lang/String;\n\tpublic final fun networkResponse ()Lokhttp3/Response;\n\tpublic final fun newBuilder ()Lokhttp3/Response$Builder;\n\tpublic final fun peekBody (J)Lokhttp3/ResponseBody;\n\tpublic final fun peekTrailers ()Lokhttp3/Headers;\n\tpublic final fun priorResponse ()Lokhttp3/Response;\n\tpublic final fun protocol ()Lokhttp3/Protocol;\n\tpublic final fun receivedResponseAtMillis ()J\n\tpublic final fun request ()Lokhttp3/Request;\n\tpublic final fun sentRequestAtMillis ()J\n\tpublic final fun socket ()Lokio/Socket;\n\tpublic fun toString ()Ljava/lang/String;\n\tpublic final fun trailers ()Lokhttp3/Headers;\n}\n\npublic class okhttp3/Response$Builder {\n\tpublic fun <init> ()V\n\tpublic fun addHeader (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Response$Builder;\n\tpublic fun body (Lokhttp3/ResponseBody;)Lokhttp3/Response$Builder;\n\tpublic fun build ()Lokhttp3/Response;\n\tpublic fun cacheResponse (Lokhttp3/Response;)Lokhttp3/Response$Builder;\n\tpublic fun code (I)Lokhttp3/Response$Builder;\n\tpublic fun handshake (Lokhttp3/Handshake;)Lokhttp3/Response$Builder;\n\tpublic fun header (Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Response$Builder;\n\tpublic fun headers (Lokhttp3/Headers;)Lokhttp3/Response$Builder;\n\tpublic fun message (Ljava/lang/String;)Lokhttp3/Response$Builder;\n\tpublic fun networkResponse (Lokhttp3/Response;)Lokhttp3/Response$Builder;\n\tpublic fun priorResponse (Lokhttp3/Response;)Lokhttp3/Response$Builder;\n\tpublic fun protocol (Lokhttp3/Protocol;)Lokhttp3/Response$Builder;\n\tpublic fun receivedResponseAtMillis (J)Lokhttp3/Response$Builder;\n\tpublic fun removeHeader (Ljava/lang/String;)Lokhttp3/Response$Builder;\n\tpublic fun request (Lokhttp3/Request;)Lokhttp3/Response$Builder;\n\tpublic fun sentRequestAtMillis (J)Lokhttp3/Response$Builder;\n\tpublic fun socket (Lokio/Socket;)Lokhttp3/Response$Builder;\n\tpublic fun trailers (Lokhttp3/TrailersSource;)Lokhttp3/Response$Builder;\n}\n\npublic abstract class okhttp3/ResponseBody : java/io/Closeable {\n\tpublic static final field Companion Lokhttp3/ResponseBody$Companion;\n\tpublic static final field EMPTY Lokhttp3/ResponseBody;\n\tpublic fun <init> ()V\n\tpublic final fun byteStream ()Ljava/io/InputStream;\n\tpublic final fun byteString ()Lokio/ByteString;\n\tpublic final fun bytes ()[B\n\tpublic final fun charStream ()Ljava/io/Reader;\n\tpublic fun close ()V\n\tpublic abstract fun contentLength ()J\n\tpublic abstract fun contentType ()Lokhttp3/MediaType;\n\tpublic static final fun create (Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokhttp3/MediaType;JLokio/BufferedSource;)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokhttp3/MediaType;Lokio/ByteString;)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokhttp3/MediaType;[B)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokio/BufferedSource;Lokhttp3/MediaType;J)Lokhttp3/ResponseBody;\n\tpublic static final fun create (Lokio/ByteString;Lokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic static final fun create ([BLokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic abstract fun source ()Lokio/BufferedSource;\n\tpublic final fun string ()Ljava/lang/String;\n}\n\npublic final class okhttp3/ResponseBody$Companion {\n\tpublic final fun create (Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokhttp3/MediaType;JLokio/BufferedSource;)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokhttp3/MediaType;Lokio/ByteString;)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokhttp3/MediaType;[B)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokio/BufferedSource;Lokhttp3/MediaType;J)Lokhttp3/ResponseBody;\n\tpublic final fun create (Lokio/ByteString;Lokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic final fun create ([BLokhttp3/MediaType;)Lokhttp3/ResponseBody;\n\tpublic static synthetic fun create$default (Lokhttp3/ResponseBody$Companion;Ljava/lang/String;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/ResponseBody;\n\tpublic static synthetic fun create$default (Lokhttp3/ResponseBody$Companion;Lokio/BufferedSource;Lokhttp3/MediaType;JILjava/lang/Object;)Lokhttp3/ResponseBody;\n\tpublic static synthetic fun create$default (Lokhttp3/ResponseBody$Companion;Lokio/ByteString;Lokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/ResponseBody;\n\tpublic static synthetic fun create$default (Lokhttp3/ResponseBody$Companion;[BLokhttp3/MediaType;ILjava/lang/Object;)Lokhttp3/ResponseBody;\n}\n\npublic final class okhttp3/Route {\n\tpublic final fun -deprecated_address ()Lokhttp3/Address;\n\tpublic final fun -deprecated_proxy ()Ljava/net/Proxy;\n\tpublic final fun -deprecated_socketAddress ()Ljava/net/InetSocketAddress;\n\tpublic fun <init> (Lokhttp3/Address;Ljava/net/Proxy;Ljava/net/InetSocketAddress;)V\n\tpublic final fun address ()Lokhttp3/Address;\n\tpublic fun equals (Ljava/lang/Object;)Z\n\tpublic fun hashCode ()I\n\tpublic final fun proxy ()Ljava/net/Proxy;\n\tpublic final fun requiresTunnel ()Z\n\tpublic final fun socketAddress ()Ljava/net/InetSocketAddress;\n\tpublic fun toString ()Ljava/lang/String;\n}\n\npublic final class okhttp3/TlsVersion : java/lang/Enum {\n\tpublic static final field Companion Lokhttp3/TlsVersion$Companion;\n\tpublic static final field SSL_3_0 Lokhttp3/TlsVersion;\n\tpublic static final field TLS_1_0 Lokhttp3/TlsVersion;\n\tpublic static final field TLS_1_1 Lokhttp3/TlsVersion;\n\tpublic static final field TLS_1_2 Lokhttp3/TlsVersion;\n\tpublic static final field TLS_1_3 Lokhttp3/TlsVersion;\n\tpublic final fun -deprecated_javaName ()Ljava/lang/String;\n\tpublic static final fun forJavaName (Ljava/lang/String;)Lokhttp3/TlsVersion;\n\tpublic static fun getEntries ()Lkotlin/enums/EnumEntries;\n\tpublic final fun javaName ()Ljava/lang/String;\n\tpublic static fun valueOf (Ljava/lang/String;)Lokhttp3/TlsVersion;\n\tpublic static fun values ()[Lokhttp3/TlsVersion;\n}\n\npublic final class okhttp3/TlsVersion$Companion {\n\tpublic final fun forJavaName (Ljava/lang/String;)Lokhttp3/TlsVersion;\n}\n\npublic abstract interface class okhttp3/TrailersSource {\n\tpublic static final field Companion Lokhttp3/TrailersSource$Companion;\n\tpublic static final field EMPTY Lokhttp3/TrailersSource;\n\tpublic abstract fun get ()Lokhttp3/Headers;\n\tpublic fun peek ()Lokhttp3/Headers;\n}\n\npublic final class okhttp3/TrailersSource$Companion {\n}\n\npublic abstract interface class okhttp3/WebSocket {\n\tpublic abstract fun cancel ()V\n\tpublic abstract fun close (ILjava/lang/String;)Z\n\tpublic abstract fun queueSize ()J\n\tpublic abstract fun request ()Lokhttp3/Request;\n\tpublic abstract fun send (Ljava/lang/String;)Z\n\tpublic abstract fun send (Lokio/ByteString;)Z\n}\n\npublic abstract interface class okhttp3/WebSocket$Factory {\n\tpublic abstract fun newWebSocket (Lokhttp3/Request;Lokhttp3/WebSocketListener;)Lokhttp3/WebSocket;\n}\n\npublic abstract class okhttp3/WebSocketListener {\n\tpublic fun <init> ()V\n\tpublic fun onClosed (Lokhttp3/WebSocket;ILjava/lang/String;)V\n\tpublic fun onClosing (Lokhttp3/WebSocket;ILjava/lang/String;)V\n\tpublic fun onFailure (Lokhttp3/WebSocket;Ljava/lang/Throwable;Lokhttp3/Response;)V\n\tpublic fun onMessage (Lokhttp3/WebSocket;Ljava/lang/String;)V\n\tpublic fun onMessage (Lokhttp3/WebSocket;Lokio/ByteString;)V\n\tpublic fun onOpen (Lokhttp3/WebSocket;Lokhttp3/Response;)V\n}\n\n"
  },
  {
    "path": "okhttp/build.gradle.kts",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\nimport okhttp3.buildsupport.alpnBootVersion\nimport okhttp3.buildsupport.platform\nimport okhttp3.buildsupport.testJavaVersion\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\nimport org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile\nimport ru.vyarus.gradle.plugin.animalsniffer.AnimalSniffer\nimport ru.vyarus.gradle.plugin.animalsniffer.AnimalSnifferExtension\n\nplugins {\n  kotlin(\"multiplatform\")\n  id(\"com.android.kotlin.multiplatform.library\")\n  kotlin(\"plugin.serialization\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n  id(\"app.cash.burst\")\n  alias(libs.plugins.maven.sympathy)\n}\n\nval copyKotlinTemplates =\n  tasks.register<Copy>(\"copyKotlinTemplates\") {\n    val kotlinTemplatesOutput = layout.buildDirectory.dir(\"generated/sources/kotlinTemplates\")\n\n    from(\"src/commonJvmAndroid/kotlinTemplates\")\n    into(kotlinTemplatesOutput)\n\n    filteringCharset = Charsets.UTF_8.toString()\n\n    expand(\n      \"projectVersion\" to project.version.toString(),\n    )\n  }\n\n// Build & use okhttp3/internal/idn/IdnaMappingTableInstance.kt\nval generateIdnaMappingTableConfiguration: Configuration by configurations.creating\ndependencies {\n  generateIdnaMappingTableConfiguration(projects.okhttpIdnaMappingTable)\n}\nval generateIdnaMappingTable =\n  tasks.register<JavaExec>(\"generateIdnaMappingTable\") {\n    val idnaOutput = layout.buildDirectory.dir(\"generated/sources/idnaMappingTable\")\n\n    outputs.dir(idnaOutput)\n    mainClass.set(\"okhttp3.internal.idn.GenerateIdnaMappingTableCode\")\n    args(idnaOutput.get())\n    classpath = generateIdnaMappingTableConfiguration\n  }\n\nkotlin {\n  jvmToolchain(21)\n\n  jvm {\n  }\n\n  androidLibrary {\n    namespace = \"okhttp.okhttp3\"\n    compileSdk = 36\n    minSdk = 21\n\n    androidResources {\n      enable = true\n    }\n\n    optimization {\n      consumerKeepRules.publish = true\n      consumerKeepRules.files.add(file(\"okhttp3.pro\"))\n    }\n\n    withDeviceTest {\n      instrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n      execution = \"HOST\"\n    }\n\n    // SDK 35 needs 17, for now avoid trying to run on\n    // multiple robolectric versions, since device tests\n    // do that\n    if (testJavaVersion >= 17) {\n      withHostTest {\n        isIncludeAndroidResources = true\n      }\n    }\n  }\n\n  sourceSets {\n    val commonJvmAndroid =\n      create(\"commonJvmAndroid\") {\n        dependsOn(commonMain.get())\n\n        kotlin.srcDir(copyKotlinTemplates.map { it.outputs })\n        kotlin.srcDir(generateIdnaMappingTable.map { it.outputs })\n\n        dependencies {\n          api(libs.square.okio)\n          api(libs.kotlin.stdlib)\n\n          compileOnly(libs.animalsniffer.annotations)\n        }\n      }\n\n    commonTest {\n      dependencies {\n        implementation(projects.okhttpTestingSupport)\n        implementation(libs.assertk)\n        implementation(libs.kotlin.test.annotations)\n        implementation(libs.kotlin.test.common)\n        implementation(libs.kotlin.test.junit)\n        implementation(libs.junit)\n        implementation(libs.junit.jupiter.api)\n        implementation(libs.junit.jupiter.params)\n      }\n    }\n\n    androidMain {\n      dependsOn(commonJvmAndroid)\n      dependencies {\n        compileOnly(libs.bouncycastle.bcprov)\n        compileOnly(libs.bouncycastle.bctls)\n        compileOnly(libs.conscrypt.openjdk)\n        implementation(libs.androidx.annotation)\n        implementation(libs.androidx.startup.runtime)\n      }\n    }\n\n    jvmMain {\n      dependsOn(commonJvmAndroid)\n\n      dependencies {\n        // These compileOnly dependencies must also be listed in applyOsgiMultiplatform() below.\n        compileOnly(libs.conscrypt.openjdk)\n        compileOnly(libs.bouncycastle.bcprov)\n        compileOnly(libs.bouncycastle.bctls)\n\n        // graal build support\n        compileOnly(libs.native.image.svm)\n        compileOnly(libs.openjsse)\n      }\n    }\n\n    val jvmTest by getting {\n      dependencies {\n        implementation(libs.assertk)\n        implementation(libs.conscrypt.openjdk)\n        implementation(libs.junit.jupiter.api)\n        implementation(libs.junit.jupiter.engine)\n        implementation(libs.junit.jupiter.params)\n        implementation(libs.junit.vintage.engine)\n        implementation(libs.junit)\n        implementation(libs.kotlin.test.annotations)\n        implementation(libs.kotlin.test.common)\n        implementation(libs.kotlin.test.junit)\n        implementation(libs.kotlinx.coroutines.core)\n        implementation(libs.kotlinx.serialization.core)\n        implementation(libs.kotlinx.serialization.json)\n        implementation(libs.openjsse)\n        implementation(libs.square.moshi.kotlin)\n        implementation(libs.square.moshi)\n        implementation(libs.square.okio.fakefilesystem)\n        implementation(projects.loggingInterceptor)\n        implementation(projects.mockwebserver)\n        implementation(projects.mockwebserver3)\n        implementation(projects.mockwebserver3Junit4)\n        implementation(projects.mockwebserver3Junit5)\n        implementation(projects.okhttpBrotli)\n        implementation(projects.okhttpCoroutines)\n        implementation(projects.okhttpDnsoverhttps)\n        implementation(projects.okhttpIdnaMappingTable)\n        implementation(projects.okhttpJavaNetCookiejar)\n        implementation(projects.okhttpSse)\n        implementation(projects.okhttpTestingSupport)\n        implementation(projects.okhttpTls)\n        implementation(projects.okhttpUrlconnection)\n\n        if (platform == \"conscrypt\") {\n          implementation(libs.conscrypt.openjdk)\n        } else if (platform == \"openjsse\") {\n          implementation(libs.openjsse)\n        }\n      }\n    }\n\n    if (testJavaVersion >= 17) {\n      val androidHostTest by getting {\n        dependencies {\n          implementation(libs.androidx.junit)\n          implementation(libs.assertk)\n          implementation(libs.junit.jupiter.engine)\n          implementation(libs.junit.vintage.engine)\n          implementation(libs.kotlin.test.annotations)\n          implementation(libs.kotlin.test.common)\n          implementation(libs.robolectric)\n        }\n      }\n    }\n  }\n}\n\nif (platform == \"jdk8alpn\") {\n  // Add alpn-boot on Java 8 so we can use HTTP/2 without a stable API.\n  val alpnBootVersion = alpnBootVersion\n  if (alpnBootVersion != null) {\n    val alpnBootJar =\n      configurations\n        .detachedConfiguration(\n          dependencies.create(\"org.mortbay.jetty.alpn:alpn-boot:$alpnBootVersion\"),\n        ).singleFile\n    tasks.withType<Test> {\n      jvmArgs(\"-Xbootclasspath/p:$alpnBootJar\")\n    }\n  }\n}\n\n// From https://github.com/Kotlin/kotlinx-atomicfu/blob/master/atomicfu/build.gradle.kts\nval compileJavaModuleInfo by tasks.registering(JavaCompile::class) {\n  val moduleName = \"okhttp3\"\n  val compilation = kotlin.targets[\"jvm\"].compilations[\"main\"]\n  val compileKotlinTask = compilation.compileTaskProvider.get() as KotlinJvmCompile\n  val targetDir = compileKotlinTask.destinationDirectory.dir(\"../java9\")\n  val sourceDir = file(\"src/jvmMain/java9/\")\n\n  // Use a Java 11 compiler for the module info.\n  javaCompiler.set(project.javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(11)) })\n\n  // Always compile kotlin classes before the module descriptor.\n  dependsOn(compileKotlinTask)\n\n  // Add the module-info source file.\n  source(sourceDir)\n\n  // Also add the module-info.java source file to the Kotlin compile task.\n  // The Kotlin compiler will parse and check module dependencies,\n  // but it currently won't compile to a module-info.class file.\n  // Note that module checking only works on JDK 9+,\n  // because the JDK built-in base modules are not available in earlier versions.\n  val javaVersion = compileKotlinTask.kotlinJavaToolchain.javaVersion.getOrNull()\n  when {\n    javaVersion?.isJava9Compatible == true -> {\n      logger.info(\"Module-info checking is enabled; $compileKotlinTask is compiled using Java $javaVersion\")\n      // Disabled as this module can't see the others in this build for some reason\n//      compileKotlinTask.source(sourceDir)\n    }\n\n    else -> {\n      logger.info(\"Module-info checking is disabled\")\n    }\n  }\n  // Set the task outputs and destination dir\n  outputs.dir(targetDir)\n  destinationDirectory.set(targetDir)\n\n  // Configure JVM compatibility\n  sourceCompatibility = JavaVersion.VERSION_1_9.toString()\n  targetCompatibility = JavaVersion.VERSION_1_9.toString()\n\n  // Set the Java release version.\n  options.release.set(9)\n\n  // Ignore warnings about using 'requires transitive' on automatic modules.\n  // not needed when compiling with recent JDKs, e.g. 17\n  options.compilerArgs.add(\"-Xlint:-requires-transitive-automatic\")\n\n  // Patch the compileKotlinJvm output classes into the compilation so exporting packages works correctly.\n  options.compilerArgs.addAll(\n    listOf(\n      \"--patch-module\",\n      \"$moduleName=${compileKotlinTask.destinationDirectory.get().asFile}\",\n    ),\n  )\n\n  // Use the classpath of the compileKotlinJvm task.\n  // Also, ensure that the module path is used instead of the classpath.\n  classpath = compileKotlinTask.libraries\n  modularity.inferModulePath.set(true)\n}\n\n// Call the convention when the task has finished, to modify the jar to contain OSGi metadata.\ntasks.named<Jar>(\"jvmJar\").configure {\n  manifest {\n    attributes(\n      \"Multi-Release\" to true,\n    )\n  }\n\n  from(compileJavaModuleInfo.map { it.destinationDirectory }) {\n    into(\"META-INF/versions/9/\")\n  }\n}\n\nproject.applyOsgiMultiplatform(\n  \"Export-Package: okhttp3,okhttp3.internal.*;okhttpinternal=true;mandatory:=okhttpinternal\",\n  \"Import-Package: \" +\n    \"com.oracle.svm.core.annotate;resolution:=optional,\" +\n    \"com.oracle.svm.core.configure;resolution:=optional,\" +\n    \"dalvik.system;resolution:=optional,\" +\n    \"org.conscrypt;resolution:=optional,\" +\n    \"org.bouncycastle.*;resolution:=optional,\" +\n    \"org.openjsse.*;resolution:=optional,\" +\n    \"org.graalvm.nativeimage;resolution:=optional,\" +\n    \"org.graalvm.nativeimage.hosted;resolution:=optional,\" +\n    \"sun.security.ssl;resolution:=optional,*\",\n  \"Automatic-Module-Name: okhttp3\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3\",\n)\n\nval androidSignature by configurations.getting\nval jvmSignature by configurations.getting\nval checkstyleConfig by configurations.getting\n\n// Animal Sniffer confirms we generally don't use APIs not on Java 8.\nconfigure<AnimalSnifferExtension> {\n  annotation = \"okhttp3.internal.SuppressSignatureCheck\"\n  defaultTargets(\"jvmMain\", \"debug\")\n}\n\nproject.tasks.withType<AnimalSniffer> {\n  if (targetName == \"animalsnifferJvmMain\") {\n    animalsnifferSignatures = jvmSignature\n  } else {\n    animalsnifferSignatures = androidSignature\n  }\n}\n\nafterEvaluate {\n  tasks.withType<Test> {\n    if (javaLauncher\n        .get()\n        .metadata.languageVersion\n        .asInt() < 9\n    ) {\n      // Work around robolectric requirements and limitations\n      // https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/factory/AndroidUnitTest.java;l=339\n      allJvmArgs = allJvmArgs.filter { !it.startsWith(\"--add-opens\") }\n    }\n  }\n}\n\n// Work around issue 8826, where the Sentry SDK assumes that OkHttp's internal-visibility symbols\n// will be suffixed '$okhttp' in deployable artifacts. This isn't intended to be a published API,\n// but it's easy enough for us to keep it working. https://github.com/square/okhttp/issues/8826\ntasks.withType<KotlinCompile> {\n  compilerOptions {\n    freeCompilerArgs.addAll(\"-module-name=okhttp\", \"-Xexpect-actual-classes\")\n  }\n}\n"
  },
  {
    "path": "okhttp/okhttp3.pro",
    "content": "# JSR 305 annotations are for embedding nullability information.\n-dontwarn javax.annotation.**\n\n# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.\n-dontwarn org.codehaus.mojo.animal_sniffer.*\n\n# OkHttp platform used only on JVM and when Conscrypt and other security providers are available.\n# May be used with robolectric or deliberate use of Bouncy Castle on Android\n-dontwarn okhttp3.internal.platform.**\n-dontwarn org.conscrypt.**\n-dontwarn org.bouncycastle.**\n"
  },
  {
    "path": "okhttp/src/androidHostTest/kotlin/okhttp3/internal/publicsuffix/PublicSuffixTesting.android.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport androidx.test.core.app.ApplicationProvider\nimport okhttp3.internal.platform.PlatformRegistry\nimport org.robolectric.RobolectricTestRunner\n\nactual typealias PublicSuffixTestRunner = RobolectricTestRunner\n\nactual fun beforePublicSuffixTest() {\n  PlatformRegistry.applicationContext = ApplicationProvider.getApplicationContext()\n}\n"
  },
  {
    "path": "okhttp/src/androidHostTest/resources/okhttp3/robolectric.properties",
    "content": "# Robolectric on API 36 requires JDK 21\nsdk=35\n"
  },
  {
    "path": "okhttp/src/androidMain/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" xmlns:tools=\"http://schemas.android.com/tools\"\n          package=\"okhttp.okhttp3\">\n\n  <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n  <application>\n    <provider\n      android:name=\"androidx.startup.InitializationProvider\"\n      android:authorities=\"${applicationId}.androidx-startup\"\n      android:exported=\"false\"\n      tools:node=\"merge\">\n      <meta-data android:name=\"okhttp3.internal.platform.PlatformInitializer\"\n                 android:value=\"androidx.startup\"/>\n    </provider>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "okhttp/src/androidMain/baseline-prof.txt",
    "content": "HSPLandroidx/arch/core/executor/ArchTaskExecutor;-><init>()V\nHSPLandroidx/arch/core/executor/ArchTaskExecutor;->getInstance()Landroidx/arch/core/executor/ArchTaskExecutor;\nHSPLandroidx/arch/core/executor/DefaultTaskExecutor$1;-><init>(Landroidx/arch/core/executor/DefaultTaskExecutor;)V\nHSPLandroidx/arch/core/executor/DefaultTaskExecutor;-><init>()V\nHSPLandroidx/arch/core/executor/DefaultTaskExecutor;->isMainThread()Z\nHSPLandroidx/arch/core/executor/TaskExecutor;-><init>()V\nHSPLandroidx/arch/core/internal/FastSafeIterableMap;-><init>()V\nHSPLandroidx/arch/core/internal/FastSafeIterableMap;->contains(Ljava/lang/Object;)Z\nHSPLandroidx/arch/core/internal/FastSafeIterableMap;->get(Ljava/lang/Object;)Landroidx/arch/core/internal/SafeIterableMap$Entry;\nHSPLandroidx/arch/core/internal/FastSafeIterableMap;->putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\nHSPLandroidx/arch/core/internal/SafeIterableMap$Entry;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V\nHSPLandroidx/arch/core/internal/SafeIterableMap$Entry;->getKey()Ljava/lang/Object;\nHSPLandroidx/arch/core/internal/SafeIterableMap$Entry;->getValue()Ljava/lang/Object;\nHSPLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;-><init>(Landroidx/arch/core/internal/SafeIterableMap;)V\nHSPLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;->hasNext()Z\nHSPLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;->next()Ljava/lang/Object;\nHSPLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;->supportRemove(Landroidx/arch/core/internal/SafeIterableMap$Entry;)V\nHSPLandroidx/arch/core/internal/SafeIterableMap;-><init>()V\nHSPLandroidx/arch/core/internal/SafeIterableMap;->get(Ljava/lang/Object;)Landroidx/arch/core/internal/SafeIterableMap$Entry;\nHSPLandroidx/arch/core/internal/SafeIterableMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Landroidx/arch/core/internal/SafeIterableMap$Entry;\nHSPLandroidx/arch/core/internal/SafeIterableMap;->putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\nHSPLandroidx/core/app/ComponentActivity;-><init>()V\nHSPLandroidx/core/app/ComponentActivity;->onCreate(Landroid/os/Bundle;)V\nHSPLandroidx/core/app/CoreComponentFactory;-><init>()V\nHSPLandroidx/core/app/CoreComponentFactory;->checkCompatWrapper(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLandroidx/core/app/CoreComponentFactory;->instantiateActivity(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/app/Activity;\nHSPLandroidx/core/app/CoreComponentFactory;->instantiateApplication(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/app/Application;\nHSPLandroidx/core/app/CoreComponentFactory;->instantiateProvider(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/content/ContentProvider;\nHSPLandroidx/core/view/MenuHostHelper;-><init>(Ljava/lang/Runnable;)V\nHSPLandroidx/lifecycle/DispatchQueue$dispatchAndEnqueue$$inlined$with$lambda$1;-><init>(Landroidx/lifecycle/DispatchQueue;Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V\nHSPLandroidx/lifecycle/DispatchQueue$dispatchAndEnqueue$$inlined$with$lambda$1;->run()V\nHSPLandroidx/lifecycle/DispatchQueue;-><init>()V\nHSPLandroidx/lifecycle/DispatchQueue;->canRun()Z\nHSPLandroidx/lifecycle/DispatchQueue;->drainQueue()V\nHSPLandroidx/lifecycle/DispatchQueue;->enqueue(Ljava/lang/Runnable;)V\nHSPLandroidx/lifecycle/Lifecycle$1;-><clinit>()V\nHSPLandroidx/lifecycle/Lifecycle$Event;-><clinit>()V\nHSPLandroidx/lifecycle/Lifecycle$Event;-><init>(Ljava/lang/String;I)V\nHSPLandroidx/lifecycle/Lifecycle$Event;->getTargetState()Landroidx/lifecycle/Lifecycle$State;\nHSPLandroidx/lifecycle/Lifecycle$Event;->upFrom(Landroidx/lifecycle/Lifecycle$State;)Landroidx/lifecycle/Lifecycle$Event;\nHSPLandroidx/lifecycle/Lifecycle$Event;->values()[Landroidx/lifecycle/Lifecycle$Event;\nHSPLandroidx/lifecycle/Lifecycle$State;-><clinit>()V\nHSPLandroidx/lifecycle/Lifecycle$State;-><init>(Ljava/lang/String;I)V\nHSPLandroidx/lifecycle/Lifecycle$State;->values()[Landroidx/lifecycle/Lifecycle$State;\nHSPLandroidx/lifecycle/Lifecycle;-><init>()V\nHSPLandroidx/lifecycle/LifecycleController$observer$1;-><init>(Landroidx/lifecycle/LifecycleController;Lkotlinx/coroutines/Job;)V\nHSPLandroidx/lifecycle/LifecycleController$observer$1;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V\nHSPLandroidx/lifecycle/LifecycleController;-><init>(Landroidx/lifecycle/Lifecycle;Landroidx/lifecycle/Lifecycle$State;Landroidx/lifecycle/DispatchQueue;Lkotlinx/coroutines/Job;)V\nHSPLandroidx/lifecycle/LifecycleController;->finish()V\nHSPLandroidx/lifecycle/LifecycleCoroutineScope$launchWhenResumed$1;-><init>(Landroidx/lifecycle/LifecycleCoroutineScope;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V\nHSPLandroidx/lifecycle/LifecycleCoroutineScope$launchWhenResumed$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;\nHSPLandroidx/lifecycle/LifecycleCoroutineScope$launchWhenResumed$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLandroidx/lifecycle/LifecycleCoroutineScope;-><init>()V\nHSPLandroidx/lifecycle/LifecycleCoroutineScope;->launchWhenResumed(Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;\nHSPLandroidx/lifecycle/LifecycleCoroutineScopeImpl$register$1;-><init>(Landroidx/lifecycle/LifecycleCoroutineScopeImpl;Lkotlin/coroutines/Continuation;)V\nHSPLandroidx/lifecycle/LifecycleCoroutineScopeImpl$register$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;\nHSPLandroidx/lifecycle/LifecycleCoroutineScopeImpl$register$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLandroidx/lifecycle/LifecycleCoroutineScopeImpl;-><init>(Landroidx/lifecycle/Lifecycle;Lkotlin/coroutines/CoroutineContext;)V\nHSPLandroidx/lifecycle/LifecycleCoroutineScopeImpl;->getCoroutineContext()Lkotlin/coroutines/CoroutineContext;\nHSPLandroidx/lifecycle/LifecycleCoroutineScopeImpl;->getLifecycle$lifecycle_runtime_ktx_release()Landroidx/lifecycle/Lifecycle;\nHSPLandroidx/lifecycle/LifecycleCoroutineScopeImpl;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V\nHSPLandroidx/lifecycle/LifecycleKt;->convertDurationUnitOverflow(JLkotlin/time/DurationUnit;Lkotlin/time/DurationUnit;)J\nHSPLandroidx/lifecycle/LifecycleKt;->createCoroutineUnintercepted(Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;\nHSPLandroidx/lifecycle/LifecycleKt;->intercepted(Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;\nHSPLandroidx/lifecycle/LifecycleOwnerKt;->getLifecycleScope(Landroidx/lifecycle/LifecycleOwner;)Landroidx/lifecycle/LifecycleCoroutineScope;\nHSPLandroidx/lifecycle/LifecycleRegistry$ObserverWithState;-><init>(Landroidx/lifecycle/LifecycleObserver;Landroidx/lifecycle/Lifecycle$State;)V\nHSPLandroidx/lifecycle/LifecycleRegistry$ObserverWithState;->dispatchEvent(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V\nHSPLandroidx/lifecycle/LifecycleRegistry;-><init>(Landroidx/lifecycle/LifecycleOwner;)V\nHSPLandroidx/lifecycle/LifecycleRegistry;->addObserver(Landroidx/lifecycle/LifecycleObserver;)V\nHSPLandroidx/lifecycle/LifecycleRegistry;->calculateTargetState(Landroidx/lifecycle/LifecycleObserver;)Landroidx/lifecycle/Lifecycle$State;\nHSPLandroidx/lifecycle/LifecycleRegistry;->enforceMainThreadIfNeeded(Ljava/lang/String;)V\nHSPLandroidx/lifecycle/LifecycleRegistry;->min(Landroidx/lifecycle/Lifecycle$State;Landroidx/lifecycle/Lifecycle$State;)Landroidx/lifecycle/Lifecycle$State;\nHSPLandroidx/lifecycle/LifecycleRegistry;->moveToState(Landroidx/lifecycle/Lifecycle$State;)V\nHSPLandroidx/lifecycle/LifecycleRegistry;->popParentState()V\nHSPLandroidx/lifecycle/LifecycleRegistry;->removeObserver(Landroidx/lifecycle/LifecycleObserver;)V\nHSPLandroidx/lifecycle/LifecycleRegistry;->sync()V\nHSPLandroidx/lifecycle/Lifecycling;-><clinit>()V\nHSPLandroidx/lifecycle/LiveData$ObserverWrapper;-><init>()V\nHSPLandroidx/lifecycle/PausingDispatcher;-><init>()V\nHSPLandroidx/lifecycle/PausingDispatcher;->dispatch(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V\nHSPLandroidx/lifecycle/PausingDispatcher;->isDispatchNeeded(Lkotlin/coroutines/CoroutineContext;)Z\nHSPLandroidx/lifecycle/PausingDispatcherKt$whenStateAtLeast$2;-><init>(Landroidx/lifecycle/Lifecycle;Landroidx/lifecycle/Lifecycle$State;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V\nHSPLandroidx/lifecycle/PausingDispatcherKt$whenStateAtLeast$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\nHSPLandroidx/lifecycle/PausingDispatcherKt$whenStateAtLeast$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;-><init>()V\nHSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPostCreated(Landroid/app/Activity;Landroid/os/Bundle;)V\nHSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPostResumed(Landroid/app/Activity;)V\nHSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPostStarted(Landroid/app/Activity;)V\nHSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityResumed(Landroid/app/Activity;)V\nHSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityStarted(Landroid/app/Activity;)V\nHSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->registerIn(Landroid/app/Activity;)V\nHSPLandroidx/lifecycle/ReportFragment;-><init>()V\nHSPLandroidx/lifecycle/ReportFragment;->dispatch(Landroid/app/Activity;Landroidx/lifecycle/Lifecycle$Event;)V\nHSPLandroidx/lifecycle/ReportFragment;->dispatch(Landroidx/lifecycle/Lifecycle$Event;)V\nHSPLandroidx/lifecycle/ReportFragment;->injectIfNeededIn(Landroid/app/Activity;)V\nHSPLandroidx/lifecycle/ReportFragment;->onActivityCreated(Landroid/os/Bundle;)V\nHSPLandroidx/lifecycle/ReportFragment;->onResume()V\nHSPLandroidx/lifecycle/ReportFragment;->onStart()V\nHSPLandroidx/lifecycle/runtime/R$id;->iterator([Ljava/lang/Object;)Ljava/util/Iterator;\nHSPLandroidx/lifecycle/runtime/R$id;->toCanonicalHost(Ljava/lang/String;)Ljava/lang/String;\nHSPLandroidx/profileinstaller/FileSectionType$EnumUnboxingSharedUtility;-><clinit>()V\nHSPLandroidx/profileinstaller/FileSectionType$EnumUnboxingSharedUtility;->checkNotZero(I)V\nHSPLandroidx/profileinstaller/FileSectionType$EnumUnboxingSharedUtility;->ordinal(I)I\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;-><init>(Landroid/content/Context;)V\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;-><init>(Landroidx/profileinstaller/ProfileInstallerInitializer;Landroid/content/Context;)V\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;->run()V\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda0;-><init>(Ljava/lang/Runnable;)V\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda0;->doFrame(J)V\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;->postFrameCallback(Ljava/lang/Runnable;)V\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer$Handler28Impl;->createAsync(Landroid/os/Looper;)Landroid/os/Handler;\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer$Result;-><init>()V\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer;-><init>()V\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer;->create(Landroid/content/Context;)Ljava/lang/Object;\nHSPLandroidx/profileinstaller/ProfileInstallerInitializer;->dependencies()Ljava/util/List;\nHSPLandroidx/savedstate/Recreator;-><init>(Landroidx/savedstate/SavedStateRegistryOwner;)V\nHSPLandroidx/savedstate/Recreator;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V\nHSPLandroidx/savedstate/SavedStateRegistry$1;-><init>(Landroidx/savedstate/SavedStateRegistry;)V\nHSPLandroidx/savedstate/SavedStateRegistry$1;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V\nHSPLandroidx/savedstate/SavedStateRegistry;-><init>()V\nHSPLandroidx/savedstate/SavedStateRegistry;->consumeRestoredStateForKey(Ljava/lang/String;)Landroid/os/Bundle;\nHSPLandroidx/savedstate/SavedStateRegistry;->registerSavedStateProvider(Ljava/lang/String;Landroidx/savedstate/SavedStateRegistry$SavedStateProvider;)V\nHSPLandroidx/savedstate/SavedStateRegistryController;-><init>(Landroidx/savedstate/SavedStateRegistryOwner;)V\nHSPLandroidx/savedstate/SavedStateRegistryController;->performRestore(Landroid/os/Bundle;)V\nHSPLandroidx/startup/AppInitializer;-><clinit>()V\nHSPLandroidx/startup/AppInitializer;-><init>(Landroid/content/Context;)V\nHSPLandroidx/startup/AppInitializer;->doInitialize(Ljava/lang/Class;Ljava/util/Set;)Ljava/lang/Object;\nHSPLandroidx/startup/InitializationProvider;-><init>()V\nHSPLandroidx/startup/InitializationProvider;->onCreate()Z\nHSPLandroidx/tracing/Trace;->isEnabled()Z\nHSPLkotlin/Result$Failure;-><init>(Ljava/lang/Throwable;)V\nHSPLkotlin/Result;->exceptionOrNull-impl(Ljava/lang/Object;)Ljava/lang/Throwable;\nHSPLkotlin/ResultKt;->createFailure(Ljava/lang/Throwable;)Ljava/lang/Object;\nHSPLkotlin/ResultKt;->throwOnFailure(Ljava/lang/Object;)V\nHSPLkotlin/SynchronizedLazyImpl;-><init>(Lkotlin/jvm/functions/Function0;Ljava/lang/Object;I)V\nHSPLkotlin/TuplesKt;-><clinit>()V\nHSPLkotlin/TuplesKt;->launch$default(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;\nHSPLkotlin/TuplesKt;->withContext(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;\nHSPLkotlin/UNINITIALIZED_VALUE;-><clinit>()V\nHSPLkotlin/UNINITIALIZED_VALUE;-><init>()V\nHSPLkotlin/Unit;-><clinit>()V\nHSPLkotlin/Unit;-><init>()V\nHSPLkotlin/collections/AbstractCollection;-><init>()V\nHSPLkotlin/collections/AbstractList;-><init>()V\nHSPLkotlin/collections/AbstractMutableList;-><init>()V\nHSPLkotlin/collections/ArrayAsCollection;-><init>([Ljava/lang/Object;Z)V\nHSPLkotlin/collections/ArrayAsCollection;->toArray()[Ljava/lang/Object;\nHSPLkotlin/collections/ArraysKt___ArraysKt;->asList([Ljava/lang/Object;)Ljava/util/List;\nHSPLkotlin/collections/ArraysKt___ArraysKt;->copyInto$default([B[BIIII)[B\nHSPLkotlin/collections/ArraysKt___ArraysKt;->copyInto([B[BIII)[B\nHSPLkotlin/collections/ArraysKt___ArraysKt;->copyInto([Ljava/lang/Object;[Ljava/lang/Object;III)[Ljava/lang/Object;\nHSPLkotlin/collections/ArraysKt___ArraysKt;->fill([Ljava/lang/Object;Ljava/lang/Object;II)V\nHSPLkotlin/collections/ArraysKt___ArraysKt;->getLastIndex([Ljava/lang/Object;)I\nHSPLkotlin/collections/CollectionsKt__IteratorsJVMKt;->collectionSizeOrDefault(Ljava/lang/Iterable;I)I\nHSPLkotlin/collections/CollectionsKt__MutableCollectionsJVMKt;->sort(Ljava/util/List;)V\nHSPLkotlin/collections/CollectionsKt__ReversedViewsKt;->addAll(Ljava/util/Collection;Ljava/lang/Iterable;)Z\nHSPLkotlin/collections/CollectionsKt___CollectionsKt;->toList(Ljava/lang/Iterable;)Ljava/util/List;\nHSPLkotlin/collections/CollectionsKt___CollectionsKt;->toMutableList(Ljava/util/Collection;)Ljava/util/List;\nHSPLkotlin/collections/CollectionsKt___CollectionsKt;->toSet(Ljava/lang/Iterable;)Ljava/util/Set;\nHSPLkotlin/collections/EmptyIterator;-><clinit>()V\nHSPLkotlin/collections/EmptyIterator;-><init>()V\nHSPLkotlin/collections/EmptyIterator;->hasNext()Z\nHSPLkotlin/collections/EmptyList;-><clinit>()V\nHSPLkotlin/collections/EmptyList;-><init>()V\nHSPLkotlin/collections/EmptyList;->isEmpty()Z\nHSPLkotlin/collections/EmptyList;->iterator()Ljava/util/Iterator;\nHSPLkotlin/collections/EmptyMap;-><clinit>()V\nHSPLkotlin/collections/EmptyMap;-><init>()V\nHSPLkotlin/collections/EmptyMap;->isEmpty()Z\nHSPLkotlin/collections/EmptySet;-><clinit>()V\nHSPLkotlin/collections/EmptySet;-><init>()V\nHSPLkotlin/collections/EmptySet;->equals(Ljava/lang/Object;)Z\nHSPLkotlin/collections/EmptySet;->hashCode()I\nHSPLkotlin/collections/EmptySet;->isEmpty()Z\nHSPLkotlin/collections/EmptySet;->iterator()Ljava/util/Iterator;\nHSPLkotlin/collections/MapsKt___MapsKt;->toMap(Ljava/util/Map;)Ljava/util/Map;\nHSPLkotlin/collections/SetsKt__SetsKt;->build(Ljava/util/List;)Ljava/util/List;\nHSPLkotlin/collections/SetsKt__SetsKt;->listOf(Ljava/lang/Object;)Ljava/util/List;\nHSPLkotlin/collections/SetsKt__SetsKt;->listOfNotNull([Ljava/lang/Object;)Ljava/util/List;\nHSPLkotlin/collections/SetsKt__SetsKt;->optimizeReadOnlyList(Ljava/util/List;)Ljava/util/List;\nHSPLkotlin/collections/builders/ListBuilder$Itr;-><init>(Lkotlin/collections/builders/ListBuilder;I)V\nHSPLkotlin/collections/builders/ListBuilder$Itr;->hasNext()Z\nHSPLkotlin/collections/builders/ListBuilder$Itr;->next()Ljava/lang/Object;\nHSPLkotlin/collections/builders/ListBuilder;-><init>()V\nHSPLkotlin/collections/builders/ListBuilder;->addAtInternal(ILjava/lang/Object;)V\nHSPLkotlin/collections/builders/ListBuilder;->checkIsMutable()V\nHSPLkotlin/collections/builders/ListBuilder;->insertAtInternal(II)V\nHSPLkotlin/collections/builders/ListBuilder;->iterator()Ljava/util/Iterator;\nHSPLkotlin/comparisons/NaturalOrderComparator;-><clinit>()V\nHSPLkotlin/comparisons/NaturalOrderComparator;-><init>()V\nHSPLkotlin/comparisons/NaturalOrderComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I\nHSPLkotlin/coroutines/AbstractCoroutineContextElement;-><init>(Lkotlin/coroutines/CoroutineContext$Key;)V\nHSPLkotlin/coroutines/AbstractCoroutineContextElement;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;\nHSPLkotlin/coroutines/AbstractCoroutineContextElement;->getKey()Lkotlin/coroutines/CoroutineContext$Key;\nHSPLkotlin/coroutines/AbstractCoroutineContextKey;-><init>(Lkotlin/coroutines/CoroutineContext$Key;Lkotlin/jvm/functions/Function1;)V\nHSPLkotlin/coroutines/CombinedContext;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/CoroutineContext$Element;)V\nHSPLkotlin/coroutines/CombinedContext;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;\nHSPLkotlin/coroutines/CombinedContext;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;\nHSPLkotlin/coroutines/CombinedContext;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;\nHSPLkotlin/coroutines/CombinedContext;->plus(Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;\nHSPLkotlin/coroutines/ContinuationInterceptor$Key;-><clinit>()V\nHSPLkotlin/coroutines/ContinuationInterceptor$Key;-><init>()V\nHSPLkotlin/coroutines/CoroutineContext$DefaultImpls;->plus(Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;\nHSPLkotlin/coroutines/CoroutineContext$Element$DefaultImpls;->fold(Lkotlin/coroutines/CoroutineContext$Element;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;\nHSPLkotlin/coroutines/CoroutineContext$Element$DefaultImpls;->get(Lkotlin/coroutines/CoroutineContext$Element;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;\nHSPLkotlin/coroutines/CoroutineContext$Element$DefaultImpls;->minusKey(Lkotlin/coroutines/CoroutineContext$Element;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;\nHSPLkotlin/coroutines/CoroutineContext$Element$DefaultImpls;->plus(Lkotlin/coroutines/CoroutineContext$Element;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;\nHSPLkotlin/coroutines/CoroutineContext$plus$1;-><clinit>()V\nHSPLkotlin/coroutines/CoroutineContext$plus$1;-><init>()V\nHSPLkotlin/coroutines/CoroutineContext$plus$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlin/coroutines/EmptyCoroutineContext;-><clinit>()V\nHSPLkotlin/coroutines/EmptyCoroutineContext;-><init>()V\nHSPLkotlin/coroutines/intrinsics/CoroutineSingletons;-><clinit>()V\nHSPLkotlin/coroutines/intrinsics/CoroutineSingletons;-><init>(Ljava/lang/String;I)V\nHSPLkotlin/coroutines/jvm/internal/BaseContinuationImpl;-><init>(Lkotlin/coroutines/Continuation;)V\nHSPLkotlin/coroutines/jvm/internal/BaseContinuationImpl;->resumeWith(Ljava/lang/Object;)V\nHSPLkotlin/coroutines/jvm/internal/CompletedContinuation;-><clinit>()V\nHSPLkotlin/coroutines/jvm/internal/CompletedContinuation;-><init>()V\nHSPLkotlin/coroutines/jvm/internal/ContinuationImpl;-><init>(Lkotlin/coroutines/Continuation;)V\nHSPLkotlin/coroutines/jvm/internal/ContinuationImpl;->getContext()Lkotlin/coroutines/CoroutineContext;\nHSPLkotlin/coroutines/jvm/internal/ContinuationImpl;->releaseIntercepted()V\nHSPLkotlin/coroutines/jvm/internal/SuspendLambda;-><init>(ILkotlin/coroutines/Continuation;)V\nHSPLkotlin/coroutines/jvm/internal/SuspendLambda;->getArity()I\nHSPLkotlin/internal/PlatformImplementations;-><init>()V\nHSPLkotlin/internal/PlatformImplementations;->defaultPlatformRandom()Lkotlin/random/Random;\nHSPLkotlin/internal/PlatformImplementationsKt;-><clinit>()V\nHSPLkotlin/io/CloseableKt;->closeFinally(Ljava/io/Closeable;Ljava/lang/Throwable;)V\nHSPLkotlin/jvm/internal/ArrayIterator;-><init>([Ljava/lang/Object;)V\nHSPLkotlin/jvm/internal/ArrayIterator;->hasNext()Z\nHSPLkotlin/jvm/internal/ArrayIterator;->next()Ljava/lang/Object;\nHSPLkotlin/jvm/internal/Intrinsics;->areEqual(Ljava/lang/Object;Ljava/lang/Object;)Z\nHSPLkotlin/jvm/internal/Intrinsics;->checkNotNull(Ljava/lang/Object;)V\nHSPLkotlin/jvm/internal/Intrinsics;->checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V\nHSPLkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V\nHSPLkotlin/jvm/internal/Intrinsics;->compare(II)I\nHSPLkotlin/jvm/internal/Intrinsics;->stringPlus(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;\nHSPLkotlin/jvm/internal/Lambda;-><init>(I)V\nHSPLkotlin/jvm/internal/Ref$ObjectRef;-><init>()V\nHSPLkotlin/jvm/internal/TypeIntrinsics;->beforeCheckcastToFunctionOfArity(Ljava/lang/Object;I)Ljava/lang/Object;\nHSPLkotlin/random/AbstractPlatformRandom;-><init>()V\nHSPLkotlin/random/AbstractPlatformRandom;->nextInt()I\nHSPLkotlin/random/FallbackThreadLocalRandom$implStorage$1;-><init>()V\nHSPLkotlin/random/FallbackThreadLocalRandom$implStorage$1;->initialValue()Ljava/lang/Object;\nHSPLkotlin/random/FallbackThreadLocalRandom;-><init>()V\nHSPLkotlin/random/FallbackThreadLocalRandom;->getImpl()Ljava/util/Random;\nHSPLkotlin/random/Random$Default;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLkotlin/random/Random;-><clinit>()V\nHSPLkotlin/random/Random;-><init>()V\nHSPLkotlin/ranges/IntProgression;-><init>(III)V\nHSPLkotlin/ranges/IntRange;-><clinit>()V\nHSPLkotlin/ranges/IntRange;-><init>(II)V\nHSPLkotlin/ranges/RangesKt___RangesKt;->step(Lkotlin/ranges/IntProgression;I)Lkotlin/ranges/IntProgression;\nHSPLkotlin/ranges/RangesKt___RangesKt;->until(II)Lkotlin/ranges/IntRange;\nHSPLkotlin/sequences/ConstrainedOnceSequence;-><init>(Lkotlin/sequences/Sequence;)V\nHSPLkotlin/sequences/ConstrainedOnceSequence;->iterator()Ljava/util/Iterator;\nHSPLkotlin/sequences/SequencesKt;->toList(Lkotlin/sequences/Sequence;)Ljava/util/List;\nHSPLkotlin/sequences/SequencesKt__SequencesKt$asSequence$$inlined$Sequence$1;-><init>(Ljava/util/Iterator;)V\nHSPLkotlin/sequences/SequencesKt__SequencesKt$asSequence$$inlined$Sequence$1;->iterator()Ljava/util/Iterator;\nHSPLkotlin/sequences/SequencesKt___SequencesJvmKt;->asSequence(Ljava/util/Iterator;)Lkotlin/sequences/Sequence;\nHSPLkotlin/text/CharsKt__CharKt;->checkRadix(I)I\nHSPLkotlin/text/CharsKt__CharKt;->equals(CCZ)Z\nHSPLkotlin/text/CharsKt__CharKt;->isWhitespace(C)Z\nHSPLkotlin/text/Charsets;-><clinit>()V\nHSPLkotlin/text/MatcherMatchResult$groupValues$1;-><init>(Lkotlin/text/MatcherMatchResult;)V\nHSPLkotlin/text/MatcherMatchResult$groupValues$1;->get(I)Ljava/lang/Object;\nHSPLkotlin/text/MatcherMatchResult$groups$1;-><init>(Lkotlin/text/MatcherMatchResult;)V\nHSPLkotlin/text/MatcherMatchResult;-><init>(Ljava/util/regex/Matcher;Ljava/lang/CharSequence;)V\nHSPLkotlin/text/MatcherMatchResult;->getRange()Lkotlin/ranges/IntRange;\nHSPLkotlin/text/Regex;-><init>(Ljava/lang/String;)V\nHSPLkotlin/text/Regex;->matches(Ljava/lang/CharSequence;)Z\nHSPLkotlin/text/StringsKt__StringNumberConversionsKt;->toIntOrNull(Ljava/lang/String;)Ljava/lang/Integer;\nHSPLkotlin/text/StringsKt__StringsJVMKt;->endsWith$default(Ljava/lang/String;Ljava/lang/String;ZI)Z\nHSPLkotlin/text/StringsKt__StringsJVMKt;->endsWith(Ljava/lang/String;Ljava/lang/String;Z)Z\nHSPLkotlin/text/StringsKt__StringsJVMKt;->equals(Ljava/lang/String;Ljava/lang/String;Z)Z\nHSPLkotlin/text/StringsKt__StringsJVMKt;->regionMatches(Ljava/lang/String;ILjava/lang/String;IIZ)Z\nHSPLkotlin/text/StringsKt__StringsJVMKt;->replace$default(Ljava/lang/String;CCZI)Ljava/lang/String;\nHSPLkotlin/text/StringsKt__StringsJVMKt;->startsWith$default(Ljava/lang/String;Ljava/lang/String;ZI)Z\nHSPLkotlin/text/StringsKt__StringsJVMKt;->startsWith(Ljava/lang/String;Ljava/lang/String;IZ)Z\nHSPLkotlin/text/StringsKt__StringsJVMKt;->startsWith(Ljava/lang/String;Ljava/lang/String;Z)Z\nHSPLkotlin/text/StringsKt__StringsKt;->contains$default(Ljava/lang/CharSequence;CZI)Z\nHSPLkotlin/text/StringsKt__StringsKt;->contains$default(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZI)Z\nHSPLkotlin/text/StringsKt__StringsKt;->endsWith$default(Ljava/lang/CharSequence;CZI)Z\nHSPLkotlin/text/StringsKt__StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I\nHSPLkotlin/text/StringsKt__StringsKt;->indexOf$default(Ljava/lang/CharSequence;CIZI)I\nHSPLkotlin/text/StringsKt__StringsKt;->indexOf$default(Ljava/lang/CharSequence;Ljava/lang/String;IZI)I\nHSPLkotlin/text/StringsKt__StringsKt;->indexOf(Ljava/lang/CharSequence;Ljava/lang/String;IZ)I\nHSPLkotlin/text/StringsKt__StringsKt;->removePrefix(Ljava/lang/String;Ljava/lang/CharSequence;)Ljava/lang/String;\nHSPLkotlin/text/StringsKt__StringsKt;->trim(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;\nHSPLkotlin/time/Duration;-><clinit>()V\nHSPLkotlin/time/Duration;->toLong-impl(JLkotlin/time/DurationUnit;)J\nHSPLkotlin/time/DurationJvmKt;-><clinit>()V\nHSPLkotlin/time/DurationUnit;-><clinit>()V\nHSPLkotlin/time/DurationUnit;-><init>(Ljava/lang/String;ILjava/util/concurrent/TimeUnit;)V\nHSPLkotlinx/coroutines/AbstractCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;ZZ)V\nHSPLkotlinx/coroutines/AbstractCoroutine;->afterResume(Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/AbstractCoroutine;->getContext()Lkotlin/coroutines/CoroutineContext;\nHSPLkotlinx/coroutines/AbstractCoroutine;->getCoroutineContext()Lkotlin/coroutines/CoroutineContext;\nHSPLkotlinx/coroutines/AbstractCoroutine;->isActive()Z\nHSPLkotlinx/coroutines/AbstractCoroutine;->onCompletionInternal(Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/AbstractCoroutine;->resumeWith(Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/Active;-><clinit>()V\nHSPLkotlinx/coroutines/Active;-><init>()V\nHSPLkotlinx/coroutines/BlockingEventLoop;-><init>(Ljava/lang/Thread;)V\nHSPLkotlinx/coroutines/CancelHandler;-><init>()V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;-><clinit>()V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;-><init>(Lkotlin/coroutines/Continuation;I)V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->detachChild$kotlinx_coroutines_core()V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->detachChildIfNonResuable()V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->dispatchResume(I)V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->getDelegate$kotlinx_coroutines_core()Lkotlin/coroutines/Continuation;\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->getExceptionalResult$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Throwable;\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->getResult()Ljava/lang/Object;\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->getSuccessfulResult$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->initCancellability()V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->installParentHandle()Lkotlinx/coroutines/DisposableHandle;\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->invokeOnCancellation(Lkotlin/jvm/functions/Function1;)V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->isReusable()Z\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->resume(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->resumeImpl(Ljava/lang/Object;ILkotlin/jvm/functions/Function1;)V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->resumeWith(Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/CancellableContinuationImpl;->takeState$kotlinx_coroutines_core()Ljava/lang/Object;\nHSPLkotlinx/coroutines/ChildContinuation;-><init>(Lkotlinx/coroutines/CancellableContinuationImpl;)V\nHSPLkotlinx/coroutines/ChildHandleNode;-><init>(Lkotlinx/coroutines/ChildJob;)V\nHSPLkotlinx/coroutines/CompletedContinuation;-><init>(Ljava/lang/Object;Lkotlinx/coroutines/CancelHandler;Lkotlin/jvm/functions/Function1;Ljava/lang/Object;Ljava/lang/Throwable;I)V\nHSPLkotlinx/coroutines/CompletedExceptionally;-><clinit>()V\nHSPLkotlinx/coroutines/CompletedExceptionally;-><init>(Ljava/lang/Throwable;ZI)V\nHSPLkotlinx/coroutines/CompletionHandlerBase;-><init>()V\nHSPLkotlinx/coroutines/CompletionStateKt;->recoverResult(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/CompletionStateKt;->toState(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/CoroutineContextKt$foldCopiesForChildCoroutine$hasToCopy$1;-><clinit>()V\nHSPLkotlinx/coroutines/CoroutineContextKt$foldCopiesForChildCoroutine$hasToCopy$1;-><init>()V\nHSPLkotlinx/coroutines/CoroutineContextKt$foldCopiesForChildCoroutine$hasToCopy$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/CoroutineDispatcher$Key$1;-><clinit>()V\nHSPLkotlinx/coroutines/CoroutineDispatcher$Key$1;-><init>()V\nHSPLkotlinx/coroutines/CoroutineDispatcher$Key;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLkotlinx/coroutines/CoroutineDispatcher;-><clinit>()V\nHSPLkotlinx/coroutines/CoroutineDispatcher;-><init>()V\nHSPLkotlinx/coroutines/CoroutineDispatcher;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;\nHSPLkotlinx/coroutines/CoroutineDispatcher;->interceptContinuation(Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;\nHSPLkotlinx/coroutines/CoroutineDispatcher;->isDispatchNeeded(Lkotlin/coroutines/CoroutineContext;)Z\nHSPLkotlinx/coroutines/CoroutineDispatcher;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;\nHSPLkotlinx/coroutines/CoroutineDispatcher;->releaseInterceptedContinuation(Lkotlin/coroutines/Continuation;)V\nHSPLkotlinx/coroutines/CoroutineExceptionHandler$Key;-><clinit>()V\nHSPLkotlinx/coroutines/CoroutineExceptionHandler$Key;-><init>()V\nHSPLkotlinx/coroutines/CoroutineExceptionHandlerImplKt$$ExternalSyntheticServiceLoad0;->m()Ljava/util/Iterator;\nHSPLkotlinx/coroutines/CoroutineExceptionHandlerImplKt;-><clinit>()V\nHSPLkotlinx/coroutines/DefaultExecutorKt;-><clinit>()V\nHSPLkotlinx/coroutines/DispatchedCoroutine;-><clinit>()V\nHSPLkotlinx/coroutines/DispatchedCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/Continuation;)V\nHSPLkotlinx/coroutines/DispatchedCoroutine;->afterResume(Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/DispatchedCoroutine;->getResult()Ljava/lang/Object;\nHSPLkotlinx/coroutines/DispatchedTask;-><init>(I)V\nHSPLkotlinx/coroutines/DispatchedTask;->getExceptionalResult$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Throwable;\nHSPLkotlinx/coroutines/DispatchedTask;->getSuccessfulResult$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/DispatchedTask;->handleFatalException(Ljava/lang/Throwable;Ljava/lang/Throwable;)V\nHSPLkotlinx/coroutines/DispatchedTask;->run()V\nHSPLkotlinx/coroutines/DispatchedTaskKt;->isCancellableMode(I)Z\nHSPLkotlinx/coroutines/Dispatchers;-><clinit>()V\nHSPLkotlinx/coroutines/Empty;-><init>(Z)V\nHSPLkotlinx/coroutines/Empty;->getList()Lkotlinx/coroutines/NodeList;\nHSPLkotlinx/coroutines/Empty;->isActive()Z\nHSPLkotlinx/coroutines/EventLoop;-><init>()V\nHSPLkotlinx/coroutines/EventLoop;->decrementUseCount(Z)V\nHSPLkotlinx/coroutines/EventLoop;->delta(Z)J\nHSPLkotlinx/coroutines/EventLoop;->incrementUseCount(Z)V\nHSPLkotlinx/coroutines/EventLoop;->isUnconfinedLoopActive()Z\nHSPLkotlinx/coroutines/EventLoop;->processUnconfinedEvent()Z\nHSPLkotlinx/coroutines/EventLoopImplBase;-><clinit>()V\nHSPLkotlinx/coroutines/EventLoopImplBase;-><init>()V\nHSPLkotlinx/coroutines/EventLoopImplPlatform;-><init>()V\nHSPLkotlinx/coroutines/ExecutorCoroutineDispatcher;-><clinit>()V\nHSPLkotlinx/coroutines/ExecutorCoroutineDispatcher;-><init>()V\nHSPLkotlinx/coroutines/InvokeOnCancel;-><init>(Lkotlin/jvm/functions/Function1;)V\nHSPLkotlinx/coroutines/Job$DefaultImpls;->invokeOnCompletion$default(Lkotlinx/coroutines/Job;ZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/DisposableHandle;\nHSPLkotlinx/coroutines/Job$Key;-><clinit>()V\nHSPLkotlinx/coroutines/Job$Key;-><init>()V\nHSPLkotlinx/coroutines/Job;-><clinit>()V\nHSPLkotlinx/coroutines/JobCancellingNode;-><init>()V\nHSPLkotlinx/coroutines/JobImpl;-><init>(Lkotlinx/coroutines/Job;)V\nHSPLkotlinx/coroutines/JobNode;-><init>()V\nHSPLkotlinx/coroutines/JobNode;->dispose()V\nHSPLkotlinx/coroutines/JobNode;->getJob()Lkotlinx/coroutines/JobSupport;\nHSPLkotlinx/coroutines/JobNode;->getList()Lkotlinx/coroutines/NodeList;\nHSPLkotlinx/coroutines/JobSupport$addLastAtomic$$inlined$addLastIf$1;-><init>(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;Lkotlinx/coroutines/JobSupport;Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/JobSupport$addLastAtomic$$inlined$addLastIf$1;->prepare(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/JobSupport;-><clinit>()V\nHSPLkotlinx/coroutines/JobSupport;-><init>(Z)V\nHSPLkotlinx/coroutines/JobSupport;->addLastAtomic(Ljava/lang/Object;Lkotlinx/coroutines/NodeList;Lkotlinx/coroutines/JobNode;)Z\nHSPLkotlinx/coroutines/JobSupport;->afterCompletion(Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/JobSupport;->attachChild(Lkotlinx/coroutines/ChildJob;)Lkotlinx/coroutines/ChildHandle;\nHSPLkotlinx/coroutines/JobSupport;->completeStateFinalization(Lkotlinx/coroutines/Incomplete;Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/JobSupport;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/JobSupport;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;\nHSPLkotlinx/coroutines/JobSupport;->getKey()Lkotlin/coroutines/CoroutineContext$Key;\nHSPLkotlinx/coroutines/JobSupport;->getParentHandle$kotlinx_coroutines_core()Lkotlinx/coroutines/ChildHandle;\nHSPLkotlinx/coroutines/JobSupport;->getState$kotlinx_coroutines_core()Ljava/lang/Object;\nHSPLkotlinx/coroutines/JobSupport;->initParentJob(Lkotlinx/coroutines/Job;)V\nHSPLkotlinx/coroutines/JobSupport;->invokeOnCompletion(ZZLkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;\nHSPLkotlinx/coroutines/JobSupport;->isActive()Z\nHSPLkotlinx/coroutines/JobSupport;->makeCompletingOnce$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/JobSupport;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;\nHSPLkotlinx/coroutines/JobSupport;->promoteSingleToNodeList(Lkotlinx/coroutines/JobNode;)V\nHSPLkotlinx/coroutines/JobSupport;->start()Z\nHSPLkotlinx/coroutines/JobSupport;->tryMakeCompleting(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/JobSupportKt;-><clinit>()V\nHSPLkotlinx/coroutines/MainCoroutineDispatcher;-><init>()V\nHSPLkotlinx/coroutines/NodeList;-><init>()V\nHSPLkotlinx/coroutines/NodeList;->getList()Lkotlinx/coroutines/NodeList;\nHSPLkotlinx/coroutines/NonDisposableHandle;-><clinit>()V\nHSPLkotlinx/coroutines/NonDisposableHandle;-><init>()V\nHSPLkotlinx/coroutines/StandaloneCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;Z)V\nHSPLkotlinx/coroutines/SupervisorJobImpl;-><init>(Lkotlinx/coroutines/Job;)V\nHSPLkotlinx/coroutines/ThreadLocalEventLoop;-><clinit>()V\nHSPLkotlinx/coroutines/ThreadLocalEventLoop;->getEventLoop$kotlinx_coroutines_core()Lkotlinx/coroutines/EventLoop;\nHSPLkotlinx/coroutines/Unconfined;-><clinit>()V\nHSPLkotlinx/coroutines/Unconfined;-><init>()V\nHSPLkotlinx/coroutines/UndispatchedCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/Continuation;)V\nHSPLkotlinx/coroutines/UndispatchedCoroutine;->afterResume(Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/UndispatchedMarker;-><clinit>()V\nHSPLkotlinx/coroutines/UndispatchedMarker;-><init>()V\nHSPLkotlinx/coroutines/UndispatchedMarker;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/UndispatchedMarker;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;\nHSPLkotlinx/coroutines/UndispatchedMarker;->getKey()Lkotlin/coroutines/CoroutineContext$Key;\nHSPLkotlinx/coroutines/android/AndroidDispatcherFactory;-><init>()V\nHSPLkotlinx/coroutines/android/AndroidDispatcherFactory;->createDispatcher(Ljava/util/List;)Lkotlinx/coroutines/MainCoroutineDispatcher;\nHSPLkotlinx/coroutines/android/AndroidExceptionPreHandler;-><init>()V\nHSPLkotlinx/coroutines/android/HandlerContext;-><init>(Landroid/os/Handler;Ljava/lang/String;Z)V\nHSPLkotlinx/coroutines/android/HandlerContext;->dispatch(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V\nHSPLkotlinx/coroutines/android/HandlerContext;->equals(Ljava/lang/Object;)Z\nHSPLkotlinx/coroutines/android/HandlerContext;->getImmediate()Lkotlinx/coroutines/MainCoroutineDispatcher;\nHSPLkotlinx/coroutines/android/HandlerContext;->isDispatchNeeded(Lkotlin/coroutines/CoroutineContext;)Z\nHSPLkotlinx/coroutines/android/HandlerDispatcher;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLkotlinx/coroutines/android/HandlerDispatcherKt;-><clinit>()V\nHSPLkotlinx/coroutines/android/HandlerDispatcherKt;->asHandler(Landroid/os/Looper;Z)Landroid/os/Handler;\nHSPLkotlinx/coroutines/internal/AtomicKt;-><clinit>()V\nHSPLkotlinx/coroutines/internal/AtomicKt;->resumeCancellableWith$default(Lkotlin/coroutines/Continuation;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;I)V\nHSPLkotlinx/coroutines/internal/AtomicKt;->resumeCancellableWith(Lkotlin/coroutines/Continuation;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V\nHSPLkotlinx/coroutines/internal/AtomicOp;-><clinit>()V\nHSPLkotlinx/coroutines/internal/AtomicOp;-><init>()V\nHSPLkotlinx/coroutines/internal/AtomicOp;->perform(Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/internal/DispatchedContinuation;-><clinit>()V\nHSPLkotlinx/coroutines/internal/DispatchedContinuation;-><init>(Lkotlinx/coroutines/CoroutineDispatcher;Lkotlin/coroutines/Continuation;)V\nHSPLkotlinx/coroutines/internal/DispatchedContinuation;->getContext()Lkotlin/coroutines/CoroutineContext;\nHSPLkotlinx/coroutines/internal/DispatchedContinuation;->getDelegate$kotlinx_coroutines_core()Lkotlin/coroutines/Continuation;\nHSPLkotlinx/coroutines/internal/DispatchedContinuation;->release()V\nHSPLkotlinx/coroutines/internal/DispatchedContinuation;->takeState$kotlinx_coroutines_core()Ljava/lang/Object;\nHSPLkotlinx/coroutines/internal/LimitedDispatcher;-><init>(Lkotlinx/coroutines/CoroutineDispatcher;I)V\nHSPLkotlinx/coroutines/internal/LimitedDispatcher;->dispatch(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V\nHSPLkotlinx/coroutines/internal/LimitedDispatcher;->run()V\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListHead;-><init>()V\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListHead;->isRemoved()Z\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode$CondAddOp;-><init>(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)V\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode$CondAddOp;->complete(Ljava/lang/Object;Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;-><clinit>()V\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;-><init>()V\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->correctPrev(Lkotlinx/coroutines/internal/OpDescriptor;)Lkotlinx/coroutines/internal/LockFreeLinkedListNode;\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->finishAdd(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)V\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->getNext()Ljava/lang/Object;\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->getNextNode()Lkotlinx/coroutines/internal/LockFreeLinkedListNode;\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->getPrevNode()Lkotlinx/coroutines/internal/LockFreeLinkedListNode;\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->isRemoved()Z\nHSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->remove()Z\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueue;-><clinit>()V\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueue;-><init>(Z)V\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueue;->addLast(Ljava/lang/Object;)Z\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueue;->getSize()I\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueue;->removeFirstOrNull()Ljava/lang/Object;\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueueCore;-><clinit>()V\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueueCore;-><init>(IZ)V\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueueCore;->addLast(Ljava/lang/Object;)I\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueueCore;->getSize()I\nHSPLkotlinx/coroutines/internal/LockFreeTaskQueueCore;->removeFirstOrNull()Ljava/lang/Object;\nHSPLkotlinx/coroutines/internal/MainDispatcherLoader$$ExternalSyntheticServiceLoad0;->m()Ljava/util/Iterator;\nHSPLkotlinx/coroutines/internal/MainDispatcherLoader;-><clinit>()V\nHSPLkotlinx/coroutines/internal/OpDescriptor;-><init>()V\nHSPLkotlinx/coroutines/internal/Removed;-><init>(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)V\nHSPLkotlinx/coroutines/internal/ScopeCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/Continuation;)V\nHSPLkotlinx/coroutines/internal/Symbol;-><init>(Ljava/lang/String;)V\nHSPLkotlinx/coroutines/internal/SystemPropsKt;->compareValues(Ljava/lang/Comparable;Ljava/lang/Comparable;)I\nHSPLkotlinx/coroutines/internal/SystemPropsKt;->systemProp$default(Ljava/lang/String;IIIILjava/lang/Object;)I\nHSPLkotlinx/coroutines/internal/SystemPropsKt;->systemProp$default(Ljava/lang/String;JJJILjava/lang/Object;)J\nHSPLkotlinx/coroutines/internal/SystemPropsKt;->systemProp(Ljava/lang/String;)Ljava/lang/String;\nHSPLkotlinx/coroutines/internal/SystemPropsKt;->systemProp(Ljava/lang/String;JJJ)J\nHSPLkotlinx/coroutines/internal/SystemPropsKt;->systemProp(Ljava/lang/String;Z)Z\nHSPLkotlinx/coroutines/internal/SystemPropsKt__SystemPropsKt;-><clinit>()V\nHSPLkotlinx/coroutines/internal/ThreadContextKt$countAll$1;-><clinit>()V\nHSPLkotlinx/coroutines/internal/ThreadContextKt$countAll$1;-><init>()V\nHSPLkotlinx/coroutines/internal/ThreadContextKt$countAll$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/internal/ThreadContextKt;-><clinit>()V\nHSPLkotlinx/coroutines/internal/ThreadContextKt;->restoreThreadContext(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/internal/ThreadContextKt;->threadContextElements(Lkotlin/coroutines/CoroutineContext;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/internal/ThreadContextKt;->updateThreadContext(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Object;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/intrinsics/CancellableKt;->startCoroutineCancellable$default(Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/Continuation;Lkotlin/jvm/functions/Function1;I)V\nHSPLkotlinx/coroutines/intrinsics/UndispatchedKt;->startUndispatchedOrReturn(Lkotlinx/coroutines/internal/ScopeCoroutine;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;-><clinit>()V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;-><init>(Lkotlinx/coroutines/scheduling/CoroutineScheduler;I)V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->findTask(Z)Lkotlinx/coroutines/scheduling/Task;\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->getIndexInArray()I\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->getNextParkedWorker()Ljava/lang/Object;\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->nextInt(I)I\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->pollGlobalQueues()Lkotlinx/coroutines/scheduling/Task;\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->run()V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->setIndexInArray(I)V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->setNextParkedWorker(Ljava/lang/Object;)V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->tryReleaseCpu$enumunboxing$(I)Z\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;->trySteal(Z)Lkotlinx/coroutines/scheduling/Task;\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;-><clinit>()V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;-><init>(IIJLjava/lang/String;)V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->createNewWorker()I\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->currentWorker()Lkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->dispatch(Ljava/lang/Runnable;Lkotlinx/coroutines/scheduling/TaskContext;Z)V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->isTerminated()Z\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->parkedWorkersStackNextIndex(Lkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;)I\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->parkedWorkersStackPush(Lkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;)Z\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->runSafely(Lkotlinx/coroutines/scheduling/Task;)V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->signalCpuWork()V\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->tryCreateWorker(J)Z\nHSPLkotlinx/coroutines/scheduling/CoroutineScheduler;->tryUnpark()Z\nHSPLkotlinx/coroutines/scheduling/DefaultIoScheduler;-><clinit>()V\nHSPLkotlinx/coroutines/scheduling/DefaultIoScheduler;-><init>()V\nHSPLkotlinx/coroutines/scheduling/DefaultIoScheduler;->dispatch(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V\nHSPLkotlinx/coroutines/scheduling/DefaultScheduler;-><clinit>()V\nHSPLkotlinx/coroutines/scheduling/DefaultScheduler;-><init>()V\nHSPLkotlinx/coroutines/scheduling/GlobalQueue;-><init>()V\nHSPLkotlinx/coroutines/scheduling/NanoTimeSource;-><clinit>()V\nHSPLkotlinx/coroutines/scheduling/NanoTimeSource;-><init>()V\nHSPLkotlinx/coroutines/scheduling/SchedulerCoroutineDispatcher;-><init>(IIJLjava/lang/String;)V\nHSPLkotlinx/coroutines/scheduling/Task;-><init>()V\nHSPLkotlinx/coroutines/scheduling/Task;-><init>(JLkotlinx/coroutines/scheduling/TaskContext;)V\nHSPLkotlinx/coroutines/scheduling/TaskContextImpl;-><init>(I)V\nHSPLkotlinx/coroutines/scheduling/TaskContextImpl;->afterTask()V\nHSPLkotlinx/coroutines/scheduling/TaskContextImpl;->getTaskMode()I\nHSPLkotlinx/coroutines/scheduling/TaskImpl;-><init>(Ljava/lang/Runnable;JLkotlinx/coroutines/scheduling/TaskContext;)V\nHSPLkotlinx/coroutines/scheduling/TaskImpl;->run()V\nHSPLkotlinx/coroutines/scheduling/TasksKt;-><clinit>()V\nHSPLkotlinx/coroutines/scheduling/UnlimitedIoScheduler;-><clinit>()V\nHSPLkotlinx/coroutines/scheduling/UnlimitedIoScheduler;-><init>()V\nHSPLkotlinx/coroutines/scheduling/UnlimitedIoScheduler;->dispatch(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V\nHSPLkotlinx/coroutines/scheduling/WorkQueue;-><clinit>()V\nHSPLkotlinx/coroutines/scheduling/WorkQueue;-><init>()V\nHSPLkotlinx/coroutines/scheduling/WorkQueue;->pollBuffer()Lkotlinx/coroutines/scheduling/Task;\nHSPLkotlinx/coroutines/scheduling/WorkQueue;->tryStealLastScheduled(Lkotlinx/coroutines/scheduling/WorkQueue;Z)J\nHSPLokhttp3/Address;-><init>(Ljava/lang/String;ILokhttp3/Dns;Ljavax/net/SocketFactory;Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/HostnameVerifier;Lokhttp3/CertificatePinner;Lokhttp3/Authenticator;Ljava/net/Proxy;Ljava/util/List;Ljava/util/List;Ljava/net/ProxySelector;)V\nHSPLokhttp3/Address;->equalsNonHost$okhttp(Lokhttp3/Address;)Z\nHSPLokhttp3/Address;->hashCode()I\nHSPLokhttp3/Authenticator$Companion$AuthenticatorNone;-><init>()V\nHSPLokhttp3/Authenticator;-><clinit>()V\nHSPLokhttp3/Cache;-><init>(Ljava/io/File;J)V\nHSPLokhttp3/CacheControl$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/CacheControl$Companion;->parse(Lokhttp3/Headers;)Lokhttp3/CacheControl;\nHSPLokhttp3/CacheControl;-><clinit>()V\nHSPLokhttp3/CacheControl;-><init>(ZZIIZZZIIZZZLjava/lang/String;)V\nHSPLokhttp3/CertificatePinner$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/CertificatePinner;-><clinit>()V\nHSPLokhttp3/CertificatePinner;-><init>(Ljava/util/Set;Lokhttp3/internal/tls/CertificateChainCleaner;)V\nHSPLokhttp3/CertificatePinner;-><init>(Ljava/util/Set;Lokhttp3/internal/tls/CertificateChainCleaner;I)V\nHSPLokhttp3/CertificatePinner;->check$okhttp(Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V\nHSPLokhttp3/CertificatePinner;->equals(Ljava/lang/Object;)Z\nHSPLokhttp3/CertificatePinner;->hashCode()I\nHSPLokhttp3/CertificatePinner;->withCertificateChainCleaner$okhttp(Lokhttp3/internal/tls/CertificateChainCleaner;)Lokhttp3/CertificatePinner;\nHSPLokhttp3/CipherSuite$Companion$ORDER_BY_NAME$1;-><init>()V\nHSPLokhttp3/CipherSuite$Companion$ORDER_BY_NAME$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I\nHSPLokhttp3/CipherSuite$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/CipherSuite$Companion;->access$init(Lokhttp3/CipherSuite$Companion;Ljava/lang/String;I)Lokhttp3/CipherSuite;\nHSPLokhttp3/CipherSuite$Companion;->forJavaName(Ljava/lang/String;)Lokhttp3/CipherSuite;\nHSPLokhttp3/CipherSuite;-><clinit>()V\nHSPLokhttp3/CipherSuite;-><init>(Ljava/lang/String;Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/ConnectionPool;-><init>(I)V\nHSPLokhttp3/ConnectionPool;-><init>(IJLjava/util/concurrent/TimeUnit;)V\nHSPLokhttp3/ConnectionSpec$Builder;-><init>(Lokhttp3/ConnectionSpec;)V\nHSPLokhttp3/ConnectionSpec$Builder;-><init>(Z)V\nHSPLokhttp3/ConnectionSpec$Builder;->build()Lokhttp3/ConnectionSpec;\nHSPLokhttp3/ConnectionSpec$Builder;->cipherSuites([Ljava/lang/String;)Lokhttp3/ConnectionSpec$Builder;\nHSPLokhttp3/ConnectionSpec$Builder;->cipherSuites([Lokhttp3/CipherSuite;)Lokhttp3/ConnectionSpec$Builder;\nHSPLokhttp3/ConnectionSpec$Builder;->supportsTlsExtensions(Z)Lokhttp3/ConnectionSpec$Builder;\nHSPLokhttp3/ConnectionSpec$Builder;->tlsVersions([Ljava/lang/String;)Lokhttp3/ConnectionSpec$Builder;\nHSPLokhttp3/ConnectionSpec$Builder;->tlsVersions([Lokhttp3/TlsVersion;)Lokhttp3/ConnectionSpec$Builder;\nHSPLokhttp3/ConnectionSpec;-><clinit>()V\nHSPLokhttp3/ConnectionSpec;-><init>(ZZ[Ljava/lang/String;[Ljava/lang/String;)V\nHSPLokhttp3/ConnectionSpec;->apply$okhttp(Ljavax/net/ssl/SSLSocket;Z)V\nHSPLokhttp3/ConnectionSpec;->cipherSuites()Ljava/util/List;\nHSPLokhttp3/ConnectionSpec;->equals(Ljava/lang/Object;)Z\nHSPLokhttp3/ConnectionSpec;->hashCode()I\nHSPLokhttp3/ConnectionSpec;->tlsVersions()Ljava/util/List;\nHSPLokhttp3/CookieJar$Companion$NoCookies;-><init>()V\nHSPLokhttp3/CookieJar$Companion$NoCookies;->loadForRequest(Lokhttp3/HttpUrl;)Ljava/util/List;\nHSPLokhttp3/CookieJar;-><clinit>()V\nHSPLokhttp3/Dispatcher;-><init>()V\nHSPLokhttp3/Dispatcher;->finished$okhttp(Lokhttp3/internal/connection/RealCall$AsyncCall;)V\nHSPLokhttp3/Dispatcher;->finished(Ljava/util/Deque;Ljava/lang/Object;)V\nHSPLokhttp3/Dispatcher;->promoteAndExecute()Z\nHSPLokhttp3/Dns$Companion$DnsSystem;-><init>()V\nHSPLokhttp3/Dns$Companion$DnsSystem;->lookup(Ljava/lang/String;)Ljava/util/List;\nHSPLokhttp3/Dns;-><clinit>()V\nHSPLokhttp3/EventListener$Companion$NONE$1;-><init>()V\nHSPLokhttp3/EventListener;-><clinit>()V\nHSPLokhttp3/EventListener;-><init>()V\nHSPLokhttp3/EventListener;->callEnd(Lokhttp3/Call;)V\nHSPLokhttp3/EventListener;->callFailed(Lokhttp3/Call;Ljava/io/IOException;)V\nHSPLokhttp3/EventListener;->callStart(Lokhttp3/Call;)V\nHSPLokhttp3/EventListener;->connectEnd(Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;)V\nHSPLokhttp3/EventListener;->connectStart(Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;)V\nHSPLokhttp3/EventListener;->connectionAcquired(Lokhttp3/Call;Lokhttp3/Connection;)V\nHSPLokhttp3/EventListener;->connectionReleased(Lokhttp3/Call;Lokhttp3/Connection;)V\nHSPLokhttp3/EventListener;->dnsEnd(Lokhttp3/Call;Ljava/lang/String;Ljava/util/List;)V\nHSPLokhttp3/EventListener;->dnsStart(Lokhttp3/Call;Ljava/lang/String;)V\nHSPLokhttp3/EventListener;->proxySelectEnd(Lokhttp3/Call;Lokhttp3/HttpUrl;Ljava/util/List;)V\nHSPLokhttp3/EventListener;->proxySelectStart(Lokhttp3/Call;Lokhttp3/HttpUrl;)V\nHSPLokhttp3/EventListener;->requestBodyEnd(Lokhttp3/Call;J)V\nHSPLokhttp3/EventListener;->requestBodyStart(Lokhttp3/Call;)V\nHSPLokhttp3/EventListener;->requestHeadersEnd(Lokhttp3/Call;Lokhttp3/Request;)V\nHSPLokhttp3/EventListener;->requestHeadersStart(Lokhttp3/Call;)V\nHSPLokhttp3/EventListener;->responseBodyEnd(Lokhttp3/Call;J)V\nHSPLokhttp3/EventListener;->responseBodyStart(Lokhttp3/Call;)V\nHSPLokhttp3/EventListener;->responseHeadersEnd(Lokhttp3/Call;Lokhttp3/Response;)V\nHSPLokhttp3/EventListener;->responseHeadersStart(Lokhttp3/Call;)V\nHSPLokhttp3/EventListener;->secureConnectEnd(Lokhttp3/Call;Lokhttp3/Handshake;)V\nHSPLokhttp3/EventListener;->secureConnectStart(Lokhttp3/Call;)V\nHSPLokhttp3/Handshake$Companion$handshake$1;-><init>(Ljava/util/List;)V\nHSPLokhttp3/Handshake$peerCertificates$2;-><init>(Lkotlin/jvm/functions/Function0;)V\nHSPLokhttp3/Handshake;-><init>(Lokhttp3/TlsVersion;Lokhttp3/CipherSuite;Ljava/util/List;Lkotlin/jvm/functions/Function0;)V\nHSPLokhttp3/Handshake;->get(Ljavax/net/ssl/SSLSession;)Lokhttp3/Handshake;\nHSPLokhttp3/Headers$Builder;-><init>()V\nHSPLokhttp3/Headers$Builder;->build()Lokhttp3/Headers;\nHSPLokhttp3/Headers$Builder;->removeAll(Ljava/lang/String;)Lokhttp3/Headers$Builder;\nHSPLokhttp3/Headers;-><init>([Ljava/lang/String;)V\nHSPLokhttp3/Headers;->get(Ljava/lang/String;)Ljava/lang/String;\nHSPLokhttp3/Headers;->name(I)Ljava/lang/String;\nHSPLokhttp3/Headers;->newBuilder()Lokhttp3/Headers$Builder;\nHSPLokhttp3/Headers;->size()I\nHSPLokhttp3/Headers;->value(I)Ljava/lang/String;\nHSPLokhttp3/HttpUrl$Builder;-><init>()V\nHSPLokhttp3/HttpUrl$Builder;->build()Lokhttp3/HttpUrl;\nHSPLokhttp3/HttpUrl$Builder;->effectivePort()I\nHSPLokhttp3/HttpUrl$Builder;->encodedQuery(Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\nHSPLokhttp3/HttpUrl$Builder;->parse$okhttp(Lokhttp3/HttpUrl;Ljava/lang/String;)Lokhttp3/HttpUrl$Builder;\nHSPLokhttp3/HttpUrl$Builder;->toString()Ljava/lang/String;\nHSPLokhttp3/HttpUrl$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/HttpUrl$Companion;->canonicalize$okhttp$default(Lokhttp3/HttpUrl$Companion;Ljava/lang/String;IILjava/lang/String;ZZZZLjava/nio/charset/Charset;I)Ljava/lang/String;\nHSPLokhttp3/HttpUrl$Companion;->defaultPort(Ljava/lang/String;)I\nHSPLokhttp3/HttpUrl$Companion;->percentDecode$okhttp$default(Lokhttp3/HttpUrl$Companion;Ljava/lang/String;IIZI)Ljava/lang/String;\nHSPLokhttp3/HttpUrl;-><clinit>()V\nHSPLokhttp3/HttpUrl;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;)V\nHSPLokhttp3/HttpUrl;->encodedPassword()Ljava/lang/String;\nHSPLokhttp3/HttpUrl;->encodedPath()Ljava/lang/String;\nHSPLokhttp3/HttpUrl;->encodedPathSegments()Ljava/util/List;\nHSPLokhttp3/HttpUrl;->encodedQuery()Ljava/lang/String;\nHSPLokhttp3/HttpUrl;->encodedUsername()Ljava/lang/String;\nHSPLokhttp3/HttpUrl;->hashCode()I\nHSPLokhttp3/HttpUrl;->redact()Ljava/lang/String;\nHSPLokhttp3/HttpUrl;->toString()Ljava/lang/String;\nHSPLokhttp3/HttpUrl;->uri()Ljava/net/URI;\nHSPLokhttp3/JvmCallExtensionsKt$executeAsync$2$1;-><init>(Lokhttp3/Call;)V\nHSPLokhttp3/JvmCallExtensionsKt$executeAsync$2$2$onResponse$1;-><init>(Lokhttp3/Call;)V\nHSPLokhttp3/JvmCallExtensionsKt$executeAsync$2$2;-><init>(Lkotlinx/coroutines/CancellableContinuation;)V\nHSPLokhttp3/JvmCallExtensionsKt$executeAsync$2$2;->onFailure(Lokhttp3/Call;Ljava/io/IOException;)V\nHSPLokhttp3/JvmCallExtensionsKt$executeAsync$2$2;->onResponse(Lokhttp3/Call;Lokhttp3/Response;)V\nHSPLokhttp3/MediaType;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V\nHSPLokhttp3/MediaType;->charset(Ljava/nio/charset/Charset;)Ljava/nio/charset/Charset;\nHSPLokhttp3/OkHttpClient$Builder;-><init>()V\nHSPLokhttp3/OkHttpClient$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/OkHttpClient;-><clinit>()V\nHSPLokhttp3/OkHttpClient;-><init>(Lokhttp3/OkHttpClient$Builder;)V\nHSPLokhttp3/OkHttpClient;->newBuilder()Lokhttp3/OkHttpClient$Builder;\nHSPLokhttp3/OkHttpClient;->newCall(Lokhttp3/Request;)Lokhttp3/Call;\nHSPLokhttp3/Protocol$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/Protocol;-><clinit>()V\nHSPLokhttp3/Protocol;-><init>(Ljava/lang/String;ILjava/lang/String;)V\nHSPLokhttp3/Request$Builder;-><init>()V\nHSPLokhttp3/Request$Builder;-><init>(Lokhttp3/Request;)V\nHSPLokhttp3/Request$Builder;->build()Lokhttp3/Request;\nHSPLokhttp3/Request$Builder;->header(Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder;\nHSPLokhttp3/Request$Builder;->method(Ljava/lang/String;Lokhttp3/RequestBody;)Lokhttp3/Request$Builder;\nHSPLokhttp3/Request$Builder;->url(Ljava/lang/String;)Lokhttp3/Request$Builder;\nHSPLokhttp3/Request$Builder;->url(Lokhttp3/HttpUrl;)Lokhttp3/Request$Builder;\nHSPLokhttp3/Request;-><init>(Lokhttp3/HttpUrl;Ljava/lang/String;Lokhttp3/Headers;Lokhttp3/RequestBody;Ljava/util/Map;)V\nHSPLokhttp3/Request;->cacheControl()Lokhttp3/CacheControl;\nHSPLokhttp3/RequestBody;-><init>()V\nHSPLokhttp3/Response$Builder;-><init>()V\nHSPLokhttp3/Response$Builder;->build()Lokhttp3/Response;\nHSPLokhttp3/Response$Builder;->headers(Lokhttp3/Headers;)Lokhttp3/Response$Builder;\nHSPLokhttp3/Response$Builder;->message(Ljava/lang/String;)Lokhttp3/Response$Builder;\nHSPLokhttp3/Response$Builder;->protocol(Lokhttp3/Protocol;)Lokhttp3/Response$Builder;\nHSPLokhttp3/Response;-><init>(Lokhttp3/Request;Lokhttp3/Protocol;Ljava/lang/String;ILokhttp3/Handshake;Lokhttp3/Headers;Lokhttp3/ResponseBody;Lokhttp3/Response;Lokhttp3/Response;Lokhttp3/Response;JJLokhttp3/internal/connection/Exchange;)V\nHSPLokhttp3/Response;->close()V\nHSPLokhttp3/Response;->header$default(Lokhttp3/Response;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;\nHSPLokhttp3/ResponseBody;-><init>()V\nHSPLokhttp3/ResponseBody;->close()V\nHSPLokhttp3/ResponseBody;->string()Ljava/lang/String;\nHSPLokhttp3/Route;-><init>(Lokhttp3/Address;Ljava/net/Proxy;Ljava/net/InetSocketAddress;)V\nHSPLokhttp3/Route;->hashCode()I\nHSPLokhttp3/TlsVersion;-><clinit>()V\nHSPLokhttp3/TlsVersion;-><init>(Ljava/lang/String;ILjava/lang/String;)V\nHSPLokhttp3/internal/Internal;->charset$default(Lokhttp3/MediaType;Ljava/nio/charset/Charset;I)Ljava/nio/charset/Charset;\nHSPLokhttp3/internal/Internal;->getProgressionLastElement(III)I\nHSPLokhttp3/internal/Internal;->mod(II)I\nHSPLokhttp3/internal/_HeadersCommonKt;->commonAddLenient(Lokhttp3/Headers$Builder;Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Headers$Builder;\nHSPLokhttp3/internal/_HeadersCommonKt;->headersCheckName(Ljava/lang/String;)V\nHSPLokhttp3/internal/_HeadersCommonKt;->headersCheckValue(Ljava/lang/String;Ljava/lang/String;)V\nHSPLokhttp3/internal/_HostnamesCommonKt;-><clinit>()V\nHSPLokhttp3/internal/_HostnamesCommonKt;->canParseAsIpAddress(Ljava/lang/String;)Z\nHSPLokhttp3/internal/_MediaTypeCommonKt;-><clinit>()V\nHSPLokhttp3/internal/_MediaTypeCommonKt;->commonToMediaType(Ljava/lang/String;)Lokhttp3/MediaType;\nHSPLokhttp3/internal/_RequestBodyCommonKt$commonToRequestBody$1;-><init>(Lokhttp3/MediaType;I[BI)V\nHSPLokhttp3/internal/_RequestBodyCommonKt$commonToRequestBody$1;->contentLength()J\nHSPLokhttp3/internal/_RequestBodyCommonKt$commonToRequestBody$1;->contentType()Lokhttp3/MediaType;\nHSPLokhttp3/internal/_RequestBodyCommonKt$commonToRequestBody$1;->writeTo(Lokio/BufferedSink;)V\nHSPLokhttp3/internal/_ResponseBodyCommonKt$commonAsResponseBody$1;-><init>(Lokhttp3/MediaType;JLokio/BufferedSource;)V\nHSPLokhttp3/internal/_ResponseCommonKt;->checkSupportResponse(Ljava/lang/String;Lokhttp3/Response;)V\nHSPLokhttp3/internal/_UtilCommonKt;-><clinit>()V\nHSPLokhttp3/internal/_UtilCommonKt;->checkOffsetAndCount(JJJ)V\nHSPLokhttp3/internal/_UtilCommonKt;->closeQuietly(Ljava/io/Closeable;)V\nHSPLokhttp3/internal/_UtilCommonKt;->delimiterOffset(Ljava/lang/String;CII)I\nHSPLokhttp3/internal/_UtilCommonKt;->delimiterOffset(Ljava/lang/String;Ljava/lang/String;II)I\nHSPLokhttp3/internal/_UtilCommonKt;->hasIntersection([Ljava/lang/String;[Ljava/lang/String;Ljava/util/Comparator;)Z\nHSPLokhttp3/internal/_UtilCommonKt;->indexOfFirstNonAsciiWhitespace(Ljava/lang/String;II)I\nHSPLokhttp3/internal/_UtilCommonKt;->indexOfLastNonAsciiWhitespace(Ljava/lang/String;II)I\nHSPLokhttp3/internal/_UtilCommonKt;->intersect([Ljava/lang/String;[Ljava/lang/String;Ljava/util/Comparator;)[Ljava/lang/String;\nHSPLokhttp3/internal/_UtilCommonKt;->matchAtPolyfill(Lkotlin/text/Regex;Ljava/lang/CharSequence;I)Lkotlin/text/MatchResult;\nHSPLokhttp3/internal/_UtilCommonKt;->readMedium(Lokio/BufferedSource;)I\nHSPLokhttp3/internal/_UtilJvmKt$$ExternalSyntheticLambda0;-><init>(Ljava/lang/String;Z)V\nHSPLokhttp3/internal/_UtilJvmKt$$ExternalSyntheticLambda0;->newThread(Ljava/lang/Runnable;)Ljava/lang/Thread;\nHSPLokhttp3/internal/_UtilJvmKt$$ExternalSyntheticLambda1;-><init>(Lokhttp3/EventListener;)V\nHSPLokhttp3/internal/_UtilJvmKt$$ExternalSyntheticLambda1;->create(Lokhttp3/Call;)Lokhttp3/EventListener;\nHSPLokhttp3/internal/_UtilJvmKt;-><clinit>()V\nHSPLokhttp3/internal/_UtilJvmKt;->closeQuietly(Ljava/net/Socket;)V\nHSPLokhttp3/internal/_UtilJvmKt;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\nHSPLokhttp3/internal/_UtilJvmKt;->headersContentLength(Lokhttp3/Response;)J\nHSPLokhttp3/internal/_UtilJvmKt;->immutableListOf([Ljava/lang/Object;)Ljava/util/List;\nHSPLokhttp3/internal/_UtilJvmKt;->readBomAsCharset(Lokio/BufferedSource;Ljava/nio/charset/Charset;)Ljava/nio/charset/Charset;\nHSPLokhttp3/internal/_UtilJvmKt;->toHeaders(Ljava/util/List;)Lokhttp3/Headers;\nHSPLokhttp3/internal/_UtilJvmKt;->toHostHeader(Lokhttp3/HttpUrl;Z)Ljava/lang/String;\nHSPLokhttp3/internal/_UtilJvmKt;->toImmutableList(Ljava/util/List;)Ljava/util/List;\nHSPLokhttp3/internal/cache/CacheInterceptor$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/internal/cache/CacheInterceptor$Companion;->access$stripBody(Lokhttp3/internal/cache/CacheInterceptor$Companion;Lokhttp3/Response;)Lokhttp3/Response;\nHSPLokhttp3/internal/cache/CacheInterceptor;-><clinit>()V\nHSPLokhttp3/internal/cache/CacheInterceptor;-><init>(Lokhttp3/Cache;)V\nHSPLokhttp3/internal/cache/CacheInterceptor;->intercept(Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\nHSPLokhttp3/internal/cache/CacheStrategy;-><init>(Lokhttp3/Request;Lokhttp3/Response;)V\nHSPLokhttp3/internal/cache/DiskLruCache$cleanupTask$1;-><init>(Lokhttp3/internal/cache/DiskLruCache;Ljava/lang/String;)V\nHSPLokhttp3/internal/cache/DiskLruCache$fileSystem$1;-><init>(Lokio/FileSystem;)V\nHSPLokhttp3/internal/cache/DiskLruCache;-><clinit>()V\nHSPLokhttp3/internal/cache/DiskLruCache;-><init>(Lokio/FileSystem;Lokio/Path;IIJLokhttp3/internal/concurrent/TaskRunner;)V\nHSPLokhttp3/internal/concurrent/Task;-><init>(Ljava/lang/String;Z)V\nHSPLokhttp3/internal/concurrent/Task;-><init>(Ljava/lang/String;ZI)V\nHSPLokhttp3/internal/concurrent/TaskQueue$execute$1;-><init>(Ljava/lang/String;ZLkotlin/jvm/functions/Function0;)V\nHSPLokhttp3/internal/concurrent/TaskQueue$execute$1;->runOnce()J\nHSPLokhttp3/internal/concurrent/TaskQueue;-><init>(Lokhttp3/internal/concurrent/TaskRunner;Ljava/lang/String;)V\nHSPLokhttp3/internal/concurrent/TaskQueue;->cancelAllAndDecide$okhttp()Z\nHSPLokhttp3/internal/concurrent/TaskQueue;->execute$default(Lokhttp3/internal/concurrent/TaskQueue;Ljava/lang/String;JZLkotlin/jvm/functions/Function0;I)V\nHSPLokhttp3/internal/concurrent/TaskQueue;->schedule$default(Lokhttp3/internal/concurrent/TaskQueue;Lokhttp3/internal/concurrent/Task;JI)V\nHSPLokhttp3/internal/concurrent/TaskQueue;->schedule(Lokhttp3/internal/concurrent/Task;J)V\nHSPLokhttp3/internal/concurrent/TaskQueue;->scheduleAndDecide$okhttp(Lokhttp3/internal/concurrent/Task;JZ)Z\nHSPLokhttp3/internal/concurrent/TaskQueue;->shutdown()V\nHSPLokhttp3/internal/concurrent/TaskRunner$RealBackend;-><init>(Ljava/util/concurrent/ThreadFactory;)V\nHSPLokhttp3/internal/concurrent/TaskRunner$RealBackend;->coordinatorNotify(Lokhttp3/internal/concurrent/TaskRunner;)V\nHSPLokhttp3/internal/concurrent/TaskRunner$RealBackend;->coordinatorWait(Lokhttp3/internal/concurrent/TaskRunner;J)V\nHSPLokhttp3/internal/concurrent/TaskRunner$RealBackend;->execute(Lokhttp3/internal/concurrent/TaskRunner;Ljava/lang/Runnable;)V\nHSPLokhttp3/internal/concurrent/TaskRunner$RealBackend;->nanoTime()J\nHSPLokhttp3/internal/concurrent/TaskRunner$runnable$1;-><init>(Lokhttp3/internal/concurrent/TaskRunner;)V\nHSPLokhttp3/internal/concurrent/TaskRunner$runnable$1;->run()V\nHSPLokhttp3/internal/concurrent/TaskRunner;-><clinit>()V\nHSPLokhttp3/internal/concurrent/TaskRunner;-><init>(Lokhttp3/internal/concurrent/TaskRunner$Backend;Ljava/util/logging/Logger;I)V\nHSPLokhttp3/internal/concurrent/TaskRunner;->access$runTask(Lokhttp3/internal/concurrent/TaskRunner;Lokhttp3/internal/concurrent/Task;)V\nHSPLokhttp3/internal/concurrent/TaskRunner;->afterRun(Lokhttp3/internal/concurrent/Task;J)V\nHSPLokhttp3/internal/concurrent/TaskRunner;->awaitTaskToRun()Lokhttp3/internal/concurrent/Task;\nHSPLokhttp3/internal/concurrent/TaskRunner;->kickCoordinator$okhttp(Lokhttp3/internal/concurrent/TaskQueue;)V\nHSPLokhttp3/internal/concurrent/TaskRunner;->newQueue()Lokhttp3/internal/concurrent/TaskQueue;\nHSPLokhttp3/internal/connection/ConnectInterceptor;-><clinit>()V\nHSPLokhttp3/internal/connection/ConnectInterceptor;-><init>()V\nHSPLokhttp3/internal/connection/ConnectInterceptor;->intercept(Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\nHSPLokhttp3/internal/connection/ConnectPlan$WhenMappings;-><clinit>()V\nHSPLokhttp3/internal/connection/ConnectPlan$connectTls$1;-><init>(Lokhttp3/Handshake;)V\nHSPLokhttp3/internal/connection/ConnectPlan$connectTls$handshake$1;-><init>(Lokhttp3/CertificatePinner;Lokhttp3/Handshake;Lokhttp3/Address;)V\nHSPLokhttp3/internal/connection/ConnectPlan;-><init>(Lokhttp3/OkHttpClient;Lokhttp3/internal/connection/RealCall;Lokhttp3/internal/connection/RealRoutePlanner;Lokhttp3/Route;Ljava/util/List;ILokhttp3/Request;IZ)V\nHSPLokhttp3/internal/connection/ConnectPlan;->connectSocket()V\nHSPLokhttp3/internal/connection/ConnectPlan;->connectTcp()Lokhttp3/internal/connection/RoutePlanner$ConnectResult;\nHSPLokhttp3/internal/connection/ConnectPlan;->connectTls(Ljavax/net/ssl/SSLSocket;Lokhttp3/ConnectionSpec;)V\nHSPLokhttp3/internal/connection/ConnectPlan;->connectTlsEtc()Lokhttp3/internal/connection/RoutePlanner$ConnectResult;\nHSPLokhttp3/internal/connection/ConnectPlan;->copy$default(Lokhttp3/internal/connection/ConnectPlan;ILokhttp3/Request;IZI)Lokhttp3/internal/connection/ConnectPlan;\nHSPLokhttp3/internal/connection/ConnectPlan;->handleSuccess()Lokhttp3/internal/connection/RealConnection;\nHSPLokhttp3/internal/connection/ConnectPlan;->isReady()Z\nHSPLokhttp3/internal/connection/ConnectPlan;->nextConnectionSpec$okhttp(Ljava/util/List;Ljavax/net/ssl/SSLSocket;)Lokhttp3/internal/connection/ConnectPlan;\nHSPLokhttp3/internal/connection/ConnectPlan;->planWithCurrentOrInitialConnectionSpec$okhttp(Ljava/util/List;Ljavax/net/ssl/SSLSocket;)Lokhttp3/internal/connection/ConnectPlan;\nHSPLokhttp3/internal/connection/Exchange$RequestBodySink;-><init>(Lokhttp3/internal/connection/Exchange;Lokio/Sink;J)V\nHSPLokhttp3/internal/connection/Exchange$RequestBodySink;->close()V\nHSPLokhttp3/internal/connection/Exchange$RequestBodySink;->complete(Ljava/io/IOException;)Ljava/io/IOException;\nHSPLokhttp3/internal/connection/Exchange$RequestBodySink;->write(Lokio/Buffer;J)V\nHSPLokhttp3/internal/connection/Exchange$ResponseBodySource;-><init>(Lokhttp3/internal/connection/Exchange;Lokio/Source;J)V\nHSPLokhttp3/internal/connection/Exchange$ResponseBodySource;->close()V\nHSPLokhttp3/internal/connection/Exchange$ResponseBodySource;->complete(Ljava/io/IOException;)Ljava/io/IOException;\nHSPLokhttp3/internal/connection/Exchange$ResponseBodySource;->read(Lokio/Buffer;J)J\nHSPLokhttp3/internal/connection/Exchange;-><init>(Lokhttp3/internal/connection/RealCall;Lokhttp3/EventListener;Lokhttp3/internal/connection/ExchangeFinder;Lokhttp3/internal/http/ExchangeCodec;)V\nHSPLokhttp3/internal/connection/Exchange;->bodyComplete(JZZLjava/io/IOException;)Ljava/io/IOException;\nHSPLokhttp3/internal/connection/Exchange;->createRequestBody(Lokhttp3/Request;Z)Lokio/Sink;\nHSPLokhttp3/internal/connection/Exchange;->getConnection$okhttp()Lokhttp3/internal/connection/RealConnection;\nHSPLokhttp3/internal/connection/Exchange;->openResponseBody(Lokhttp3/Response;)Lokhttp3/ResponseBody;\nHSPLokhttp3/internal/connection/Exchange;->readResponseHeaders(Z)Lokhttp3/Response$Builder;\nHSPLokhttp3/internal/connection/Exchange;->responseHeadersStart()V\nHSPLokhttp3/internal/connection/Exchange;->writeRequestHeaders(Lokhttp3/Request;)V\nHSPLokhttp3/internal/connection/RealCall$AsyncCall;-><init>(Lokhttp3/internal/connection/RealCall;Lokhttp3/Callback;)V\nHSPLokhttp3/internal/connection/RealCall$AsyncCall;->getHost()Ljava/lang/String;\nHSPLokhttp3/internal/connection/RealCall$AsyncCall;->run()V\nHSPLokhttp3/internal/connection/RealCall$CallReference;-><init>(Lokhttp3/internal/connection/RealCall;Ljava/lang/Object;)V\nHSPLokhttp3/internal/connection/RealCall$timeout$1;-><init>(Lokhttp3/internal/connection/RealCall;)V\nHSPLokhttp3/internal/connection/RealCall;-><init>(Lokhttp3/OkHttpClient;Lokhttp3/Request;Z)V\nHSPLokhttp3/internal/connection/RealCall;->acquireConnectionNoEvents(Lokhttp3/internal/connection/RealConnection;)V\nHSPLokhttp3/internal/connection/RealCall;->callDone(Ljava/io/IOException;)Ljava/io/IOException;\nHSPLokhttp3/internal/connection/RealCall;->enqueue(Lokhttp3/Callback;)V\nHSPLokhttp3/internal/connection/RealCall;->execute()Lokhttp3/Response;\nHSPLokhttp3/internal/connection/RealCall;->exitNetworkInterceptorExchange$okhttp(Z)V\nHSPLokhttp3/internal/connection/RealCall;->getResponseWithInterceptorChain$okhttp()Lokhttp3/Response;\nHSPLokhttp3/internal/connection/RealCall;->messageDone$okhttp(Lokhttp3/internal/connection/Exchange;ZZLjava/io/IOException;)Ljava/io/IOException;\nHSPLokhttp3/internal/connection/RealCall;->noMoreExchanges$okhttp(Ljava/io/IOException;)Ljava/io/IOException;\nHSPLokhttp3/internal/connection/RealCall;->releaseConnectionNoEvents$okhttp()Ljava/net/Socket;\nHSPLokhttp3/internal/connection/RealConnection;-><init>(Lokhttp3/internal/concurrent/TaskRunner;Lokhttp3/internal/connection/RealConnectionPool;Lokhttp3/Route;Ljava/net/Socket;Ljava/net/Socket;Lokhttp3/Handshake;Lokhttp3/Protocol;Lokio/BufferedSource;Lokio/BufferedSink;I)V\nHSPLokhttp3/internal/connection/RealConnection;->isEligible$okhttp(Lokhttp3/Address;Ljava/util/List;)Z\nHSPLokhttp3/internal/connection/RealConnection;->isHealthy(Z)Z\nHSPLokhttp3/internal/connection/RealConnection;->isMultiplexed$okhttp()Z\nHSPLokhttp3/internal/connection/RealConnection;->onSettings(Lokhttp3/internal/http2/Http2Connection;Lokhttp3/internal/http2/Settings;)V\nHSPLokhttp3/internal/connection/RealConnection;->start()V\nHSPLokhttp3/internal/connection/RealConnectionPool$cleanupTask$1;-><init>(Lokhttp3/internal/connection/RealConnectionPool;Ljava/lang/String;)V\nHSPLokhttp3/internal/connection/RealConnectionPool$cleanupTask$1;->runOnce()J\nHSPLokhttp3/internal/connection/RealConnectionPool;-><init>(Lokhttp3/internal/concurrent/TaskRunner;IJLjava/util/concurrent/TimeUnit;)V\nHSPLokhttp3/internal/connection/RealConnectionPool;->pruneAndGetAllocationCount(Lokhttp3/internal/connection/RealConnection;J)I\nHSPLokhttp3/internal/connection/RealConnectionPool;->put(Lokhttp3/internal/connection/RealConnection;)V\nHSPLokhttp3/internal/connection/RealRoutePlanner;-><init>(Lokhttp3/OkHttpClient;Lokhttp3/Address;Lokhttp3/internal/connection/RealCall;Lokhttp3/internal/http/RealInterceptorChain;)V\nHSPLokhttp3/internal/connection/RealRoutePlanner;->hasNext(Lokhttp3/internal/connection/RealConnection;)Z\nHSPLokhttp3/internal/connection/RealRoutePlanner;->isCanceled()Z\nHSPLokhttp3/internal/connection/RealRoutePlanner;->plan()Lokhttp3/internal/connection/RoutePlanner$Plan;\nHSPLokhttp3/internal/connection/RealRoutePlanner;->planConnectToRoute$okhttp(Lokhttp3/Route;Ljava/util/List;)Lokhttp3/internal/connection/ConnectPlan;\nHSPLokhttp3/internal/connection/RealRoutePlanner;->planReusePooledConnection$okhttp(Lokhttp3/internal/connection/ConnectPlan;Ljava/util/List;)Lokhttp3/internal/connection/ReusePlan;\nHSPLokhttp3/internal/connection/RealRoutePlanner;->sameHostAndPort(Lokhttp3/HttpUrl;)Z\nHSPLokhttp3/internal/connection/ReusePlan;-><init>(Lokhttp3/internal/connection/RealConnection;)V\nHSPLokhttp3/internal/connection/ReusePlan;->handleSuccess()Lokhttp3/internal/connection/RealConnection;\nHSPLokhttp3/internal/connection/ReusePlan;->isReady()Z\nHSPLokhttp3/internal/connection/RouteDatabase;-><init>()V\nHSPLokhttp3/internal/connection/RoutePlanner$ConnectResult;-><init>(Lokhttp3/internal/connection/RoutePlanner$Plan;Lokhttp3/internal/connection/RoutePlanner$Plan;Ljava/lang/Throwable;I)V\nHSPLokhttp3/internal/connection/RoutePlanner$ConnectResult;->isSuccess()Z\nHSPLokhttp3/internal/connection/RoutePlanner$DefaultImpls;->hasNext$default(Lokhttp3/internal/connection/RoutePlanner;Lokhttp3/internal/connection/RealConnection;ILjava/lang/Object;)Z\nHSPLokhttp3/internal/connection/RouteSelector$Selection;-><init>(Ljava/util/List;)V\nHSPLokhttp3/internal/connection/RouteSelector$Selection;->hasNext()Z\nHSPLokhttp3/internal/connection/RouteSelector$Selection;->next()Lokhttp3/Route;\nHSPLokhttp3/internal/connection/RouteSelector;-><init>(Lokhttp3/Address;Lokhttp3/internal/connection/RouteDatabase;Lokhttp3/Call;ZLokhttp3/EventListener;)V\nHSPLokhttp3/internal/connection/RouteSelector;->hasNext()Z\nHSPLokhttp3/internal/connection/RouteSelector;->hasNextProxy()Z\nHSPLokhttp3/internal/connection/SequentialExchangeFinder;-><init>(Lokhttp3/internal/connection/RoutePlanner;)V\nHSPLokhttp3/internal/connection/SequentialExchangeFinder;->find()Lokhttp3/internal/connection/RealConnection;\nHSPLokhttp3/internal/connection/SequentialExchangeFinder;->getRoutePlanner()Lokhttp3/internal/connection/RoutePlanner;\nHSPLokhttp3/internal/http/BridgeInterceptor;-><init>(Lokhttp3/CookieJar;)V\nHSPLokhttp3/internal/http/BridgeInterceptor;->intercept(Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\nHSPLokhttp3/internal/http/CallServerInterceptor;-><init>(Z)V\nHSPLokhttp3/internal/http/CallServerInterceptor;->intercept(Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\nHSPLokhttp3/internal/http/HttpHeaders;-><clinit>()V\nHSPLokhttp3/internal/http/HttpHeaders;->promisesBody(Lokhttp3/Response;)Z\nHSPLokhttp3/internal/http/HttpHeaders;->receiveHeaders(Lokhttp3/CookieJar;Lokhttp3/HttpUrl;Lokhttp3/Headers;)V\nHSPLokhttp3/internal/http/HttpMethod;->permitsRequestBody(Ljava/lang/String;)Z\nHSPLokhttp3/internal/http/RealInterceptorChain;-><init>(Lokhttp3/internal/connection/RealCall;Ljava/util/List;ILokhttp3/internal/connection/Exchange;Lokhttp3/Request;III)V\nHSPLokhttp3/internal/http/RealInterceptorChain;->copy$okhttp$default(Lokhttp3/internal/http/RealInterceptorChain;ILokhttp3/internal/connection/Exchange;Lokhttp3/Request;IIII)Lokhttp3/internal/http/RealInterceptorChain;\nHSPLokhttp3/internal/http/RealInterceptorChain;->proceed(Lokhttp3/Request;)Lokhttp3/Response;\nHSPLokhttp3/internal/http/RealResponseBody;-><init>(Ljava/lang/String;JLokio/BufferedSource;)V\nHSPLokhttp3/internal/http/RealResponseBody;->contentType()Lokhttp3/MediaType;\nHSPLokhttp3/internal/http/RealResponseBody;->source()Lokio/BufferedSource;\nHSPLokhttp3/internal/http/RetryAndFollowUpInterceptor;-><init>(Lokhttp3/OkHttpClient;)V\nHSPLokhttp3/internal/http/RetryAndFollowUpInterceptor;->followUpRequest(Lokhttp3/Response;Lokhttp3/internal/connection/Exchange;)Lokhttp3/Request;\nHSPLokhttp3/internal/http/RetryAndFollowUpInterceptor;->intercept(Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\nHSPLokhttp3/internal/http/RetryAndFollowUpInterceptor;->recover(Ljava/io/IOException;Lokhttp3/internal/connection/RealCall;Lokhttp3/Request;Z)Z\nHSPLokhttp3/internal/http/StatusLine;-><init>(Lokhttp3/Protocol;ILjava/lang/String;)V\nHSPLokhttp3/internal/http/StatusLine;->parse(Ljava/lang/String;)Lokhttp3/internal/http/StatusLine;\nHSPLokhttp3/internal/http2/ErrorCode;-><clinit>()V\nHSPLokhttp3/internal/http2/ErrorCode;-><init>(Ljava/lang/String;II)V\nHSPLokhttp3/internal/http2/Header;-><clinit>()V\nHSPLokhttp3/internal/http2/Header;-><init>(Ljava/lang/String;Ljava/lang/String;)V\nHSPLokhttp3/internal/http2/Header;-><init>(Lokio/ByteString;Ljava/lang/String;)V\nHSPLokhttp3/internal/http2/Header;-><init>(Lokio/ByteString;Lokio/ByteString;)V\nHSPLokhttp3/internal/http2/Hpack$Reader;-><init>(Lokio/Source;III)V\nHSPLokhttp3/internal/http2/Hpack$Reader;->evictToRecoverBytes(I)I\nHSPLokhttp3/internal/http2/Hpack$Reader;->getName(I)Lokio/ByteString;\nHSPLokhttp3/internal/http2/Hpack$Reader;->insertIntoDynamicTable(ILokhttp3/internal/http2/Header;)V\nHSPLokhttp3/internal/http2/Hpack$Reader;->readByteString()Lokio/ByteString;\nHSPLokhttp3/internal/http2/Hpack$Reader;->readInt(II)I\nHSPLokhttp3/internal/http2/Hpack$Writer;-><init>(IZLokio/Buffer;I)V\nHSPLokhttp3/internal/http2/Hpack$Writer;->evictToRecoverBytes(I)I\nHSPLokhttp3/internal/http2/Hpack$Writer;->insertIntoDynamicTable(Lokhttp3/internal/http2/Header;)V\nHSPLokhttp3/internal/http2/Hpack$Writer;->writeByteString(Lokio/ByteString;)V\nHSPLokhttp3/internal/http2/Hpack$Writer;->writeHeaders(Ljava/util/List;)V\nHSPLokhttp3/internal/http2/Hpack$Writer;->writeInt(III)V\nHSPLokhttp3/internal/http2/Hpack;-><clinit>()V\nHSPLokhttp3/internal/http2/Hpack;-><init>()V\nHSPLokhttp3/internal/http2/Hpack;->checkLowercase(Lokio/ByteString;)Lokio/ByteString;\nHSPLokhttp3/internal/http2/Http2;-><clinit>()V\nHSPLokhttp3/internal/http2/Http2;-><init>()V\nHSPLokhttp3/internal/http2/Http2Connection$Builder;-><init>(ZLokhttp3/internal/concurrent/TaskRunner;)V\nHSPLokhttp3/internal/http2/Http2Connection$Listener$Companion$REFUSE_INCOMING_STREAMS$1;-><init>()V\nHSPLokhttp3/internal/http2/Http2Connection$Listener;-><clinit>()V\nHSPLokhttp3/internal/http2/Http2Connection$Listener;-><init>()V\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable$applyAndAckSettings$1$1$2;-><init>(Lokhttp3/internal/http2/Http2Connection;Lkotlin/jvm/internal/Ref$ObjectRef;)V\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable$applyAndAckSettings$1$1$2;->invoke()Ljava/lang/Object;\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable$settings$1;-><init>(Lokhttp3/internal/http2/Http2Connection$ReaderRunnable;ZLokhttp3/internal/http2/Settings;)V\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable$settings$1;->invoke()Ljava/lang/Object;\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable;-><init>(Lokhttp3/internal/http2/Http2Connection;Lokhttp3/internal/http2/Http2Reader;)V\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable;->ackSettings()V\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable;->data(ZILokio/BufferedSource;I)V\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable;->headers(ZIILjava/util/List;)V\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable;->invoke()Ljava/lang/Object;\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable;->settings(ZLokhttp3/internal/http2/Settings;)V\nHSPLokhttp3/internal/http2/Http2Connection$ReaderRunnable;->windowUpdate(IJ)V\nHSPLokhttp3/internal/http2/Http2Connection;-><clinit>()V\nHSPLokhttp3/internal/http2/Http2Connection;-><init>(Lokhttp3/internal/http2/Http2Connection$Builder;)V\nHSPLokhttp3/internal/http2/Http2Connection;->close$okhttp(Lokhttp3/internal/http2/ErrorCode;Lokhttp3/internal/http2/ErrorCode;Ljava/io/IOException;)V\nHSPLokhttp3/internal/http2/Http2Connection;->getStream(I)Lokhttp3/internal/http2/Http2Stream;\nHSPLokhttp3/internal/http2/Http2Connection;->pushedStream$okhttp(I)Z\nHSPLokhttp3/internal/http2/Http2Connection;->removeStream$okhttp(I)Lokhttp3/internal/http2/Http2Stream;\nHSPLokhttp3/internal/http2/Http2Connection;->shutdown(Lokhttp3/internal/http2/ErrorCode;)V\nHSPLokhttp3/internal/http2/Http2Connection;->updateConnectionFlowControl$okhttp(J)V\nHSPLokhttp3/internal/http2/Http2Connection;->writeData(IZLokio/Buffer;J)V\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;-><clinit>()V\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;-><init>(Lokhttp3/OkHttpClient;Lokhttp3/internal/http/ExchangeCodec$Carrier;Lokhttp3/internal/http/RealInterceptorChain;Lokhttp3/internal/http2/Http2Connection;)V\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;->createRequestBody(Lokhttp3/Request;J)Lokio/Sink;\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;->finishRequest()V\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;->getCarrier()Lokhttp3/internal/http/ExchangeCodec$Carrier;\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;->openResponseBodySource(Lokhttp3/Response;)Lokio/Source;\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;->readResponseHeaders(Z)Lokhttp3/Response$Builder;\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;->reportedContentLength(Lokhttp3/Response;)J\nHSPLokhttp3/internal/http2/Http2ExchangeCodec;->writeRequestHeaders(Lokhttp3/Request;)V\nHSPLokhttp3/internal/http2/Http2Reader$ContinuationSource;-><init>(Lokio/BufferedSource;)V\nHSPLokhttp3/internal/http2/Http2Reader$ContinuationSource;->read(Lokio/Buffer;J)J\nHSPLokhttp3/internal/http2/Http2Reader;-><clinit>()V\nHSPLokhttp3/internal/http2/Http2Reader;-><init>(Lokio/BufferedSource;Z)V\nHSPLokhttp3/internal/http2/Http2Reader;->close()V\nHSPLokhttp3/internal/http2/Http2Reader;->lengthWithoutPadding(III)I\nHSPLokhttp3/internal/http2/Http2Reader;->nextFrame(ZLokhttp3/internal/http2/Http2Reader$Handler;)Z\nHSPLokhttp3/internal/http2/Http2Reader;->readConnectionPreface(Lokhttp3/internal/http2/Http2Reader$Handler;)V\nHSPLokhttp3/internal/http2/Http2Reader;->readHeaderBlock(IIII)Ljava/util/List;\nHSPLokhttp3/internal/http2/Http2Stream$FramingSink;-><init>(Lokhttp3/internal/http2/Http2Stream;Z)V\nHSPLokhttp3/internal/http2/Http2Stream$FramingSink;->close()V\nHSPLokhttp3/internal/http2/Http2Stream$FramingSink;->emitFrame(Z)V\nHSPLokhttp3/internal/http2/Http2Stream$FramingSink;->write(Lokio/Buffer;J)V\nHSPLokhttp3/internal/http2/Http2Stream$FramingSource;-><init>(Lokhttp3/internal/http2/Http2Stream;JZ)V\nHSPLokhttp3/internal/http2/Http2Stream$FramingSource;->close()V\nHSPLokhttp3/internal/http2/Http2Stream$FramingSource;->read(Lokio/Buffer;J)J\nHSPLokhttp3/internal/http2/Http2Stream$FramingSource;->updateConnectionFlowControl(J)V\nHSPLokhttp3/internal/http2/Http2Stream$StreamTimeout;-><init>(Lokhttp3/internal/http2/Http2Stream;)V\nHSPLokhttp3/internal/http2/Http2Stream$StreamTimeout;->exitAndThrowIfTimedOut()V\nHSPLokhttp3/internal/http2/Http2Stream;-><init>(ILokhttp3/internal/http2/Http2Connection;ZZLokhttp3/Headers;)V\nHSPLokhttp3/internal/http2/Http2Stream;->cancelStreamIfNecessary$okhttp()V\nHSPLokhttp3/internal/http2/Http2Stream;->checkOutNotClosed$okhttp()V\nHSPLokhttp3/internal/http2/Http2Stream;->getErrorCode$okhttp()Lokhttp3/internal/http2/ErrorCode;\nHSPLokhttp3/internal/http2/Http2Stream;->getSink()Lokio/Sink;\nHSPLokhttp3/internal/http2/Http2Stream;->isLocallyInitiated()Z\nHSPLokhttp3/internal/http2/Http2Stream;->isOpen()Z\nHSPLokhttp3/internal/http2/Http2Stream;->receiveHeaders(Lokhttp3/Headers;Z)V\nHSPLokhttp3/internal/http2/Http2Stream;->waitForIo$okhttp()V\nHSPLokhttp3/internal/http2/Http2Writer;-><clinit>()V\nHSPLokhttp3/internal/http2/Http2Writer;-><init>(Lokio/BufferedSink;Z)V\nHSPLokhttp3/internal/http2/Http2Writer;->applyAndAckSettings(Lokhttp3/internal/http2/Settings;)V\nHSPLokhttp3/internal/http2/Http2Writer;->close()V\nHSPLokhttp3/internal/http2/Http2Writer;->data(ZILokio/Buffer;I)V\nHSPLokhttp3/internal/http2/Http2Writer;->flush()V\nHSPLokhttp3/internal/http2/Http2Writer;->frameHeader(IIII)V\nHSPLokhttp3/internal/http2/Http2Writer;->goAway(ILokhttp3/internal/http2/ErrorCode;[B)V\nHSPLokhttp3/internal/http2/Http2Writer;->headers(ZILjava/util/List;)V\nHSPLokhttp3/internal/http2/Http2Writer;->windowUpdate(IJ)V\nHSPLokhttp3/internal/http2/Huffman$Node;-><init>()V\nHSPLokhttp3/internal/http2/Huffman$Node;-><init>(II)V\nHSPLokhttp3/internal/http2/Huffman;-><clinit>()V\nHSPLokhttp3/internal/http2/Huffman;-><init>()V\nHSPLokhttp3/internal/http2/Huffman;->addCode(III)V\nHSPLokhttp3/internal/http2/PushObserver$Companion$PushObserverCancel;-><init>()V\nHSPLokhttp3/internal/http2/PushObserver;-><clinit>()V\nHSPLokhttp3/internal/http2/Settings;-><init>()V\nHSPLokhttp3/internal/http2/Settings;->getInitialWindowSize()I\nHSPLokhttp3/internal/http2/Settings;->merge(Lokhttp3/internal/http2/Settings;)V\nHSPLokhttp3/internal/http2/Settings;->set(II)Lokhttp3/internal/http2/Settings;\nHSPLokhttp3/internal/platform/Android10Platform;-><clinit>()V\nHSPLokhttp3/internal/platform/Android10Platform;-><init>()V\nHSPLokhttp3/internal/platform/Android10Platform;->buildCertificateChainCleaner(Ljavax/net/ssl/X509TrustManager;)Lokhttp3/internal/tls/CertificateChainCleaner;\nHSPLokhttp3/internal/platform/Android10Platform;->configureTlsExtensions(Ljavax/net/ssl/SSLSocket;Ljava/lang/String;Ljava/util/List;)V\nHSPLokhttp3/internal/platform/Android10Platform;->getSelectedProtocol(Ljavax/net/ssl/SSLSocket;)Ljava/lang/String;\nHSPLokhttp3/internal/platform/Android10Platform;->getStackTraceForCloseable(Ljava/lang/String;)Ljava/lang/Object;\nHSPLokhttp3/internal/platform/Android10Platform;->isCleartextTrafficPermitted(Ljava/lang/String;)Z\nHSPLokhttp3/internal/platform/Platform$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/internal/platform/Platform$Companion;->alpnProtocolNames(Ljava/util/List;)Ljava/util/List;\nHSPLokhttp3/internal/platform/Platform$Companion;->isAndroid()Z\nHSPLokhttp3/internal/platform/Platform;-><clinit>()V\nHSPLokhttp3/internal/platform/Platform;-><init>()V\nHSPLokhttp3/internal/platform/Platform;->afterHandshake(Ljavax/net/ssl/SSLSocket;)V\nHSPLokhttp3/internal/platform/Platform;->connectSocket(Ljava/net/Socket;Ljava/net/InetSocketAddress;I)V\nHSPLokhttp3/internal/platform/Platform;->newSSLContext()Ljavax/net/ssl/SSLContext;\nHSPLokhttp3/internal/platform/Platform;->newSslSocketFactory(Ljavax/net/ssl/X509TrustManager;)Ljavax/net/ssl/SSLSocketFactory;\nHSPLokhttp3/internal/platform/Platform;->platformTrustManager()Ljavax/net/ssl/X509TrustManager;\nHSPLokhttp3/internal/platform/android/Android10SocketAdapter$Companion;->isSupported()Z\nHSPLokhttp3/internal/platform/android/Android10SocketAdapter;-><init>()V\nHSPLokhttp3/internal/platform/android/Android10SocketAdapter;->configureTlsExtensions(Ljavax/net/ssl/SSLSocket;Ljava/lang/String;Ljava/util/List;)V\nHSPLokhttp3/internal/platform/android/Android10SocketAdapter;->getSelectedProtocol(Ljavax/net/ssl/SSLSocket;)Ljava/lang/String;\nHSPLokhttp3/internal/platform/android/Android10SocketAdapter;->isSupported()Z\nHSPLokhttp3/internal/platform/android/Android10SocketAdapter;->matchesSocket(Ljavax/net/ssl/SSLSocket;)Z\nHSPLokhttp3/internal/platform/android/AndroidCertificateChainCleaner;-><init>(Ljavax/net/ssl/X509TrustManager;Landroid/net/http/X509TrustManagerExtensions;)V\nHSPLokhttp3/internal/platform/android/AndroidCertificateChainCleaner;->equals(Ljava/lang/Object;)Z\nHSPLokhttp3/internal/platform/android/AndroidCertificateChainCleaner;->hashCode()I\nHSPLokhttp3/internal/platform/android/AndroidLog;-><clinit>()V\nHSPLokhttp3/internal/platform/android/AndroidLogHandler;-><clinit>()V\nHSPLokhttp3/internal/platform/android/AndroidLogHandler;-><init>()V\nHSPLokhttp3/internal/platform/android/AndroidSocketAdapter$Companion$factory$1;-><init>(Ljava/lang/String;)V\nHSPLokhttp3/internal/platform/android/AndroidSocketAdapter$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokhttp3/internal/platform/android/AndroidSocketAdapter;-><clinit>()V\nHSPLokhttp3/internal/platform/android/BouncyCastleSocketAdapter$Companion$factory$1;-><init>()V\nHSPLokhttp3/internal/platform/android/BouncyCastleSocketAdapter;-><clinit>()V\nHSPLokhttp3/internal/platform/android/ConscryptSocketAdapter$Companion$factory$1;-><init>()V\nHSPLokhttp3/internal/platform/android/ConscryptSocketAdapter;-><clinit>()V\nHSPLokhttp3/internal/platform/android/DeferredSocketAdapter;-><init>(Lokhttp3/internal/platform/android/DeferredSocketAdapter$Factory;)V\nHSPLokhttp3/internal/platform/android/DeferredSocketAdapter;->isSupported()Z\nHSPLokhttp3/internal/tls/CertificateChainCleaner;-><init>()V\nHSPLokhttp3/internal/tls/OkHostnameVerifier;-><clinit>()V\nHSPLokhttp3/internal/tls/OkHostnameVerifier;-><init>()V\nHSPLokhttp3/internal/tls/OkHostnameVerifier;->asciiToLowercase(Ljava/lang/String;)Ljava/lang/String;\nHSPLokhttp3/internal/tls/OkHostnameVerifier;->getSubjectAltNames(Ljava/security/cert/X509Certificate;I)Ljava/util/List;\nHSPLokhttp3/internal/tls/OkHostnameVerifier;->isAscii(Ljava/lang/String;)Z\nHSPLokhttp3/internal/tls/OkHostnameVerifier;->verify(Ljava/lang/String;Ljava/security/cert/X509Certificate;)Z\nHSPLokhttp3/internal/tls/OkHostnameVerifier;->verify(Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Z\nHSPLokhttp3/logging/HttpLoggingInterceptor$Logger$Companion$DefaultLogger;-><init>()V\nHSPLokhttp3/logging/HttpLoggingInterceptor$Logger;-><clinit>()V\nHSPLokhttp3/logging/HttpLoggingInterceptor;-><init>(Lokhttp3/logging/HttpLoggingInterceptor$Logger;I)V\nHSPLokhttp3/logging/LoggingEventListener$Factory;-><init>(Lokhttp3/logging/HttpLoggingInterceptor$Logger;I)V\nHSPLokio/AsyncTimeout$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokio/AsyncTimeout$Companion;->awaitTimeout$okio()Lokio/AsyncTimeout;\nHSPLokio/AsyncTimeout$Watchdog;-><init>()V\nHSPLokio/AsyncTimeout$Watchdog;->run()V\nHSPLokio/AsyncTimeout$sink$1;-><init>(Lokio/AsyncTimeout;Lokio/Sink;)V\nHSPLokio/AsyncTimeout$sink$1;->close()V\nHSPLokio/AsyncTimeout$sink$1;->flush()V\nHSPLokio/AsyncTimeout$sink$1;->write(Lokio/Buffer;J)V\nHSPLokio/AsyncTimeout$source$1;-><init>(Lokio/AsyncTimeout;Lokio/Source;)V\nHSPLokio/AsyncTimeout$source$1;->close()V\nHSPLokio/AsyncTimeout$source$1;->read(Lokio/Buffer;J)J\nHSPLokio/AsyncTimeout;-><clinit>()V\nHSPLokio/AsyncTimeout;-><init>()V\nHSPLokio/AsyncTimeout;->enter()V\nHSPLokio/AsyncTimeout;->exit()Z\nHSPLokio/Buffer;-><init>()V\nHSPLokio/Buffer;->exhausted()Z\nHSPLokio/Buffer;->getByte(J)B\nHSPLokio/Buffer;->indexOfElement(Lokio/ByteString;)J\nHSPLokio/Buffer;->rangeEquals(JLokio/ByteString;)Z\nHSPLokio/Buffer;->read(Lokio/Buffer;J)J\nHSPLokio/Buffer;->read([BII)I\nHSPLokio/Buffer;->readByte()B\nHSPLokio/Buffer;->readByteArray(J)[B\nHSPLokio/Buffer;->readByteString()Lokio/ByteString;\nHSPLokio/Buffer;->readByteString(J)Lokio/ByteString;\nHSPLokio/Buffer;->readInt()I\nHSPLokio/Buffer;->readIntLe()I\nHSPLokio/Buffer;->readShort()S\nHSPLokio/Buffer;->readString(JLjava/nio/charset/Charset;)Ljava/lang/String;\nHSPLokio/Buffer;->readString(Ljava/nio/charset/Charset;)Ljava/lang/String;\nHSPLokio/Buffer;->skip(J)V\nHSPLokio/Buffer;->writableSegment$okio(I)Lokio/Segment;\nHSPLokio/Buffer;->write(Lokio/Buffer;J)V\nHSPLokio/Buffer;->write(Lokio/ByteString;)Lokio/Buffer;\nHSPLokio/Buffer;->write([B)Lokio/Buffer;\nHSPLokio/Buffer;->write([BII)Lokio/Buffer;\nHSPLokio/Buffer;->writeAll(Lokio/Source;)J\nHSPLokio/Buffer;->writeByte(I)Lokio/Buffer;\nHSPLokio/Buffer;->writeByte(I)Lokio/BufferedSink;\nHSPLokio/Buffer;->writeInt(I)Lokio/Buffer;\nHSPLokio/Buffer;->writeShort(I)Lokio/Buffer;\nHSPLokio/Buffer;->writeUtf8(Ljava/lang/String;)Lokio/Buffer;\nHSPLokio/Buffer;->writeUtf8(Ljava/lang/String;II)Lokio/Buffer;\nHSPLokio/ByteString$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokio/ByteString$Companion;->decodeHex(Ljava/lang/String;)Lokio/ByteString;\nHSPLokio/ByteString$Companion;->encodeUtf8(Ljava/lang/String;)Lokio/ByteString;\nHSPLokio/ByteString;-><clinit>()V\nHSPLokio/ByteString;-><init>([B)V\nHSPLokio/ByteString;->compareTo(Ljava/lang/Object;)I\nHSPLokio/ByteString;->compareTo(Lokio/ByteString;)I\nHSPLokio/ByteString;->equals(Ljava/lang/Object;)Z\nHSPLokio/ByteString;->getSize$okio()I\nHSPLokio/ByteString;->hashCode()I\nHSPLokio/ByteString;->indexOf$default(Lokio/ByteString;Lokio/ByteString;IILjava/lang/Object;)I\nHSPLokio/ByteString;->indexOf([BI)I\nHSPLokio/ByteString;->internalArray$okio()[B\nHSPLokio/ByteString;->internalGet$okio(I)B\nHSPLokio/ByteString;->rangeEquals(ILokio/ByteString;II)Z\nHSPLokio/ByteString;->rangeEquals(I[BII)Z\nHSPLokio/ByteString;->startsWith(Lokio/ByteString;)Z\nHSPLokio/ByteString;->toAsciiLowercase()Lokio/ByteString;\nHSPLokio/ByteString;->utf8()Ljava/lang/String;\nHSPLokio/ByteString;->write$okio(Lokio/Buffer;II)V\nHSPLokio/FileSystem;-><clinit>()V\nHSPLokio/FileSystem;-><init>()V\nHSPLokio/ForwardingFileSystem;-><init>(Lokio/FileSystem;)V\nHSPLokio/ForwardingSink;-><init>(Lokio/Sink;)V\nHSPLokio/ForwardingSink;->close()V\nHSPLokio/ForwardingSink;->write(Lokio/Buffer;J)V\nHSPLokio/ForwardingSource;-><init>(Lokio/Source;)V\nHSPLokio/ForwardingSource;->close()V\nHSPLokio/GzipSource;-><init>(Lokio/Source;)V\nHSPLokio/GzipSource;->checkEqual(Ljava/lang/String;II)V\nHSPLokio/GzipSource;->close()V\nHSPLokio/GzipSource;->read(Lokio/Buffer;J)J\nHSPLokio/GzipSource;->updateCrc(Lokio/Buffer;JJ)V\nHSPLokio/InflaterSource;-><init>(Lokio/BufferedSource;Ljava/util/zip/Inflater;)V\nHSPLokio/InflaterSource;->close()V\nHSPLokio/InflaterSource;->read(Lokio/Buffer;J)J\nHSPLokio/InputStreamSource;-><init>(Ljava/io/InputStream;Lokio/Timeout;)V\nHSPLokio/InputStreamSource;->close()V\nHSPLokio/InputStreamSource;->read(Lokio/Buffer;J)J\nHSPLokio/JvmSystemFileSystem;-><init>()V\nHSPLokio/NioSystemFileSystem;-><init>()V\nHSPLokio/Okio;->buffer(Lokio/Sink;)Lokio/BufferedSink;\nHSPLokio/Okio;->buffer(Lokio/Source;)Lokio/BufferedSource;\nHSPLokio/Okio;->sink(Ljava/net/Socket;)Lokio/Sink;\nHSPLokio/Okio;->source(Ljava/net/Socket;)Lokio/Source;\nHSPLokio/Okio__JvmOkioKt;-><clinit>()V\nHSPLokio/Options$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokio/Options$Companion;->buildTrieRecursive(JLokio/Buffer;ILjava/util/List;IILjava/util/List;)V\nHSPLokio/Options$Companion;->getIntCount(Lokio/Buffer;)J\nHSPLokio/Options;-><clinit>()V\nHSPLokio/Options;-><init>([Lokio/ByteString;[ILandroidx/lifecycle/viewmodel/R$id;)V\nHSPLokio/OutputStreamSink;-><init>(Ljava/io/OutputStream;Lokio/Timeout;)V\nHSPLokio/OutputStreamSink;->close()V\nHSPLokio/OutputStreamSink;->flush()V\nHSPLokio/OutputStreamSink;->write(Lokio/Buffer;J)V\nHSPLokio/Path$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokio/Path$Companion;->get$default(Lokio/Path$Companion;Ljava/io/File;ZI)Lokio/Path;\nHSPLokio/Path$Companion;->get(Ljava/lang/String;Z)Lokio/Path;\nHSPLokio/Path;-><clinit>()V\nHSPLokio/Path;-><init>(Lokio/ByteString;)V\nHSPLokio/Path;->resolve(Ljava/lang/String;)Lokio/Path;\nHSPLokio/Path;->volumeLetter()Ljava/lang/Character;\nHSPLokio/RealBufferedSink;-><init>(Lokio/Sink;)V\nHSPLokio/RealBufferedSink;->close()V\nHSPLokio/RealBufferedSink;->emitCompleteSegments()Lokio/BufferedSink;\nHSPLokio/RealBufferedSink;->flush()V\nHSPLokio/RealBufferedSink;->write(Lokio/Buffer;J)V\nHSPLokio/RealBufferedSink;->write(Lokio/ByteString;)Lokio/BufferedSink;\nHSPLokio/RealBufferedSink;->write([BII)Lokio/BufferedSink;\nHSPLokio/RealBufferedSink;->writeByte(I)Lokio/BufferedSink;\nHSPLokio/RealBufferedSink;->writeInt(I)Lokio/BufferedSink;\nHSPLokio/RealBufferedSink;->writeShort(I)Lokio/BufferedSink;\nHSPLokio/RealBufferedSource;-><init>(Lokio/Source;)V\nHSPLokio/RealBufferedSource;->close()V\nHSPLokio/RealBufferedSource;->exhausted()Z\nHSPLokio/RealBufferedSource;->getBuffer()Lokio/Buffer;\nHSPLokio/RealBufferedSource;->read(Lokio/Buffer;J)J\nHSPLokio/RealBufferedSource;->readByte()B\nHSPLokio/RealBufferedSource;->readByteString(J)Lokio/ByteString;\nHSPLokio/RealBufferedSource;->readInt()I\nHSPLokio/RealBufferedSource;->readIntLe()I\nHSPLokio/RealBufferedSource;->readShort()S\nHSPLokio/RealBufferedSource;->readString(Ljava/nio/charset/Charset;)Ljava/lang/String;\nHSPLokio/RealBufferedSource;->request(J)Z\nHSPLokio/RealBufferedSource;->require(J)V\nHSPLokio/RealBufferedSource;->select(Lokio/Options;)I\nHSPLokio/RealBufferedSource;->skip(J)V\nHSPLokio/Segment;-><init>()V\nHSPLokio/Segment;-><init>([BIIZZ)V\nHSPLokio/Segment;->pop()Lokio/Segment;\nHSPLokio/Segment;->push(Lokio/Segment;)Lokio/Segment;\nHSPLokio/Segment;->writeTo(Lokio/Segment;I)V\nHSPLokio/SegmentPool;-><clinit>()V\nHSPLokio/SegmentPool;-><init>()V\nHSPLokio/SegmentPool;->firstRef()Ljava/util/concurrent/atomic/AtomicReference;\nHSPLokio/SegmentPool;->recycle(Lokio/Segment;)V\nHSPLokio/SegmentPool;->take()Lokio/Segment;\nHSPLokio/SocketAsyncTimeout;-><init>(Ljava/net/Socket;)V\nHSPLokio/Timeout$Companion$NONE$1;-><init>()V\nHSPLokio/Timeout;-><clinit>()V\nHSPLokio/Timeout;-><init>()V\nHSPLokio/Timeout;->throwIfReached()V\nHSPLokio/Timeout;->timeout(JLjava/util/concurrent/TimeUnit;)Lokio/Timeout;\nHSPLokio/_UtilKt;->arrayRangeEquals([BI[BII)Z\nHSPLokio/_UtilKt;->checkOffsetAndCount(JJJ)V\nHSPLokio/internal/ResourceFileSystem$Companion;-><init>(Landroidx/lifecycle/viewmodel/R$id;)V\nHSPLokio/internal/ResourceFileSystem$roots$2;-><init>(Ljava/lang/ClassLoader;)V\nHSPLokio/internal/ResourceFileSystem;-><clinit>()V\nHSPLokio/internal/ResourceFileSystem;-><init>(Ljava/lang/ClassLoader;Z)V\nHSPLokio/internal/_BufferKt;-><clinit>()V\nHSPLokio/internal/_BufferKt;->selectPrefix(Lokio/Buffer;Lokio/Options;Z)I\nHSPLokio/internal/_ByteStringKt;-><clinit>()V\nHSPLokio/internal/_ByteStringKt;->access$decodeHexDigit(C)I\nHSPLokio/internal/_PathKt;-><clinit>()V\nHSPLokio/internal/_PathKt;->access$rootLength(Lokio/Path;)I\nHSPLokio/internal/_PathKt;->commonResolve(Lokio/Path;Lokio/Path;Z)Lokio/Path;\nHSPLokio/internal/_PathKt;->getSlash(Lokio/Path;)Lokio/ByteString;\nHSPLokio/internal/_PathKt;->toPath(Lokio/Buffer;Z)Lokio/Path;\nHSPLokio/internal/_PathKt;->toSlash(B)Lokio/ByteString;\nHSPLokio/internal/_PathKt;->toSlash(Ljava/lang/String;)Lokio/ByteString;\nLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda0;\nLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda1;\nLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda2;\nLandroidx/activity/ComponentActivity$1;\nLandroidx/activity/ComponentActivity$2;\nLandroidx/activity/ComponentActivity$3;\nLandroidx/activity/ComponentActivity$4;\nLandroidx/activity/ComponentActivity$5;\nLandroidx/activity/ComponentActivity$NonConfigurationInstances;\nLandroidx/activity/ComponentActivity;\nLandroidx/activity/OnBackPressedCallback;\nLandroidx/activity/OnBackPressedDispatcher;\nLandroidx/activity/contextaware/ContextAwareHelper;\nLandroidx/activity/contextaware/OnContextAvailableListener;\nLandroidx/activity/result/ActivityResult$$ExternalSyntheticOutline0;\nLandroidx/activity/result/ActivityResult;\nLandroidx/activity/result/ActivityResultCallback;\nLandroidx/activity/result/ActivityResultRegistry$CallbackAndContract;\nLandroidx/activity/result/ActivityResultRegistry;\nLandroidx/arch/core/executor/ArchTaskExecutor;\nLandroidx/arch/core/executor/DefaultTaskExecutor$1;\nLandroidx/arch/core/executor/DefaultTaskExecutor;\nLandroidx/arch/core/executor/TaskExecutor;\nLandroidx/arch/core/internal/FastSafeIterableMap;\nLandroidx/arch/core/internal/SafeIterableMap$AscendingIterator;\nLandroidx/arch/core/internal/SafeIterableMap$DescendingIterator;\nLandroidx/arch/core/internal/SafeIterableMap$Entry;\nLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;\nLandroidx/arch/core/internal/SafeIterableMap$ListIterator;\nLandroidx/arch/core/internal/SafeIterableMap$SupportRemove;\nLandroidx/arch/core/internal/SafeIterableMap;\nLandroidx/core/app/ComponentActivity;\nLandroidx/core/app/CoreComponentFactory$CompatWrapped;\nLandroidx/core/app/CoreComponentFactory;\nLandroidx/core/view/KeyEventDispatcher;\nLandroidx/core/view/MenuHostHelper;\nLandroidx/core/view/MenuProvider;\nLandroidx/core/view/ViewCompat;\nLandroidx/lifecycle/ClassesInfoCache$CallbackInfo;\nLandroidx/lifecycle/ClassesInfoCache;\nLandroidx/lifecycle/CompositeGeneratedAdaptersObserver;\nLandroidx/lifecycle/DispatchQueue$dispatchAndEnqueue$$inlined$with$lambda$1;\nLandroidx/lifecycle/DispatchQueue;\nLandroidx/lifecycle/FullLifecycleObserver;\nLandroidx/lifecycle/FullLifecycleObserverAdapter;\nLandroidx/lifecycle/GeneratedAdapter;\nLandroidx/lifecycle/Lifecycle$1;\nLandroidx/lifecycle/Lifecycle$Event;\nLandroidx/lifecycle/Lifecycle$State;\nLandroidx/lifecycle/Lifecycle;\nLandroidx/lifecycle/LifecycleController$observer$1;\nLandroidx/lifecycle/LifecycleController;\nLandroidx/lifecycle/LifecycleCoroutineScope$launchWhenResumed$1;\nLandroidx/lifecycle/LifecycleCoroutineScope;\nLandroidx/lifecycle/LifecycleCoroutineScopeImpl$register$1;\nLandroidx/lifecycle/LifecycleCoroutineScopeImpl;\nLandroidx/lifecycle/LifecycleEventObserver;\nLandroidx/lifecycle/LifecycleKt;\nLandroidx/lifecycle/LifecycleObserver;\nLandroidx/lifecycle/LifecycleOwner;\nLandroidx/lifecycle/LifecycleOwnerKt;\nLandroidx/lifecycle/LifecycleRegistry$ObserverWithState;\nLandroidx/lifecycle/LifecycleRegistry;\nLandroidx/lifecycle/LifecycleRegistryOwner;\nLandroidx/lifecycle/Lifecycling;\nLandroidx/lifecycle/LiveData$ObserverWrapper;\nLandroidx/lifecycle/OnLifecycleEvent;\nLandroidx/lifecycle/PausingDispatcher;\nLandroidx/lifecycle/PausingDispatcherKt$whenStateAtLeast$2;\nLandroidx/lifecycle/ReflectiveGenericLifecycleObserver;\nLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;\nLandroidx/lifecycle/ReportFragment;\nLandroidx/lifecycle/SingleGeneratedAdapterObserver;\nLandroidx/lifecycle/ViewModel;\nLandroidx/lifecycle/ViewModelStoreOwner;\nLandroidx/lifecycle/runtime/R$id;\nLandroidx/lifecycle/viewmodel/R$id;\nLandroidx/profileinstaller/FileSectionType$EnumUnboxingSharedUtility;\nLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;\nLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;\nLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;\nLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda0;\nLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;\nLandroidx/profileinstaller/ProfileInstallerInitializer$Handler28Impl;\nLandroidx/profileinstaller/ProfileInstallerInitializer$Result;\nLandroidx/profileinstaller/ProfileInstallerInitializer;\nLandroidx/savedstate/R$id;\nLandroidx/savedstate/Recreator$SavedStateProvider;\nLandroidx/savedstate/Recreator;\nLandroidx/savedstate/SavedStateRegistry$1;\nLandroidx/savedstate/SavedStateRegistry$AutoRecreated;\nLandroidx/savedstate/SavedStateRegistry$SavedStateProvider;\nLandroidx/savedstate/SavedStateRegistry;\nLandroidx/savedstate/SavedStateRegistryController;\nLandroidx/savedstate/SavedStateRegistryOwner;\nLandroidx/startup/AppInitializer;\nLandroidx/startup/InitializationProvider;\nLandroidx/startup/Initializer;\nLandroidx/startup/R$string;\nLandroidx/startup/StartupException;\nLandroidx/tracing/Trace;\nLandroidx/tracing/TraceApi18Impl;\nLkotlin/ExceptionsKt;\nLkotlin/Function;\nLkotlin/Lazy;\nLkotlin/NoWhenBranchMatchedException;\nLkotlin/Pair;\nLkotlin/Result$Failure;\nLkotlin/Result;\nLkotlin/ResultKt;\nLkotlin/SynchronizedLazyImpl;\nLkotlin/TuplesKt;\nLkotlin/UNINITIALIZED_VALUE;\nLkotlin/UninitializedPropertyAccessException;\nLkotlin/Unit;\nLkotlin/collections/AbstractCollection$toString$1;\nLkotlin/collections/AbstractCollection;\nLkotlin/collections/AbstractList$IteratorImpl;\nLkotlin/collections/AbstractList$ListIteratorImpl$$ExternalSyntheticOutline0;\nLkotlin/collections/AbstractList$ListIteratorImpl;\nLkotlin/collections/AbstractList$SubList;\nLkotlin/collections/AbstractList;\nLkotlin/collections/AbstractMutableList;\nLkotlin/collections/ArrayAsCollection;\nLkotlin/collections/ArrayDeque;\nLkotlin/collections/ArraysKt__ArraysKt;\nLkotlin/collections/ArraysKt___ArraysKt;\nLkotlin/collections/CollectionsKt__IteratorsJVMKt;\nLkotlin/collections/CollectionsKt__MutableCollectionsJVMKt;\nLkotlin/collections/CollectionsKt__ReversedViewsKt;\nLkotlin/collections/CollectionsKt___CollectionsKt$$ExternalSyntheticOutline0;\nLkotlin/collections/CollectionsKt___CollectionsKt$asSequence$$inlined$Sequence$1;\nLkotlin/collections/CollectionsKt___CollectionsKt;\nLkotlin/collections/EmptyIterator;\nLkotlin/collections/EmptyList;\nLkotlin/collections/EmptyMap;\nLkotlin/collections/EmptySet;\nLkotlin/collections/IntIterator;\nLkotlin/collections/MapsKt__MapsJVMKt;\nLkotlin/collections/MapsKt___MapsKt;\nLkotlin/collections/SetsKt__SetsKt;\nLkotlin/collections/builders/ListBuilder$Itr;\nLkotlin/collections/builders/ListBuilder;\nLkotlin/comparisons/NaturalOrderComparator;\nLkotlin/comparisons/ReverseOrderComparator;\nLkotlin/coroutines/AbstractCoroutineContextElement;\nLkotlin/coroutines/AbstractCoroutineContextKey;\nLkotlin/coroutines/CombinedContext$toString$1;\nLkotlin/coroutines/CombinedContext;\nLkotlin/coroutines/Continuation;\nLkotlin/coroutines/ContinuationInterceptor$Key;\nLkotlin/coroutines/ContinuationInterceptor;\nLkotlin/coroutines/CoroutineContext$DefaultImpls;\nLkotlin/coroutines/CoroutineContext$Element$DefaultImpls;\nLkotlin/coroutines/CoroutineContext$Element;\nLkotlin/coroutines/CoroutineContext$Key;\nLkotlin/coroutines/CoroutineContext$plus$1;\nLkotlin/coroutines/CoroutineContext;\nLkotlin/coroutines/EmptyCoroutineContext;\nLkotlin/coroutines/intrinsics/CoroutineSingletons;\nLkotlin/coroutines/intrinsics/IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3;\nLkotlin/coroutines/intrinsics/IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4;\nLkotlin/coroutines/jvm/internal/BaseContinuationImpl;\nLkotlin/coroutines/jvm/internal/CompletedContinuation;\nLkotlin/coroutines/jvm/internal/ContinuationImpl;\nLkotlin/coroutines/jvm/internal/CoroutineStackFrame;\nLkotlin/coroutines/jvm/internal/DebugMetadata;\nLkotlin/coroutines/jvm/internal/ModuleNameRetriever$Cache;\nLkotlin/coroutines/jvm/internal/ModuleNameRetriever;\nLkotlin/coroutines/jvm/internal/RestrictedContinuationImpl;\nLkotlin/coroutines/jvm/internal/SuspendLambda;\nLkotlin/internal/PlatformImplementations$ReflectThrowable;\nLkotlin/internal/PlatformImplementations;\nLkotlin/internal/PlatformImplementationsKt;\nLkotlin/internal/jdk7/JDK7PlatformImplementations;\nLkotlin/internal/jdk8/JDK8PlatformImplementations;\nLkotlin/io/CloseableKt;\nLkotlin/jvm/functions/Function0;\nLkotlin/jvm/functions/Function10;\nLkotlin/jvm/functions/Function11;\nLkotlin/jvm/functions/Function12;\nLkotlin/jvm/functions/Function13;\nLkotlin/jvm/functions/Function14;\nLkotlin/jvm/functions/Function15;\nLkotlin/jvm/functions/Function16;\nLkotlin/jvm/functions/Function17;\nLkotlin/jvm/functions/Function18;\nLkotlin/jvm/functions/Function19;\nLkotlin/jvm/functions/Function1;\nLkotlin/jvm/functions/Function20;\nLkotlin/jvm/functions/Function21;\nLkotlin/jvm/functions/Function22;\nLkotlin/jvm/functions/Function2;\nLkotlin/jvm/functions/Function3;\nLkotlin/jvm/functions/Function4;\nLkotlin/jvm/functions/Function5;\nLkotlin/jvm/functions/Function6;\nLkotlin/jvm/functions/Function7;\nLkotlin/jvm/functions/Function8;\nLkotlin/jvm/functions/Function9;\nLkotlin/jvm/internal/ArrayIterator;\nLkotlin/jvm/internal/CallableReference;\nLkotlin/jvm/internal/ClassBasedDeclarationContainer;\nLkotlin/jvm/internal/ClassReference;\nLkotlin/jvm/internal/CollectionToArray;\nLkotlin/jvm/internal/FunctionBase;\nLkotlin/jvm/internal/Intrinsics;\nLkotlin/jvm/internal/Lambda;\nLkotlin/jvm/internal/PropertyReference0Impl;\nLkotlin/jvm/internal/PropertyReference;\nLkotlin/jvm/internal/Ref$ObjectRef;\nLkotlin/jvm/internal/Reflection;\nLkotlin/jvm/internal/ReflectionFactory;\nLkotlin/jvm/internal/TypeIntrinsics;\nLkotlin/jvm/internal/markers/KMappedMarker;\nLkotlin/jvm/internal/markers/KMutableList;\nLkotlin/random/AbstractPlatformRandom;\nLkotlin/random/FallbackThreadLocalRandom$implStorage$1;\nLkotlin/random/FallbackThreadLocalRandom;\nLkotlin/random/Random$Default;\nLkotlin/random/Random;\nLkotlin/ranges/IntProgression;\nLkotlin/ranges/IntProgressionIterator;\nLkotlin/ranges/IntRange;\nLkotlin/ranges/RangesKt___RangesKt;\nLkotlin/reflect/KCallable;\nLkotlin/reflect/KClass;\nLkotlin/reflect/KDeclarationContainer;\nLkotlin/reflect/KProperty;\nLkotlin/sequences/ConstrainedOnceSequence;\nLkotlin/sequences/Sequence;\nLkotlin/sequences/SequencesKt;\nLkotlin/sequences/SequencesKt__SequencesKt$asSequence$$inlined$Sequence$1;\nLkotlin/sequences/SequencesKt___SequencesJvmKt;\nLkotlin/sequences/SequencesKt___SequencesKt$asIterable$$inlined$Iterable$1;\nLkotlin/sequences/TransformingSequence$iterator$1;\nLkotlin/sequences/TransformingSequence;\nLkotlin/text/CharsKt__CharKt;\nLkotlin/text/Charsets;\nLkotlin/text/DelimitedRangesSequence;\nLkotlin/text/MatchGroup;\nLkotlin/text/MatchGroupCollection;\nLkotlin/text/MatchResult;\nLkotlin/text/MatcherMatchResult$groupValues$1;\nLkotlin/text/MatcherMatchResult$groups$1$iterator$1;\nLkotlin/text/MatcherMatchResult$groups$1;\nLkotlin/text/MatcherMatchResult;\nLkotlin/text/Regex;\nLkotlin/text/StringsKt__IndentKt;\nLkotlin/text/StringsKt__RegexExtensionsKt;\nLkotlin/text/StringsKt__StringBuilderKt;\nLkotlin/text/StringsKt__StringNumberConversionsKt;\nLkotlin/text/StringsKt__StringsJVMKt;\nLkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$1;\nLkotlin/text/StringsKt__StringsKt$rangesDelimitedBy$2;\nLkotlin/text/StringsKt__StringsKt$splitToSequence$1;\nLkotlin/text/StringsKt__StringsKt;\nLkotlin/text/StringsKt___StringsKt;\nLkotlin/time/Duration;\nLkotlin/time/DurationJvmKt;\nLkotlin/time/DurationUnit;\nLkotlinx/coroutines/AbstractCoroutine;\nLkotlinx/coroutines/Active;\nLkotlinx/coroutines/BlockingEventLoop;\nLkotlinx/coroutines/CancelHandler;\nLkotlinx/coroutines/CancellableContinuation;\nLkotlinx/coroutines/CancellableContinuationImpl;\nLkotlinx/coroutines/CancelledContinuation;\nLkotlinx/coroutines/ChildContinuation;\nLkotlinx/coroutines/ChildHandle;\nLkotlinx/coroutines/ChildHandleNode;\nLkotlinx/coroutines/ChildJob;\nLkotlinx/coroutines/CompletedContinuation;\nLkotlinx/coroutines/CompletedExceptionally;\nLkotlinx/coroutines/CompletedWithCancellation;\nLkotlinx/coroutines/CompletionHandlerBase;\nLkotlinx/coroutines/CompletionHandlerException;\nLkotlinx/coroutines/CompletionStateKt;\nLkotlinx/coroutines/CopyableThreadContextElement;\nLkotlinx/coroutines/CoroutineContextKt$foldCopiesForChildCoroutine$1;\nLkotlinx/coroutines/CoroutineContextKt$foldCopiesForChildCoroutine$hasToCopy$1;\nLkotlinx/coroutines/CoroutineContextKt;\nLkotlinx/coroutines/CoroutineDispatcher$Key$1;\nLkotlinx/coroutines/CoroutineDispatcher$Key;\nLkotlinx/coroutines/CoroutineDispatcher;\nLkotlinx/coroutines/CoroutineExceptionHandler$Key;\nLkotlinx/coroutines/CoroutineExceptionHandler;\nLkotlinx/coroutines/CoroutineExceptionHandlerImplKt$$ExternalSyntheticServiceLoad0;\nLkotlinx/coroutines/CoroutineExceptionHandlerImplKt;\nLkotlinx/coroutines/CoroutineExceptionHandlerKt;\nLkotlinx/coroutines/CoroutineScope;\nLkotlinx/coroutines/CoroutinesInternalError;\nLkotlinx/coroutines/DebugStringsKt;\nLkotlinx/coroutines/DefaultExecutor;\nLkotlinx/coroutines/DefaultExecutorKt;\nLkotlinx/coroutines/Delay;\nLkotlinx/coroutines/DispatchedCoroutine;\nLkotlinx/coroutines/DispatchedTask;\nLkotlinx/coroutines/DispatchedTaskKt;\nLkotlinx/coroutines/Dispatchers;\nLkotlinx/coroutines/DisposableHandle;\nLkotlinx/coroutines/Empty;\nLkotlinx/coroutines/EventLoop;\nLkotlinx/coroutines/EventLoopImplBase$DelayedTask;\nLkotlinx/coroutines/EventLoopImplBase$DelayedTaskQueue;\nLkotlinx/coroutines/EventLoopImplBase;\nLkotlinx/coroutines/EventLoopImplPlatform;\nLkotlinx/coroutines/EventLoop_commonKt;\nLkotlinx/coroutines/ExecutorCoroutineDispatcher;\nLkotlinx/coroutines/InactiveNodeList;\nLkotlinx/coroutines/Incomplete;\nLkotlinx/coroutines/IncompleteStateBox;\nLkotlinx/coroutines/InvokeOnCancel;\nLkotlinx/coroutines/InvokeOnCancelling;\nLkotlinx/coroutines/InvokeOnCompletion;\nLkotlinx/coroutines/Job$DefaultImpls;\nLkotlinx/coroutines/Job$Key;\nLkotlinx/coroutines/Job;\nLkotlinx/coroutines/JobCancellationException;\nLkotlinx/coroutines/JobCancellingNode;\nLkotlinx/coroutines/JobImpl;\nLkotlinx/coroutines/JobKt;\nLkotlinx/coroutines/JobNode;\nLkotlinx/coroutines/JobSupport$ChildCompletion;\nLkotlinx/coroutines/JobSupport$Finishing;\nLkotlinx/coroutines/JobSupport$addLastAtomic$$inlined$addLastIf$1;\nLkotlinx/coroutines/JobSupport;\nLkotlinx/coroutines/JobSupportKt;\nLkotlinx/coroutines/LazyStandaloneCoroutine;\nLkotlinx/coroutines/MainCoroutineDispatcher;\nLkotlinx/coroutines/NodeList;\nLkotlinx/coroutines/NonDisposableHandle;\nLkotlinx/coroutines/NotCompleted;\nLkotlinx/coroutines/ParentJob;\nLkotlinx/coroutines/StandaloneCoroutine;\nLkotlinx/coroutines/SupervisorJobImpl;\nLkotlinx/coroutines/ThreadContextElement;\nLkotlinx/coroutines/ThreadLocalEventLoop;\nLkotlinx/coroutines/Unconfined;\nLkotlinx/coroutines/UndispatchedCoroutine;\nLkotlinx/coroutines/UndispatchedMarker;\nLkotlinx/coroutines/YieldContext$Key;\nLkotlinx/coroutines/YieldContext;\nLkotlinx/coroutines/android/AndroidDispatcherFactory;\nLkotlinx/coroutines/android/AndroidExceptionPreHandler;\nLkotlinx/coroutines/android/HandlerContext;\nLkotlinx/coroutines/android/HandlerDispatcher;\nLkotlinx/coroutines/android/HandlerDispatcherKt;\nLkotlinx/coroutines/internal/ArrayQueue;\nLkotlinx/coroutines/internal/AtomicKt;\nLkotlinx/coroutines/internal/AtomicOp;\nLkotlinx/coroutines/internal/DispatchedContinuation;\nLkotlinx/coroutines/internal/LimitedDispatcher;\nLkotlinx/coroutines/internal/LockFreeLinkedListHead;\nLkotlinx/coroutines/internal/LockFreeLinkedListNode$CondAddOp;\nLkotlinx/coroutines/internal/LockFreeLinkedListNode$toString$1;\nLkotlinx/coroutines/internal/LockFreeLinkedListNode;\nLkotlinx/coroutines/internal/LockFreeTaskQueue;\nLkotlinx/coroutines/internal/LockFreeTaskQueueCore$Placeholder;\nLkotlinx/coroutines/internal/LockFreeTaskQueueCore;\nLkotlinx/coroutines/internal/MainDispatcherFactory;\nLkotlinx/coroutines/internal/MainDispatcherLoader$$ExternalSyntheticServiceLoad0;\nLkotlinx/coroutines/internal/MainDispatcherLoader;\nLkotlinx/coroutines/internal/OpDescriptor;\nLkotlinx/coroutines/internal/Removed;\nLkotlinx/coroutines/internal/ScopeCoroutine;\nLkotlinx/coroutines/internal/Symbol;\nLkotlinx/coroutines/internal/SystemPropsKt;\nLkotlinx/coroutines/internal/SystemPropsKt__SystemPropsKt;\nLkotlinx/coroutines/internal/ThreadContextKt$countAll$1;\nLkotlinx/coroutines/internal/ThreadContextKt$findOne$1;\nLkotlinx/coroutines/internal/ThreadContextKt$updateState$1;\nLkotlinx/coroutines/internal/ThreadContextKt;\nLkotlinx/coroutines/internal/ThreadSafeHeap;\nLkotlinx/coroutines/internal/ThreadSafeHeapNode;\nLkotlinx/coroutines/internal/ThreadState;\nLkotlinx/coroutines/intrinsics/CancellableKt;\nLkotlinx/coroutines/intrinsics/UndispatchedKt;\nLkotlinx/coroutines/scheduling/CoroutineScheduler$Worker;\nLkotlinx/coroutines/scheduling/CoroutineScheduler;\nLkotlinx/coroutines/scheduling/DefaultIoScheduler;\nLkotlinx/coroutines/scheduling/DefaultScheduler;\nLkotlinx/coroutines/scheduling/GlobalQueue;\nLkotlinx/coroutines/scheduling/NanoTimeSource;\nLkotlinx/coroutines/scheduling/SchedulerCoroutineDispatcher;\nLkotlinx/coroutines/scheduling/Task;\nLkotlinx/coroutines/scheduling/TaskContext;\nLkotlinx/coroutines/scheduling/TaskContextImpl;\nLkotlinx/coroutines/scheduling/TaskImpl;\nLkotlinx/coroutines/scheduling/TasksKt;\nLkotlinx/coroutines/scheduling/UnlimitedIoScheduler;\nLkotlinx/coroutines/scheduling/WorkQueue;\nLokhttp3/Address;\nLokhttp3/Authenticator$Companion$AuthenticatorNone;\nLokhttp3/Authenticator;\nLokhttp3/Cache$CacheResponseBody;\nLokhttp3/Cache$Entry;\nLokhttp3/Cache$RealCacheRequest;\nLokhttp3/Cache;\nLokhttp3/CacheControl$Companion;\nLokhttp3/CacheControl;\nLokhttp3/Call;\nLokhttp3/Callback;\nLokhttp3/CertificatePinner$Companion;\nLokhttp3/CertificatePinner$Pin;\nLokhttp3/CertificatePinner$check$1;\nLokhttp3/CertificatePinner;\nLokhttp3/CipherSuite$Companion$ORDER_BY_NAME$1;\nLokhttp3/CipherSuite$Companion;\nLokhttp3/CipherSuite;\nLokhttp3/Connection;\nLokhttp3/ConnectionPool;\nLokhttp3/ConnectionSpec$Builder;\nLokhttp3/ConnectionSpec;\nLokhttp3/Cookie;\nLokhttp3/CookieJar$Companion$NoCookies;\nLokhttp3/CookieJar;\nLokhttp3/Dispatcher;\nLokhttp3/Dns$Companion$DnsSystem;\nLokhttp3/Dns;\nLokhttp3/EventListener$Companion$NONE$1;\nLokhttp3/EventListener$Factory;\nLokhttp3/EventListener;\nLokhttp3/Handshake$Companion$handshake$1;\nLokhttp3/Handshake$peerCertificates$2;\nLokhttp3/Handshake;\nLokhttp3/Headers$Builder;\nLokhttp3/Headers;\nLokhttp3/HttpUrl$Builder;\nLokhttp3/HttpUrl$Companion;\nLokhttp3/HttpUrl;\nLokhttp3/Interceptor$Chain;\nLokhttp3/Interceptor;\nLokhttp3/JvmCallExtensionsKt$executeAsync$2$1;\nLokhttp3/JvmCallExtensionsKt$executeAsync$2$2$onResponse$1;\nLokhttp3/JvmCallExtensionsKt$executeAsync$2$2;\nLokhttp3/MediaType;\nLokhttp3/OkHttpClient$Builder;\nLokhttp3/OkHttpClient$Companion;\nLokhttp3/OkHttpClient;\nLokhttp3/Protocol$Companion;\nLokhttp3/Protocol;\nLokhttp3/Request$Builder;\nLokhttp3/Request;\nLokhttp3/RequestBody;\nLokhttp3/Response$Builder;\nLokhttp3/Response;\nLokhttp3/ResponseBody;\nLokhttp3/Route;\nLokhttp3/TlsVersion;\nLokhttp3/internal/Internal;\nLokhttp3/internal/_CacheControlCommonKt;\nLokhttp3/internal/_HeadersCommonKt;\nLokhttp3/internal/_HostnamesCommonKt;\nLokhttp3/internal/_MediaTypeCommonKt;\nLokhttp3/internal/_RequestBodyCommonKt$commonToRequestBody$1;\nLokhttp3/internal/_ResponseBodyCommonKt$commonAsResponseBody$1;\nLokhttp3/internal/_ResponseCommonKt;\nLokhttp3/internal/_UtilCommonKt;\nLokhttp3/internal/_UtilJvmKt$$ExternalSyntheticLambda0;\nLokhttp3/internal/_UtilJvmKt$$ExternalSyntheticLambda1;\nLokhttp3/internal/_UtilJvmKt;\nLokhttp3/internal/cache/CacheInterceptor$Companion;\nLokhttp3/internal/cache/CacheInterceptor$cacheWritingResponse$cacheWritingSource$1;\nLokhttp3/internal/cache/CacheInterceptor;\nLokhttp3/internal/cache/CacheRequest;\nLokhttp3/internal/cache/CacheStrategy;\nLokhttp3/internal/cache/DiskLruCache$Editor;\nLokhttp3/internal/cache/DiskLruCache$Entry;\nLokhttp3/internal/cache/DiskLruCache$Snapshot;\nLokhttp3/internal/cache/DiskLruCache$cleanupTask$1;\nLokhttp3/internal/cache/DiskLruCache$fileSystem$1;\nLokhttp3/internal/cache/DiskLruCache$newJournalWriter$faultHidingSink$1;\nLokhttp3/internal/cache/DiskLruCache;\nLokhttp3/internal/cache/FaultHidingSink;\nLokhttp3/internal/concurrent/Task;\nLokhttp3/internal/concurrent/TaskLoggerKt;\nLokhttp3/internal/concurrent/TaskQueue$execute$1;\nLokhttp3/internal/concurrent/TaskQueue$schedule$2;\nLokhttp3/internal/concurrent/TaskQueue;\nLokhttp3/internal/concurrent/TaskRunner$Backend;\nLokhttp3/internal/concurrent/TaskRunner$RealBackend;\nLokhttp3/internal/concurrent/TaskRunner$runnable$1;\nLokhttp3/internal/concurrent/TaskRunner;\nLokhttp3/internal/connection/ConnectInterceptor;\nLokhttp3/internal/connection/ConnectPlan$WhenMappings;\nLokhttp3/internal/connection/ConnectPlan$connectTls$1;\nLokhttp3/internal/connection/ConnectPlan$connectTls$handshake$1;\nLokhttp3/internal/connection/ConnectPlan;\nLokhttp3/internal/connection/Exchange$RequestBodySink;\nLokhttp3/internal/connection/Exchange$ResponseBodySource;\nLokhttp3/internal/connection/Exchange;\nLokhttp3/internal/connection/ExchangeFinder;\nLokhttp3/internal/connection/FastFallbackExchangeFinder;\nLokhttp3/internal/connection/RealCall$AsyncCall;\nLokhttp3/internal/connection/RealCall$CallReference;\nLokhttp3/internal/connection/RealCall$timeout$1;\nLokhttp3/internal/connection/RealCall;\nLokhttp3/internal/connection/RealConnection;\nLokhttp3/internal/connection/RealConnectionPool$cleanupTask$1;\nLokhttp3/internal/connection/RealConnectionPool;\nLokhttp3/internal/connection/RealRoutePlanner;\nLokhttp3/internal/connection/ReusePlan;\nLokhttp3/internal/connection/RouteDatabase;\nLokhttp3/internal/connection/RoutePlanner$ConnectResult;\nLokhttp3/internal/connection/RoutePlanner$DefaultImpls;\nLokhttp3/internal/connection/RoutePlanner$Plan;\nLokhttp3/internal/connection/RoutePlanner;\nLokhttp3/internal/connection/RouteSelector$Selection;\nLokhttp3/internal/connection/RouteSelector;\nLokhttp3/internal/connection/SequentialExchangeFinder;\nLokhttp3/internal/http/BridgeInterceptor;\nLokhttp3/internal/http/CallServerInterceptor;\nLokhttp3/internal/http/DatesKt;\nLokhttp3/internal/http/ExchangeCodec$Carrier;\nLokhttp3/internal/http/ExchangeCodec;\nLokhttp3/internal/http/HttpHeaders;\nLokhttp3/internal/http/HttpMethod;\nLokhttp3/internal/http/RealInterceptorChain;\nLokhttp3/internal/http/RealResponseBody;\nLokhttp3/internal/http/RetryAndFollowUpInterceptor;\nLokhttp3/internal/http/StatusLine;\nLokhttp3/internal/http1/Http1ExchangeCodec$AbstractSource;\nLokhttp3/internal/http1/Http1ExchangeCodec$FixedLengthSource;\nLokhttp3/internal/http1/Http1ExchangeCodec;\nLokhttp3/internal/http2/ConnectionShutdownException;\nLokhttp3/internal/http2/ErrorCode;\nLokhttp3/internal/http2/Header;\nLokhttp3/internal/http2/Hpack$Reader;\nLokhttp3/internal/http2/Hpack$Writer;\nLokhttp3/internal/http2/Hpack;\nLokhttp3/internal/http2/Http2;\nLokhttp3/internal/http2/Http2Connection$1;\nLokhttp3/internal/http2/Http2Connection$Builder;\nLokhttp3/internal/http2/Http2Connection$Listener$Companion$REFUSE_INCOMING_STREAMS$1;\nLokhttp3/internal/http2/Http2Connection$Listener;\nLokhttp3/internal/http2/Http2Connection$ReaderRunnable$applyAndAckSettings$1$1$2;\nLokhttp3/internal/http2/Http2Connection$ReaderRunnable$headers$1$1;\nLokhttp3/internal/http2/Http2Connection$ReaderRunnable$ping$2;\nLokhttp3/internal/http2/Http2Connection$ReaderRunnable$settings$1;\nLokhttp3/internal/http2/Http2Connection$ReaderRunnable;\nLokhttp3/internal/http2/Http2Connection$pushDataLater$1;\nLokhttp3/internal/http2/Http2Connection$pushHeadersLater$1;\nLokhttp3/internal/http2/Http2Connection$pushRequestLater$2;\nLokhttp3/internal/http2/Http2Connection$pushResetLater$1;\nLokhttp3/internal/http2/Http2Connection$sendDegradedPingLater$2;\nLokhttp3/internal/http2/Http2Connection$writeSynResetLater$1;\nLokhttp3/internal/http2/Http2Connection$writeWindowUpdateLater$1;\nLokhttp3/internal/http2/Http2Connection;\nLokhttp3/internal/http2/Http2ExchangeCodec;\nLokhttp3/internal/http2/Http2Reader$ContinuationSource;\nLokhttp3/internal/http2/Http2Reader$Handler;\nLokhttp3/internal/http2/Http2Reader;\nLokhttp3/internal/http2/Http2Stream$FramingSink;\nLokhttp3/internal/http2/Http2Stream$FramingSource;\nLokhttp3/internal/http2/Http2Stream$StreamTimeout;\nLokhttp3/internal/http2/Http2Stream;\nLokhttp3/internal/http2/Http2Writer;\nLokhttp3/internal/http2/Huffman$Node;\nLokhttp3/internal/http2/Huffman;\nLokhttp3/internal/http2/PushObserver$Companion$PushObserverCancel;\nLokhttp3/internal/http2/PushObserver;\nLokhttp3/internal/http2/Settings;\nLokhttp3/internal/http2/StreamResetException;\nLokhttp3/internal/platform/Android10Platform;\nLokhttp3/internal/platform/AndroidPlatform$Companion;\nLokhttp3/internal/platform/AndroidPlatform;\nLokhttp3/internal/platform/BouncyCastlePlatform$Companion;\nLokhttp3/internal/platform/BouncyCastlePlatform;\nLokhttp3/internal/platform/ConscryptPlatform$Companion;\nLokhttp3/internal/platform/ConscryptPlatform;\nLokhttp3/internal/platform/Jdk8WithJettyBootPlatform;\nLokhttp3/internal/platform/Jdk9Platform$Companion;\nLokhttp3/internal/platform/Jdk9Platform;\nLokhttp3/internal/platform/OpenJSSEPlatform$Companion;\nLokhttp3/internal/platform/OpenJSSEPlatform;\nLokhttp3/internal/platform/Platform$Companion;\nLokhttp3/internal/platform/Platform;\nLokhttp3/internal/platform/android/Android10SocketAdapter$Companion;\nLokhttp3/internal/platform/android/Android10SocketAdapter;\nLokhttp3/internal/platform/android/AndroidCertificateChainCleaner;\nLokhttp3/internal/platform/android/AndroidLog;\nLokhttp3/internal/platform/android/AndroidLogHandler;\nLokhttp3/internal/platform/android/AndroidSocketAdapter$Companion$factory$1;\nLokhttp3/internal/platform/android/AndroidSocketAdapter$Companion;\nLokhttp3/internal/platform/android/AndroidSocketAdapter;\nLokhttp3/internal/platform/android/BouncyCastleSocketAdapter$Companion$factory$1;\nLokhttp3/internal/platform/android/BouncyCastleSocketAdapter;\nLokhttp3/internal/platform/android/ConscryptSocketAdapter$Companion$factory$1;\nLokhttp3/internal/platform/android/ConscryptSocketAdapter;\nLokhttp3/internal/platform/android/DeferredSocketAdapter$Factory;\nLokhttp3/internal/platform/android/DeferredSocketAdapter;\nLokhttp3/internal/platform/android/SocketAdapter;\nLokhttp3/internal/proxy/NullProxySelector;\nLokhttp3/internal/publicsuffix/PublicSuffixDatabase$Companion;\nLokhttp3/internal/publicsuffix/PublicSuffixDatabase;\nLokhttp3/internal/tls/BasicCertificateChainCleaner;\nLokhttp3/internal/tls/BasicTrustRootIndex;\nLokhttp3/internal/tls/CertificateChainCleaner;\nLokhttp3/internal/tls/OkHostnameVerifier;\nLokhttp3/internal/tls/TrustRootIndex;\nLokhttp3/logging/HttpLoggingInterceptor$Logger$Companion$DefaultLogger;\nLokhttp3/logging/HttpLoggingInterceptor$Logger;\nLokhttp3/logging/HttpLoggingInterceptor;\nLokhttp3/logging/LoggingEventListener$Factory;\nLokhttp3/logging/LoggingEventListener;\nLokio/AsyncTimeout$Companion;\nLokio/AsyncTimeout$Watchdog;\nLokio/AsyncTimeout$sink$1;\nLokio/AsyncTimeout$source$1;\nLokio/AsyncTimeout;\nLokio/BlackholeSink;\nLokio/Buffer;\nLokio/BufferedSink;\nLokio/BufferedSource;\nLokio/ByteString$Companion;\nLokio/ByteString;\nLokio/FileHandle;\nLokio/FileMetadata;\nLokio/FileSystem;\nLokio/ForwardingFileSystem;\nLokio/ForwardingSink;\nLokio/ForwardingSource;\nLokio/GzipSource;\nLokio/InflaterSource;\nLokio/InputStreamSource;\nLokio/JvmFileHandle;\nLokio/JvmSystemFileSystem;\nLokio/NioSystemFileSystem;\nLokio/Okio;\nLokio/Okio__JvmOkioKt;\nLokio/Options$Companion;\nLokio/Options;\nLokio/OutputStreamSink;\nLokio/Path$Companion;\nLokio/Path;\nLokio/RealBufferedSink;\nLokio/RealBufferedSource;\nLokio/Segment;\nLokio/SegmentPool;\nLokio/SegmentedByteString;\nLokio/Sink;\nLokio/SocketAsyncTimeout;\nLokio/Source;\nLokio/Timeout$Companion$NONE$1;\nLokio/Timeout;\nLokio/ZipFileSystem;\nLokio/_Base64Kt;\nLokio/_UtilKt;\nLokio/internal/EocdRecord;\nLokio/internal/ResourceFileSystem$Companion;\nLokio/internal/ResourceFileSystem$roots$2;\nLokio/internal/ResourceFileSystem;\nLokio/internal/ZipEntry;\nLokio/internal/ZipKt;\nLokio/internal/_BufferKt;\nLokio/internal/_ByteStringKt;\nLokio/internal/_PathKt;\nPLandroidx/arch/core/internal/SafeIterableMap$DescendingIterator;-><init>(Landroidx/arch/core/internal/SafeIterableMap$Entry;Landroidx/arch/core/internal/SafeIterableMap$Entry;)V\nPLandroidx/arch/core/internal/SafeIterableMap$DescendingIterator;->forward(Landroidx/arch/core/internal/SafeIterableMap$Entry;)Landroidx/arch/core/internal/SafeIterableMap$Entry;\nPLandroidx/arch/core/internal/SafeIterableMap$ListIterator;-><init>(Landroidx/arch/core/internal/SafeIterableMap$Entry;Landroidx/arch/core/internal/SafeIterableMap$Entry;)V\nPLandroidx/arch/core/internal/SafeIterableMap$ListIterator;->hasNext()Z\nPLandroidx/arch/core/internal/SafeIterableMap$ListIterator;->next()Ljava/lang/Object;\nPLandroidx/arch/core/internal/SafeIterableMap$ListIterator;->supportRemove(Landroidx/arch/core/internal/SafeIterableMap$Entry;)V\nPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityDestroyed(Landroid/app/Activity;)V\nPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPaused(Landroid/app/Activity;)V\nPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPreDestroyed(Landroid/app/Activity;)V\nPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPrePaused(Landroid/app/Activity;)V\nPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPreStopped(Landroid/app/Activity;)V\nPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityStopped(Landroid/app/Activity;)V\nPLandroidx/lifecycle/ReportFragment;->onDestroy()V\nPLandroidx/lifecycle/ReportFragment;->onPause()V\nPLandroidx/lifecycle/ReportFragment;->onStop()V\nPLandroidx/profileinstaller/DeviceProfileWriter;-><init>(Landroid/content/res/AssetManager;Ljava/util/concurrent/Executor;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V\nPLandroidx/profileinstaller/DeviceProfileWriter;->assertDeviceAllowsProfileInstallerAotWritesCalled()V\nPLandroidx/profileinstaller/ProfileInstallReceiver$$ExternalSyntheticLambda0;-><clinit>()V\nPLandroidx/profileinstaller/ProfileInstallReceiver$$ExternalSyntheticLambda0;-><init>()V\nPLandroidx/profileinstaller/ProfileInstaller$1;-><init>()V\nPLandroidx/profileinstaller/ProfileInstaller$1;->onResultReceived(ILjava/lang/Object;)V\nPLandroidx/profileinstaller/ProfileInstaller;-><clinit>()V\nPLandroidx/profileinstaller/ProfileInstaller;->writeProfile(Landroid/content/Context;Ljava/util/concurrent/Executor;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;Z)V\nPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;-><init>(Landroid/content/Context;)V\nPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;->run()V\nPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;->run()V\nPLandroidx/profileinstaller/ProfileVersion;-><clinit>()V\nPLkotlinx/coroutines/JobCancellationException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;Lkotlinx/coroutines/Job;)V\nPLkotlinx/coroutines/JobCancellationException;->equals(Ljava/lang/Object;)Z\nPLkotlinx/coroutines/JobCancellationException;->fillInStackTrace()Ljava/lang/Throwable;\nPLkotlinx/coroutines/JobImpl;->getOnCancelComplete$kotlinx_coroutines_core()Z\nPLkotlinx/coroutines/JobKt;->cancel$default(Lkotlin/coroutines/CoroutineContext;Ljava/util/concurrent/CancellationException;ILjava/lang/Object;)V\nPLkotlinx/coroutines/JobSupport$Finishing;-><init>(Lkotlinx/coroutines/NodeList;ZLjava/lang/Throwable;)V\nPLkotlinx/coroutines/JobSupport$Finishing;->addExceptionLocked(Ljava/lang/Throwable;)V\nPLkotlinx/coroutines/JobSupport$Finishing;->allocateList()Ljava/util/ArrayList;\nPLkotlinx/coroutines/JobSupport$Finishing;->getList()Lkotlinx/coroutines/NodeList;\nPLkotlinx/coroutines/JobSupport$Finishing;->getRootCause()Ljava/lang/Throwable;\nPLkotlinx/coroutines/JobSupport$Finishing;->isCancelling()Z\nPLkotlinx/coroutines/JobSupport$Finishing;->isCompleting()Z\nPLkotlinx/coroutines/JobSupport$Finishing;->sealLocked(Ljava/lang/Throwable;)Ljava/util/List;\nPLkotlinx/coroutines/JobSupport$Finishing;->setCompleting(Z)V\nPLkotlinx/coroutines/JobSupport;->cancel(Ljava/util/concurrent/CancellationException;)V\nPLkotlinx/coroutines/JobSupport;->cancelImpl$kotlinx_coroutines_core(Ljava/lang/Object;)Z\nPLkotlinx/coroutines/JobSupport;->cancelParent(Ljava/lang/Throwable;)Z\nPLkotlinx/coroutines/JobSupport;->cancellationExceptionMessage()Ljava/lang/String;\nPLkotlinx/coroutines/JobSupport;->createCauseException(Ljava/lang/Object;)Ljava/lang/Throwable;\nPLkotlinx/coroutines/JobSupport;->finalizeFinishingState(Lkotlinx/coroutines/JobSupport$Finishing;Ljava/lang/Object;)Ljava/lang/Object;\nPLkotlinx/coroutines/JobSupport;->getOrPromoteCancellingList(Lkotlinx/coroutines/Incomplete;)Lkotlinx/coroutines/NodeList;\nPLkotlinx/coroutines/JobSupport;->isScopedCoroutine()Z\nPLkotlinx/coroutines/JobSupport;->nextChild(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)Lkotlinx/coroutines/ChildHandleNode;\nPLkotlinx/coroutines/JobSupport;->notifyCancelling(Lkotlinx/coroutines/NodeList;Ljava/lang/Throwable;)V\nPLkotlinx/coroutines/JobSupport;->onCompletionInternal(Ljava/lang/Object;)V\nPLkotlinx/coroutines/NonDisposableHandle;->dispose()V\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/OkHttp.android.kt",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport android.content.Context\nimport okhttp3.internal.CONST_VERSION\nimport okhttp3.internal.platform.PlatformRegistry\n\nactual object OkHttp {\n  @JvmField\n  actual val VERSION: String = CONST_VERSION\n\n  /**\n   * Configure the ApplicationContext. Not needed unless the AndroidX Startup [Initializer] is disabled, or running\n   * a robolectric test.\n   *\n   * The functionality that will fail without a valid Context is primarily Cookies and URL Domain handling, but\n   * may expand in the future.\n   */\n  fun initialize(applicationContext: Context) {\n    if (PlatformRegistry.applicationContext == null) {\n      // Make sure we aren't using an Activity or Service Context\n      PlatformRegistry.applicationContext = applicationContext.applicationContext\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/Android10Platform.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.os.Build\nimport android.os.StrictMode\nimport android.security.NetworkSecurityPolicy\nimport android.util.CloseGuard\nimport android.util.Log\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Protocol\nimport okhttp3.internal.SuppressSignatureCheck\nimport okhttp3.internal.platform.AndroidPlatform.Companion.Tag\nimport okhttp3.internal.platform.android.Android10SocketAdapter\nimport okhttp3.internal.platform.android.AndroidCertificateChainCleaner\nimport okhttp3.internal.platform.android.AndroidSocketAdapter\nimport okhttp3.internal.platform.android.BouncyCastleSocketAdapter\nimport okhttp3.internal.platform.android.ConscryptSocketAdapter\nimport okhttp3.internal.platform.android.DeferredSocketAdapter\nimport okhttp3.internal.tls.CertificateChainCleaner\nimport okhttp3.internal.tls.TrustRootIndex\n\n/** Android 10+ (API 29+). */\n@SuppressSignatureCheck\nclass Android10Platform :\n  Platform(),\n  ContextAwarePlatform {\n  override var applicationContext: Context? = null\n\n  private val socketAdapters =\n    listOfNotNull(\n      Android10SocketAdapter.buildIfSupported(),\n      DeferredSocketAdapter(AndroidSocketAdapter.playProviderFactory),\n      // Delay and Defer any initialisation of Conscrypt and BouncyCastle\n      DeferredSocketAdapter(ConscryptSocketAdapter.factory),\n      DeferredSocketAdapter(BouncyCastleSocketAdapter.factory),\n    ).filter { it.isSupported() }\n\n  override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? =\n    socketAdapters\n      .find { it.matchesSocketFactory(sslSocketFactory) }\n      ?.trustManager(sslSocketFactory)\n\n  override fun newSSLContext(): SSLContext {\n    StrictMode.noteSlowCall(\"newSSLContext\")\n\n    return super.newSSLContext()\n  }\n\n  override fun buildTrustRootIndex(trustManager: X509TrustManager): TrustRootIndex {\n    StrictMode.noteSlowCall(\"buildTrustRootIndex\")\n\n    return super.buildTrustRootIndex(trustManager)\n  }\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<Protocol>,\n  ) {\n    // No TLS extensions if the socket class is custom.\n    socketAdapters\n      .find { it.matchesSocket(sslSocket) }\n      ?.configureTlsExtensions(sslSocket, hostname, protocols)\n  }\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? =\n    // No TLS extensions if the socket class is custom.\n    socketAdapters.find { it.matchesSocket(sslSocket) }?.getSelectedProtocol(sslSocket)\n\n  override fun getStackTraceForCloseable(closer: String): Any? =\n    if (Build.VERSION.SDK_INT >= 30) {\n      CloseGuard().apply { open(closer) }\n    } else {\n      super.getStackTraceForCloseable(closer)\n    }\n\n  override fun logCloseableLeak(\n    message: String,\n    stackTrace: Any?,\n  ) {\n    if (Build.VERSION.SDK_INT >= 30) {\n      (stackTrace as CloseGuard).warnIfOpen()\n    } else {\n      // Unable to report via CloseGuard. As a last-ditch effort, send it to the logger.\n      super.logCloseableLeak(message, stackTrace)\n    }\n  }\n\n  @SuppressLint(\"NewApi\")\n  override fun isCleartextTrafficPermitted(hostname: String): Boolean =\n    NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(hostname)\n\n  override fun buildCertificateChainCleaner(trustManager: X509TrustManager): CertificateChainCleaner =\n    AndroidCertificateChainCleaner.buildIfSupported(trustManager) ?: super.buildCertificateChainCleaner(trustManager)\n\n  override fun log(\n    message: String,\n    level: Int,\n    t: Throwable?,\n  ) {\n    if (level == WARN) {\n      Log.w(Tag, message, t)\n    } else {\n      Log.i(Tag, message, t)\n    }\n  }\n\n  companion object {\n    val isSupported: Boolean = isAndroid && Build.VERSION.SDK_INT >= 29\n\n    fun buildIfSupported(): Platform? = if (isSupported) Android10Platform() else null\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/AndroidPlatform.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport android.content.Context\nimport android.os.Build\nimport android.os.StrictMode\nimport android.security.NetworkSecurityPolicy\nimport android.util.Log\nimport java.io.IOException\nimport java.lang.reflect.InvocationTargetException\nimport java.lang.reflect.Method\nimport java.net.InetSocketAddress\nimport java.net.Socket\nimport java.security.cert.TrustAnchor\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Protocol\nimport okhttp3.internal.SuppressSignatureCheck\nimport okhttp3.internal.platform.android.AndroidCertificateChainCleaner\nimport okhttp3.internal.platform.android.AndroidSocketAdapter\nimport okhttp3.internal.platform.android.BouncyCastleSocketAdapter\nimport okhttp3.internal.platform.android.ConscryptSocketAdapter\nimport okhttp3.internal.platform.android.DeferredSocketAdapter\nimport okhttp3.internal.platform.android.StandardAndroidSocketAdapter\nimport okhttp3.internal.tls.BasicTrustRootIndex\nimport okhttp3.internal.tls.CertificateChainCleaner\nimport okhttp3.internal.tls.TrustRootIndex\n\n/** Android 5 to 9 (API 21 to 28). */\n@SuppressSignatureCheck\nclass AndroidPlatform :\n  Platform(),\n  ContextAwarePlatform {\n  override var applicationContext: Context? = null\n\n  private val socketAdapters =\n    listOfNotNull(\n      StandardAndroidSocketAdapter.buildIfSupported(),\n      DeferredSocketAdapter(AndroidSocketAdapter.playProviderFactory),\n      // Delay and Defer any initialisation of Conscrypt and BouncyCastle\n      DeferredSocketAdapter(ConscryptSocketAdapter.factory),\n      DeferredSocketAdapter(BouncyCastleSocketAdapter.factory),\n    ).filter { it.isSupported() }\n\n  @Throws(IOException::class)\n  override fun connectSocket(\n    socket: Socket,\n    address: InetSocketAddress,\n    connectTimeout: Int,\n  ) {\n    try {\n      socket.connect(address, connectTimeout)\n    } catch (e: ClassCastException) {\n      // On android 8.0, socket.connect throws a ClassCastException due to a bug\n      // see https://issuetracker.google.com/issues/63649622\n      if (Build.VERSION.SDK_INT == 26) {\n        throw IOException(\"Exception in connect\", e)\n      } else {\n        throw e\n      }\n    }\n  }\n\n  override fun newSSLContext(): SSLContext {\n    StrictMode.noteSlowCall(\"newSSLContext\")\n\n    return super.newSSLContext()\n  }\n\n  override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? =\n    socketAdapters\n      .find { it.matchesSocketFactory(sslSocketFactory) }\n      ?.trustManager(sslSocketFactory)\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<@JvmSuppressWildcards Protocol>,\n  ) {\n    // No TLS extensions if the socket class is custom.\n    socketAdapters\n      .find { it.matchesSocket(sslSocket) }\n      ?.configureTlsExtensions(sslSocket, hostname, protocols)\n  }\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? =\n    // No TLS extensions if the socket class is custom.\n    socketAdapters.find { it.matchesSocket(sslSocket) }?.getSelectedProtocol(sslSocket)\n\n  override fun isCleartextTrafficPermitted(hostname: String): Boolean =\n    when {\n      Build.VERSION.SDK_INT >= 24 -> NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(hostname)\n      Build.VERSION.SDK_INT >= 23 -> NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted\n      else -> true\n    }\n\n  override fun buildCertificateChainCleaner(trustManager: X509TrustManager): CertificateChainCleaner =\n    AndroidCertificateChainCleaner.buildIfSupported(trustManager) ?: super.buildCertificateChainCleaner(trustManager)\n\n  override fun buildTrustRootIndex(trustManager: X509TrustManager): TrustRootIndex =\n    try {\n      StrictMode.noteSlowCall(\"buildTrustRootIndex\")\n\n      // From org.conscrypt.TrustManagerImpl, we want the method with this signature:\n      // private TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate lastCert);\n      val method =\n        trustManager.javaClass.getDeclaredMethod(\n          \"findTrustAnchorByIssuerAndSignature\",\n          X509Certificate::class.java,\n        )\n      method.isAccessible = true\n      CustomTrustRootIndex(trustManager, method)\n    } catch (e: NoSuchMethodException) {\n      super.buildTrustRootIndex(trustManager)\n    }\n\n  override fun getHandshakeServerNames(sslSocket: SSLSocket): List<String> {\n    // The superclass implementation requires APIs not available until API 25+.\n    if (Build.VERSION.SDK_INT <= 24) return listOf()\n    return super.getHandshakeServerNames(sslSocket)\n  }\n\n  override fun log(\n    message: String,\n    level: Int,\n    t: Throwable?,\n  ) {\n    if (level == WARN) {\n      Log.w(Tag, message, t)\n    } else {\n      Log.i(Tag, message, t)\n    }\n  }\n\n  /**\n   * A trust manager for Android applications that customize the trust manager.\n   *\n   * This class exploits knowledge of Android implementation details. This class is potentially\n   * much faster to initialize than [BasicTrustRootIndex] because it doesn't need to load and\n   * index trusted CA certificates.\n   */\n  internal data class CustomTrustRootIndex(\n    private val trustManager: X509TrustManager,\n    private val findByIssuerAndSignatureMethod: Method,\n  ) : TrustRootIndex {\n    override fun findByIssuerAndSignature(cert: X509Certificate): X509Certificate? =\n      try {\n        val trustAnchor =\n          findByIssuerAndSignatureMethod.invoke(\n            trustManager,\n            cert,\n          ) as TrustAnchor\n        trustAnchor.trustedCert\n      } catch (e: IllegalAccessException) {\n        throw AssertionError(\"unable to get issues and signature\", e)\n      } catch (_: InvocationTargetException) {\n        null\n      }\n  }\n\n  companion object {\n    val Tag = \"OkHttp\"\n\n    val isSupported: Boolean = isAndroid && Build.VERSION.SDK_INT in 21 until 29\n\n    fun buildIfSupported(): Platform? = if (isSupported) AndroidPlatform() else null\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/ContextAwarePlatform.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport android.content.Context\n\ninterface ContextAwarePlatform {\n  var applicationContext: Context?\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/PlatformInitializer.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport android.content.Context\nimport androidx.startup.Initializer\n\n/**\n * Androidx Startup initializer to ensure that the AndroidPlatform has access to the application context.\n */\nclass PlatformInitializer : Initializer<Platform> {\n  override fun create(context: Context): Platform {\n    PlatformRegistry.applicationContext = context\n\n    return Platform.get()\n  }\n\n  override fun dependencies(): List<Class<Initializer<*>>> = listOf()\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/PlatformRegistry.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport android.content.Context\nimport android.os.Build\nimport java.lang.IllegalStateException\nimport okhttp3.internal.platform.android.AndroidLog\n\nactual object PlatformRegistry {\n  actual fun findPlatform(): Platform {\n    AndroidLog.enable()\n\n    val androidPlatform =\n      Android10Platform.buildIfSupported()\n        ?: AndroidPlatform.buildIfSupported()\n    if (androidPlatform != null) return androidPlatform\n\n    // If the API version is 0, assume this is the Android artifact, but running on the JVM without Robolectric.\n    if (Build.VERSION.SDK_INT == 0) {\n      return Jdk9Platform.buildIfSupported()\n        ?: Platform()\n    }\n\n    throw IllegalStateException(\"Expected Android API level 21+ but was ${Build.VERSION.SDK_INT}\")\n  }\n\n  actual val isAndroid: Boolean\n    get() = true\n\n  var applicationContext: Context?\n    get() = (Platform.get() as? ContextAwarePlatform)?.applicationContext\n    set(value) {\n      (Platform.get() as? ContextAwarePlatform)?.applicationContext = value\n    }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/Android10SocketAdapter.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport android.annotation.SuppressLint\nimport android.net.ssl.SSLSockets\nimport android.os.Build\nimport java.io.IOException\nimport java.lang.IllegalArgumentException\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Protocol\nimport okhttp3.internal.SuppressSignatureCheck\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.Platform.Companion.isAndroid\n\n/**\n * Simple non-reflection SocketAdapter for Android Q+.\n *\n * These API assumptions make it unsuitable for use on earlier Android versions.\n */\n@SuppressLint(\"NewApi\")\n@SuppressSignatureCheck\nclass Android10SocketAdapter : SocketAdapter {\n  override fun matchesSocket(sslSocket: SSLSocket): Boolean = SSLSockets.isSupportedSocket(sslSocket)\n\n  override fun isSupported(): Boolean = Companion.isSupported()\n\n  @SuppressLint(\"NewApi\")\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? =\n    try {\n      // SSLSocket.getApplicationProtocol returns \"\" if application protocols values will not\n      // be used. Observed if you didn't specify SSLParameters.setApplicationProtocols\n      when (val protocol = sslSocket.applicationProtocol) {\n        null, \"\" -> null\n        else -> protocol\n      }\n    } catch (e: UnsupportedOperationException) {\n      // https://docs.oracle.com/javase/9/docs/api/javax/net/ssl/SSLSocket.html#getApplicationProtocol--\n      null\n    }\n\n  @SuppressLint(\"NewApi\")\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<Protocol>,\n  ) {\n    try {\n      SSLSockets.setUseSessionTickets(sslSocket, true)\n\n      val sslParameters = sslSocket.sslParameters\n\n      // Enable ALPN.\n      sslParameters.applicationProtocols = Platform.alpnProtocolNames(protocols).toTypedArray()\n\n      sslSocket.sslParameters = sslParameters\n    } catch (iae: IllegalArgumentException) {\n      // probably java.lang.IllegalArgumentException: Invalid input to toASCII from IDN.toASCII\n      throw IOException(\"Android internal error\", iae)\n    }\n  }\n\n  @SuppressSignatureCheck\n  companion object {\n    fun buildIfSupported(): SocketAdapter? = if (isSupported()) Android10SocketAdapter() else null\n\n    @androidx.annotation.ChecksSdkIntAtLeast(api = 29)\n    fun isSupported() = isAndroid && Build.VERSION.SDK_INT >= 29\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/AndroidCertificateChainCleaner.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport android.net.http.X509TrustManagerExtensions\nimport java.lang.IllegalArgumentException\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateException\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.internal.SuppressSignatureCheck\nimport okhttp3.internal.tls.CertificateChainCleaner\n\n/**\n * Android implementation of CertificateChainCleaner using direct Android API calls.\n * Not used if X509TrustManager doesn't implement [X509TrustManager.checkServerTrusted] with\n * an additional host param.\n */\ninternal class AndroidCertificateChainCleaner(\n  private val trustManager: X509TrustManager,\n  private val x509TrustManagerExtensions: X509TrustManagerExtensions,\n) : CertificateChainCleaner() {\n  @Suppress(\"UNCHECKED_CAST\")\n  @Throws(SSLPeerUnverifiedException::class)\n  @SuppressSignatureCheck\n  override fun clean(\n    chain: List<Certificate>,\n    hostname: String,\n  ): List<Certificate> {\n    val certificates = (chain as List<X509Certificate>).toTypedArray()\n    try {\n      return x509TrustManagerExtensions.checkServerTrusted(certificates, \"RSA\", hostname)\n    } catch (ce: CertificateException) {\n      throw SSLPeerUnverifiedException(ce.message).apply { initCause(ce) }\n    }\n  }\n\n  override fun equals(other: Any?): Boolean =\n    other is AndroidCertificateChainCleaner &&\n      other.trustManager === this.trustManager\n\n  override fun hashCode(): Int = System.identityHashCode(trustManager)\n\n  companion object {\n    @SuppressSignatureCheck\n    fun buildIfSupported(trustManager: X509TrustManager): AndroidCertificateChainCleaner? {\n      val extensions =\n        try {\n          X509TrustManagerExtensions(trustManager)\n        } catch (iae: IllegalArgumentException) {\n          // X509TrustManagerExtensions checks for checkServerTrusted(X509Certificate[], String, String)\n          null\n        }\n\n      return when {\n        extensions != null -> AndroidCertificateChainCleaner(trustManager, extensions)\n        else -> null\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/AndroidLog.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport android.util.Log\nimport java.util.concurrent.CopyOnWriteArraySet\nimport java.util.logging.Handler\nimport java.util.logging.Level\nimport java.util.logging.LogRecord\nimport java.util.logging.Logger\nimport okhttp3.OkHttpClient\nimport okhttp3.internal.SuppressSignatureCheck\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.http2.Http2\nimport okhttp3.internal.platform.android.AndroidLog.androidLog\n\nprivate val LogRecord.androidLevel: Int\n  get() =\n    when {\n      level.intValue() > Level.INFO.intValue() -> Log.WARN\n      level.intValue() == Level.INFO.intValue() -> Log.INFO\n      else -> Log.DEBUG\n    }\n\nobject AndroidLogHandler : Handler() {\n  override fun publish(record: LogRecord) {\n    androidLog(record.loggerName, record.androidLevel, record.message, record.thrown)\n  }\n\n  override fun flush() {\n  }\n\n  override fun close() {\n  }\n}\n\n@SuppressSignatureCheck\nobject AndroidLog {\n  private const val MAX_LOG_LENGTH = 4000\n\n  // Keep references to loggers to prevent their configuration from being GC'd.\n  private val configuredLoggers = CopyOnWriteArraySet<Logger>()\n\n  private val knownLoggers =\n    LinkedHashMap<String, String>()\n      .apply {\n        val packageName = OkHttpClient::class.java.`package`?.name\n\n        if (packageName != null) {\n          this[packageName] = \"OkHttp\"\n        }\n\n        this[OkHttpClient::class.java.name] = \"okhttp.OkHttpClient\"\n        this[Http2::class.java.name] = \"okhttp.Http2\"\n        this[TaskRunner::class.java.name] = \"okhttp.TaskRunner\"\n        this[\"okhttp3.mockwebserver.MockWebServer\"] = \"okhttp.MockWebServer\"\n      }.toMap()\n\n  internal fun androidLog(\n    loggerName: String,\n    logLevel: Int,\n    message: String,\n    t: Throwable? = null,\n  ) {\n    val tag = loggerTag(loggerName)\n\n    if (Log.isLoggable(tag, logLevel)) {\n      var logMessage = message\n      if (t != null) logMessage = logMessage + '\\n'.toString() + Log.getStackTraceString(t)\n\n      // Split by line, then ensure each line can fit into Log's maximum length.\n      var i = 0\n      val length = logMessage.length\n      while (i < length) {\n        var newline = logMessage.indexOf('\\n', i)\n        newline = if (newline != -1) newline else length\n        do {\n          val end = minOf(newline, i + MAX_LOG_LENGTH)\n          Log.println(logLevel, tag, logMessage.substring(i, end))\n          i = end\n        } while (i < newline)\n        i++\n      }\n    }\n  }\n\n  private fun loggerTag(loggerName: String): String {\n    // We need to handle long logger names before they hit Log.\n    // java.lang.IllegalArgumentException: Log tag \"okhttp3.mockwebserver.MockWebServer\" exceeds limit of 23 characters\n    return knownLoggers[loggerName] ?: loggerName.take(23)\n  }\n\n  fun enable() {\n    try {\n      for ((logger, tag) in knownLoggers) {\n        enableLogging(logger, tag)\n      }\n    } catch (re: RuntimeException) {\n      // Happens with non-robolectric unit tests\n      System.err.println(\"Possibly running android unit test without robolectric\")\n      re.printStackTrace()\n    } catch (ule: UnsatisfiedLinkError) {\n      // Happens with Paparazzi - with Android classes on the classpath\n      System.err.println(\"Possibly running android unit test without robolectric\")\n      ule.printStackTrace()\n    }\n  }\n\n  private fun enableLogging(\n    logger: String,\n    tag: String,\n  ) {\n    val logger = Logger.getLogger(logger)\n    if (configuredLoggers.add(logger)) {\n      logger.useParentHandlers = false\n      // log based on levels at startup to avoid logging each frame\n      logger.level =\n        when {\n          Log.isLoggable(tag, Log.DEBUG) -> Level.FINE\n          Log.isLoggable(tag, Log.INFO) -> Level.INFO\n          else -> Level.WARNING\n        }\n      logger.addHandler(AndroidLogHandler)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/AndroidSocketAdapter.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport android.os.Build\nimport java.lang.reflect.InvocationTargetException\nimport java.lang.reflect.Method\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Protocol\nimport okhttp3.internal.platform.AndroidPlatform\nimport okhttp3.internal.platform.Platform\n\n/**\n * Modern reflection based SocketAdapter for Conscrypt class SSLSockets.\n *\n * This is used directly for providers where class name is known e.g. the Google Play Provider\n * but we can't compile directly against it, or in fact reliably know if it is registered and\n * on classpath.\n */\nopen class AndroidSocketAdapter(\n  private val sslSocketClass: Class<in SSLSocket>,\n) : SocketAdapter {\n  private val setUseSessionTickets: Method =\n    sslSocketClass.getDeclaredMethod(\"setUseSessionTickets\", Boolean::class.javaPrimitiveType)\n  private val setHostname = sslSocketClass.getMethod(\"setHostname\", String::class.java)\n  private val getAlpnSelectedProtocol = sslSocketClass.getMethod(\"getAlpnSelectedProtocol\")\n  private val setAlpnProtocols =\n    sslSocketClass.getMethod(\"setAlpnProtocols\", ByteArray::class.java)\n\n  override fun isSupported(): Boolean = AndroidPlatform.isSupported\n\n  override fun matchesSocket(sslSocket: SSLSocket): Boolean = sslSocketClass.isInstance(sslSocket)\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<Protocol>,\n  ) {\n    // No TLS extensions if the socket class is custom.\n    if (matchesSocket(sslSocket)) {\n      try {\n        // Enable session tickets.\n        setUseSessionTickets.invoke(sslSocket, true)\n\n        // Assume platform support on 24+\n        if (hostname != null && Build.VERSION.SDK_INT <= 23) {\n          // This is SSLParameters.setServerNames() in API 24+.\n          setHostname.invoke(sslSocket, hostname)\n        }\n\n        // Enable ALPN.\n        setAlpnProtocols.invoke(\n          sslSocket,\n          Platform.concatLengthPrefixed(protocols),\n        )\n      } catch (e: IllegalAccessException) {\n        throw AssertionError(e)\n      } catch (e: InvocationTargetException) {\n        throw AssertionError(e)\n      }\n    }\n  }\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? {\n    // No TLS extensions if the socket class is custom.\n    if (!matchesSocket(sslSocket)) {\n      return null\n    }\n\n    return try {\n      val alpnResult = getAlpnSelectedProtocol.invoke(sslSocket) as ByteArray?\n      alpnResult?.toString(Charsets.UTF_8)\n    } catch (e: IllegalAccessException) {\n      throw AssertionError(e)\n    } catch (e: InvocationTargetException) {\n      // https://github.com/square/okhttp/issues/5587\n      val cause = e.cause\n      when {\n        cause is NullPointerException && cause.message == \"ssl == null\" -> null\n        else -> throw AssertionError(e)\n      }\n    }\n  }\n\n  companion object {\n    val playProviderFactory: DeferredSocketAdapter.Factory =\n      factory(\"com.google.android.gms.org.conscrypt\")\n\n    /**\n     * Builds a SocketAdapter from an observed implementation class, by grabbing the Class\n     * reference to perform reflection on at runtime.\n     *\n     * @param actualSSLSocketClass the runtime class of Conscrypt class socket.\n     */\n    private fun build(actualSSLSocketClass: Class<in SSLSocket>): AndroidSocketAdapter {\n      var possibleClass: Class<in SSLSocket>? = actualSSLSocketClass\n      while (possibleClass != null && possibleClass.simpleName != \"OpenSSLSocketImpl\") {\n        possibleClass = possibleClass.superclass\n\n        if (possibleClass == null) {\n          throw AssertionError(\n            \"No OpenSSLSocketImpl superclass of socket of type $actualSSLSocketClass\",\n          )\n        }\n      }\n\n      return AndroidSocketAdapter(possibleClass!!)\n    }\n\n    fun factory(packageName: String): DeferredSocketAdapter.Factory =\n      object : DeferredSocketAdapter.Factory {\n        override fun matchesSocket(sslSocket: SSLSocket): Boolean = sslSocket.javaClass.name.startsWith(\"$packageName.\")\n\n        override fun create(sslSocket: SSLSocket): SocketAdapter = build(sslSocket.javaClass)\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/BouncyCastleSocketAdapter.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Protocol\nimport okhttp3.internal.platform.Platform\nimport org.bouncycastle.jsse.BCSSLSocket\n\n/**\n * Simple non-reflection SocketAdapter for BouncyCastle.\n */\nclass BouncyCastleSocketAdapter : SocketAdapter {\n  override fun matchesSocket(sslSocket: SSLSocket): Boolean = sslSocket is BCSSLSocket\n\n  override fun isSupported(): Boolean = isSupported\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? {\n    val s = sslSocket as BCSSLSocket\n\n    return when (val protocol = s.applicationProtocol) {\n      null, \"\" -> null\n      else -> protocol\n    }\n  }\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<Protocol>,\n  ) {\n    // No TLS extensions if the socket class is custom.\n    if (matchesSocket(sslSocket)) {\n      val bcSocket = sslSocket as BCSSLSocket\n\n      val sslParameters = bcSocket.parameters\n\n      // Enable ALPN.\n      sslParameters.applicationProtocols = Platform.alpnProtocolNames(protocols).toTypedArray()\n\n      bcSocket.parameters = sslParameters\n    }\n  }\n\n  companion object {\n    val factory =\n      object : DeferredSocketAdapter.Factory {\n        override fun matchesSocket(sslSocket: SSLSocket): Boolean = isSupported && sslSocket is BCSSLSocket\n\n        override fun create(sslSocket: SSLSocket): SocketAdapter = BouncyCastleSocketAdapter()\n      }\n\n    val isSupported: Boolean =\n      try {\n        // Trigger an early exception over a fatal error, prefer a RuntimeException over Error.\n        Class.forName(\"org.bouncycastle.jsse.provider.BouncyCastleJsseProvider\", false, javaClass.classLoader)\n\n        true\n      } catch (_: ClassNotFoundException) {\n        false\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/ConscryptSocketAdapter.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Protocol\nimport okhttp3.internal.platform.Platform\nimport org.conscrypt.Conscrypt\n\n/**\n * Simple non-reflection SocketAdapter for Conscrypt when included as an application dependency\n * directly.\n */\nclass ConscryptSocketAdapter : SocketAdapter {\n  override fun matchesSocket(sslSocket: SSLSocket): Boolean = Conscrypt.isConscrypt(sslSocket)\n\n  override fun isSupported(): Boolean = isSupported\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? =\n    when {\n      matchesSocket(sslSocket) -> Conscrypt.getApplicationProtocol(sslSocket)\n      else -> null // No TLS extensions if the socket class is custom.\n    }\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<Protocol>,\n  ) {\n    // No TLS extensions if the socket class is custom.\n    if (matchesSocket(sslSocket)) {\n      // Enable session tickets.\n      Conscrypt.setUseSessionTickets(sslSocket, true)\n\n      // Enable ALPN.\n      val names = Platform.alpnProtocolNames(protocols)\n      Conscrypt.setApplicationProtocols(sslSocket, names.toTypedArray())\n    }\n  }\n\n  companion object {\n    val factory =\n      object : DeferredSocketAdapter.Factory {\n        override fun matchesSocket(sslSocket: SSLSocket): Boolean = isSupported && Conscrypt.isConscrypt(sslSocket)\n\n        override fun create(sslSocket: SSLSocket): SocketAdapter = ConscryptSocketAdapter()\n      }\n\n    val isSupported: Boolean =\n      try {\n        // Trigger an early exception over a fatal error, prefer a RuntimeException over Error.\n        Class.forName(\"org.conscrypt.Conscrypt\\$Version\", false, javaClass.classLoader)\n\n        when {\n          // Bump this version if we ever have a binary incompatibility\n          Conscrypt.isAvailable() && atLeastVersion(2, 1, 0) -> true\n\n          else -> false\n        }\n      } catch (e: NoClassDefFoundError) {\n        false\n      } catch (e: ClassNotFoundException) {\n        false\n      }\n\n    fun atLeastVersion(\n      major: Int,\n      minor: Int = 0,\n      patch: Int = 0,\n    ): Boolean {\n      val conscryptVersion = Conscrypt.version() ?: return false\n\n      if (conscryptVersion.major() != major) {\n        return conscryptVersion.major() > major\n      }\n\n      if (conscryptVersion.minor() != minor) {\n        return conscryptVersion.minor() > minor\n      }\n\n      return conscryptVersion.patch() >= patch\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/DeferredSocketAdapter.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Protocol\n\n/**\n * Deferred implementation of SocketAdapter that works by observing the socket\n * and initializing on first use.\n *\n * We use this because eager classpath checks cause confusion and excessive logging in Android,\n * and we can't rely on classnames after proguard, so are probably best served by falling through\n * to a situation of trying our least likely noisiest options.\n */\nclass DeferredSocketAdapter(\n  private val socketAdapterFactory: Factory,\n) : SocketAdapter {\n  private var delegate: SocketAdapter? = null\n\n  override fun isSupported(): Boolean = true\n\n  override fun matchesSocket(sslSocket: SSLSocket): Boolean = socketAdapterFactory.matchesSocket(sslSocket)\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<Protocol>,\n  ) {\n    getDelegate(sslSocket)?.configureTlsExtensions(sslSocket, hostname, protocols)\n  }\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? = getDelegate(sslSocket)?.getSelectedProtocol(sslSocket)\n\n  @Synchronized private fun getDelegate(sslSocket: SSLSocket): SocketAdapter? {\n    if (this.delegate == null && socketAdapterFactory.matchesSocket(sslSocket)) {\n      this.delegate = socketAdapterFactory.create(sslSocket)\n    }\n\n    return delegate\n  }\n\n  interface Factory {\n    fun matchesSocket(sslSocket: SSLSocket): Boolean\n\n    fun create(sslSocket: SSLSocket): SocketAdapter\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/SocketAdapter.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Protocol\n\ninterface SocketAdapter {\n  fun isSupported(): Boolean\n\n  fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? = null\n\n  fun matchesSocket(sslSocket: SSLSocket): Boolean\n\n  fun matchesSocketFactory(sslSocketFactory: SSLSocketFactory): Boolean = false\n\n  fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<Protocol>,\n  )\n\n  fun getSelectedProtocol(sslSocket: SSLSocket): String?\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/platform/android/StandardAndroidSocketAdapter.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform.android\n\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.OkHttpClient\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.readFieldOrNull\n\n/**\n * Base Android reflection based SocketAdapter for the built in Android SSLSocket.\n *\n * It's assumed to always be present with known class names on Android devices, so we build\n * optimistically via [buildIfSupported]. But it also doesn't assume a compile time API.\n */\nclass StandardAndroidSocketAdapter(\n  sslSocketClass: Class<in SSLSocket>,\n  private val sslSocketFactoryClass: Class<in SSLSocketFactory>,\n  private val paramClass: Class<*>,\n) : AndroidSocketAdapter(sslSocketClass) {\n  override fun matchesSocketFactory(sslSocketFactory: SSLSocketFactory): Boolean = sslSocketFactoryClass.isInstance(sslSocketFactory)\n\n  override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? {\n    val context: Any? =\n      readFieldOrNull(\n        sslSocketFactory,\n        paramClass,\n        \"sslParameters\",\n      )\n    val x509TrustManager =\n      readFieldOrNull(\n        context!!,\n        X509TrustManager::class.java,\n        \"x509TrustManager\",\n      )\n    return x509TrustManager\n      ?: readFieldOrNull(\n        context,\n        X509TrustManager::class.java,\n        \"trustManager\",\n      )\n  }\n\n  companion object {\n    @Suppress(\"UNCHECKED_CAST\", \"PrivateApi\")\n    fun buildIfSupported(packageName: String = \"com.android.org.conscrypt\"): SocketAdapter? =\n      try {\n        val sslSocketClass =\n          Class.forName(\"$packageName.OpenSSLSocketImpl\") as Class<in SSLSocket>\n        val sslSocketFactoryClass =\n          Class.forName(\"$packageName.OpenSSLSocketFactoryImpl\") as\n            Class<in SSLSocketFactory>\n        val paramsClass = Class.forName(\"$packageName.SSLParametersImpl\")\n\n        StandardAndroidSocketAdapter(sslSocketClass, sslSocketFactoryClass, paramsClass)\n      } catch (e: Exception) {\n        AndroidLog.androidLog(\n          loggerName = OkHttpClient::class.java.name,\n          logLevel = Platform.WARN,\n          message = \"unable to load android socket classes\",\n          t = e,\n        )\n        null\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/publicsuffix/AssetPublicSuffixList.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport android.os.Build\nimport java.io.IOException\nimport okhttp3.internal.platform.PlatformRegistry\nimport okio.Source\nimport okio.source\n\ninternal class AssetPublicSuffixList(\n  override val path: String = PUBLIC_SUFFIX_RESOURCE,\n) : BasePublicSuffixList() {\n  override fun listSource(): Source {\n    val assets = PlatformRegistry.applicationContext?.assets\n\n    if (assets == null) {\n      if (Build.FINGERPRINT == null) {\n        throw IOException(\n          \"Platform applicationContext not initialized. \" +\n            \"Possibly running Android unit test without Robolectric. \" +\n            \"Android tests should run with Robolectric \" +\n            \"and call OkHttp.initialize before test\",\n        )\n      } else {\n        throw IOException(\n          \"Platform applicationContext not initialized. \" +\n            \"Startup Initializer possibly disabled, \" +\n            \"call OkHttp.initialize before test.\",\n        )\n      }\n    }\n\n    return assets.open(path).source()\n  }\n\n  companion object {\n    val PUBLIC_SUFFIX_RESOURCE = \"PublicSuffixDatabase.list\"\n  }\n}\n"
  },
  {
    "path": "okhttp/src/androidMain/kotlin/okhttp3/internal/publicsuffix/PublicSuffixList.android.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\ninternal actual val PublicSuffixList.Companion.Default: PublicSuffixList\n  get() = AssetPublicSuffixList()\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Address.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.util.Objects\nimport javax.net.SocketFactory\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.internal.toImmutableList\n\n/**\n * A specification for a connection to an origin server. For simple connections, this is the\n * server's hostname and port. If an explicit proxy is requested (or [no proxy][Proxy.NO_PROXY] is\n * explicitly requested), this also includes that proxy information. For secure connections the\n * address also includes the SSL socket factory, hostname verifier, and certificate pinner.\n *\n * HTTP requests that share the same [Address] may also share the same [Connection].\n */\nclass Address(\n  uriHost: String,\n  uriPort: Int,\n  /** Returns the service that will be used to resolve IP addresses for hostnames. */\n  @get:JvmName(\"dns\") val dns: Dns,\n  /** Returns the socket factory for new connections. */\n  @get:JvmName(\"socketFactory\") val socketFactory: SocketFactory,\n  /** Returns the SSL socket factory, or null if this is not an HTTPS address. */\n  @get:JvmName(\"sslSocketFactory\") val sslSocketFactory: SSLSocketFactory?,\n  /** Returns the hostname verifier, or null if this is not an HTTPS address. */\n  @get:JvmName(\"hostnameVerifier\") val hostnameVerifier: HostnameVerifier?,\n  /** Returns this address's certificate pinner, or null if this is not an HTTPS address. */\n  @get:JvmName(\"certificatePinner\") val certificatePinner: CertificatePinner?,\n  /** Returns the client's proxy authenticator. */\n  @get:JvmName(\"proxyAuthenticator\") val proxyAuthenticator: Authenticator,\n  /**\n   * Returns this address's explicitly-specified HTTP proxy, or null to delegate to the\n   * [proxy selector][proxySelector].\n   */\n  @get:JvmName(\"proxy\") val proxy: Proxy?,\n  protocols: List<Protocol>,\n  connectionSpecs: List<ConnectionSpec>,\n  /**\n   * Returns this address's proxy selector. Only used if the proxy is null. If none of this\n   * selector's proxies are reachable, a direct connection will be attempted.\n   */\n  @get:JvmName(\"proxySelector\") val proxySelector: ProxySelector,\n) {\n  /**\n   * Returns a URL with the hostname and port of the origin server. The path, query, and fragment of\n   * this URL are always empty, since they are not significant for planning a route.\n   */\n  @get:JvmName(\"url\")\n  val url: HttpUrl =\n    HttpUrl\n      .Builder()\n      .scheme(if (sslSocketFactory != null) \"https\" else \"http\")\n      .host(uriHost)\n      .port(uriPort)\n      .build()\n\n  /**\n   * The protocols the client supports. This method always returns a non-null list that\n   * contains minimally [Protocol.HTTP_1_1].\n   */\n  @get:JvmName(\"protocols\")\n  val protocols: List<Protocol> = protocols.toImmutableList()\n\n  @get:JvmName(\"connectionSpecs\")\n  val connectionSpecs: List<ConnectionSpec> =\n    connectionSpecs.toImmutableList()\n\n  @JvmName(\"-deprecated_url\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"url\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun url(): HttpUrl = url\n\n  @JvmName(\"-deprecated_dns\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"dns\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun dns(): Dns = dns\n\n  @JvmName(\"-deprecated_socketFactory\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"socketFactory\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun socketFactory(): SocketFactory = socketFactory\n\n  @JvmName(\"-deprecated_proxyAuthenticator\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"proxyAuthenticator\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun proxyAuthenticator(): Authenticator = proxyAuthenticator\n\n  @JvmName(\"-deprecated_protocols\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"protocols\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun protocols(): List<Protocol> = protocols\n\n  @JvmName(\"-deprecated_connectionSpecs\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"connectionSpecs\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun connectionSpecs(): List<ConnectionSpec> = connectionSpecs\n\n  @JvmName(\"-deprecated_proxySelector\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"proxySelector\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun proxySelector(): ProxySelector = proxySelector\n\n  @JvmName(\"-deprecated_proxy\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"proxy\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun proxy(): Proxy? = proxy\n\n  @JvmName(\"-deprecated_sslSocketFactory\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"sslSocketFactory\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun sslSocketFactory(): SSLSocketFactory? = sslSocketFactory\n\n  @JvmName(\"-deprecated_hostnameVerifier\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"hostnameVerifier\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun hostnameVerifier(): HostnameVerifier? = hostnameVerifier\n\n  @JvmName(\"-deprecated_certificatePinner\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"certificatePinner\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun certificatePinner(): CertificatePinner? = certificatePinner\n\n  override fun equals(other: Any?): Boolean =\n    other is Address &&\n      url == other.url &&\n      equalsNonHost(other)\n\n  override fun hashCode(): Int {\n    var result = 17\n    result = 31 * result + url.hashCode()\n    result = 31 * result + dns.hashCode()\n    result = 31 * result + proxyAuthenticator.hashCode()\n    result = 31 * result + protocols.hashCode()\n    result = 31 * result + connectionSpecs.hashCode()\n    result = 31 * result + proxySelector.hashCode()\n    result = 31 * result + Objects.hashCode(proxy)\n    result = 31 * result + Objects.hashCode(sslSocketFactory)\n    result = 31 * result + Objects.hashCode(hostnameVerifier)\n    result = 31 * result + Objects.hashCode(certificatePinner)\n    return result\n  }\n\n  internal fun equalsNonHost(that: Address): Boolean =\n    this.dns == that.dns &&\n      this.proxyAuthenticator == that.proxyAuthenticator &&\n      this.protocols == that.protocols &&\n      this.connectionSpecs == that.connectionSpecs &&\n      this.proxySelector == that.proxySelector &&\n      this.proxy == that.proxy &&\n      this.sslSocketFactory == that.sslSocketFactory &&\n      this.hostnameVerifier == that.hostnameVerifier &&\n      this.certificatePinner == that.certificatePinner &&\n      this.url.port == that.url.port\n\n  override fun toString(): String =\n    \"Address{\" +\n      \"${url.host}:${url.port}, \" +\n      (if (proxy != null) \"proxy=$proxy\" else \"proxySelector=$proxySelector\") +\n      \"}\"\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Authenticator.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport okhttp3.internal.authenticator.JavaNetAuthenticator\n\n/**\n * Performs either **preemptive** authentication before connecting to a proxy server, or\n * **reactive** authentication after receiving a challenge from either an origin web server or proxy\n * server.\n *\n * ## Preemptive Authentication\n *\n * To make HTTPS calls using an HTTP proxy server OkHttp must first negotiate a connection with\n * the proxy. This proxy connection is called a \"TLS Tunnel\" and is specified by\n * [RFC 2817][1]. The HTTP CONNECT request that creates this tunnel connection is special: it\n * does not participate in any [interceptors][Interceptor] or [event listeners][EventListener]. It\n * doesn't include the motivating request's HTTP headers or even its full URL; only the target\n * server's hostname is sent to the proxy.\n *\n * Prior to sending any CONNECT request OkHttp always calls the proxy authenticator so that it may\n * prepare preemptive authentication. OkHttp will call [authenticate] with a fake `HTTP/1.1 407\n * Proxy Authentication Required` response that has a `Proxy-Authenticate: OkHttp-Preemptive`\n * challenge. The proxy authenticator may return either an authenticated request, or null to\n * connect without authentication.\n *\n * ```java\n * for (Challenge challenge : response.challenges()) {\n *   // If this is preemptive auth, use a preemptive credential.\n *   if (challenge.scheme().equalsIgnoreCase(\"OkHttp-Preemptive\")) {\n *     return response.request().newBuilder()\n *         .header(\"Proxy-Authorization\", \"secret\")\n *         .build();\n *   }\n * }\n * return null; // Didn't find a preemptive auth scheme.\n * ```\n *\n * ## Reactive Authentication\n *\n * Implementations authenticate by returning a follow-up request that includes an authorization\n * header, or they may decline the challenge by returning null. In this case the unauthenticated\n * response will be returned to the caller that triggered it.\n *\n * Implementations should check if the initial request already included an attempt to\n * authenticate. If so it is likely that further attempts will not be useful and the authenticator\n * should give up.\n *\n * When reactive authentication is requested by an origin web server, the response code is 401\n * and the implementation should respond with a new request that sets the \"Authorization\" header.\n *\n * ```java\n * if (response.request().header(\"Authorization\") != null) {\n *   return null; // Give up, we've already failed to authenticate.\n * }\n *\n * String credential = Credentials.basic(...)\n * return response.request().newBuilder()\n *     .header(\"Authorization\", credential)\n *     .build();\n * ```\n *\n * When reactive authentication is requested by a proxy server, the response code is 407 and the\n * implementation should respond with a new request that sets the \"Proxy-Authorization\" header.\n *\n * ```java\n * if (response.request().header(\"Proxy-Authorization\") != null) {\n *   return null; // Give up, we've already failed to authenticate.\n * }\n *\n * String credential = Credentials.basic(...)\n * return response.request().newBuilder()\n *     .header(\"Proxy-Authorization\", credential)\n *     .build();\n * ```\n *\n * The proxy authenticator may implement preemptive authentication, reactive authentication, or\n * both.\n *\n * Applications may configure OkHttp with an authenticator for origin servers, or proxy servers,\n * or both.\n *\n * ## Authentication Retries\n *\n * If your authentication may be flaky and requires retries you should apply some policy\n * to limit the retries by the class of errors and number of attempts.  To get the number of\n * attempts to the current point use this function.\n *\n * ```java\n * private int responseCount(Response response) {\n *   int result = 1;\n *   while ((response = response.priorResponse()) != null) {\n *     result++;\n *   }\n *   return result;\n * }\n * ```\n *\n * [1]: https://tools.ietf.org/html/rfc2817\n */\nfun interface Authenticator {\n  /**\n   * Returns a request that includes a credential to satisfy an authentication challenge in\n   * [response]. Returns null if the challenge cannot be satisfied.\n   *\n   * The route is best effort, it currently may not always be provided even when logically\n   * available. It may also not be provided when an authenticator is re-used manually in an\n   * application interceptor, such as when implementing client-specific retries.\n   */\n  @Throws(IOException::class)\n  fun authenticate(\n    route: Route?,\n    response: Response,\n  ): Request?\n\n  companion object {\n    /** An authenticator that knows no credentials and makes no attempt to authenticate. */\n    @JvmField\n    val NONE: Authenticator = AuthenticatorNone()\n\n    private class AuthenticatorNone : Authenticator {\n      override fun authenticate(\n        route: Route?,\n        response: Response,\n      ): Request? = null\n    }\n\n    /** An authenticator that uses the java.net.Authenticator global authenticator. */\n    @JvmField\n    val JAVA_NET_AUTHENTICATOR: Authenticator = JavaNetAuthenticator()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Cache.kt",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.Closeable\nimport java.io.File\nimport java.io.Flushable\nimport java.io.IOException\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateEncodingException\nimport java.security.cert.CertificateException\nimport java.security.cert.CertificateFactory\nimport java.util.TreeSet\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.internal.cache.CacheRequest\nimport okhttp3.internal.cache.CacheStrategy\nimport okhttp3.internal.cache.DiskLruCache\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.http.HttpMethod\nimport okhttp3.internal.http.StatusLine\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.Platform.Companion.WARN\nimport okhttp3.internal.toLongOrDefault\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString.Companion.decodeBase64\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.ByteString.Companion.toByteString\nimport okio.FileSystem\nimport okio.ForwardingSink\nimport okio.ForwardingSource\nimport okio.Path\nimport okio.Path.Companion.toOkioPath\nimport okio.Sink\nimport okio.Source\nimport okio.buffer\n\n/**\n * Caches HTTP and HTTPS responses to the filesystem so they may be reused, saving time and\n * bandwidth.\n *\n * The Cache instance must have exclusive access to the [directory], since the internal data structures\n * may cause corruption or runtime errors if not. It may however be shared amongst multiple OkHttpClient\n * instances.\n *\n * ## Cache Optimization\n *\n * To measure cache effectiveness, this class tracks three statistics:\n *\n *  * **[Request Count:][requestCount]** the number of HTTP requests issued since this cache was\n *    created.\n *  * **[Network Count:][networkCount]** the number of those requests that required network use.\n *  * **[Hit Count:][hitCount]** the number of those requests whose responses were served by the\n *    cache.\n *\n * Sometimes a request will result in a conditional cache hit. If the cache contains a stale copy of\n * the response, the client will issue a conditional `GET`. The server will then send either\n * the updated response if it has changed, or a short 'not modified' response if the client's copy\n * is still valid. Such responses increment both the network count and hit count.\n *\n * The best way to improve the cache hit rate is by configuring the web server to return cacheable\n * responses. Although this client honors all [HTTP/1.1 (RFC 7234)][rfc_7234] cache headers, it\n * doesn't cache partial responses.\n *\n * ## Force a Network Response\n *\n * In some situations, such as after a user clicks a 'refresh' button, it may be necessary to skip\n * the cache, and fetch data directly from the server. To force a full refresh, add the `no-cache`\n * directive:\n *\n * ```java\n * Request request = new Request.Builder()\n *     .cacheControl(new CacheControl.Builder().noCache().build())\n *     .url(\"http://publicobject.com/helloworld.txt\")\n *     .build();\n * ```\n *\n * If it is only necessary to force a cached response to be validated by the server, use the more\n * efficient `max-age=0` directive instead:\n *\n * ```java\n * Request request = new Request.Builder()\n *     .cacheControl(new CacheControl.Builder()\n *         .maxAge(0, TimeUnit.SECONDS)\n *         .build())\n *     .url(\"http://publicobject.com/helloworld.txt\")\n *     .build();\n * ```\n *\n * ## Force a Cache Response\n *\n * Sometimes you'll want to show resources if they are available immediately, but not otherwise.\n * This can be used so your application can show *something* while waiting for the latest data to be\n * downloaded. To restrict a request to locally-cached resources, add the `only-if-cached`\n * directive:\n *\n * ```java\n * Request request = new Request.Builder()\n *     .cacheControl(new CacheControl.Builder()\n *         .onlyIfCached()\n *         .build())\n *     .url(\"http://publicobject.com/helloworld.txt\")\n *     .build();\n * Response forceCacheResponse = client.newCall(request).execute();\n * if (forceCacheResponse.code() != 504) {\n *   // The resource was cached! Show it.\n * } else {\n *   // The resource was not cached.\n * }\n * ```\n *\n * This technique works even better in situations where a stale response is better than no response.\n * To permit stale cached responses, use the `max-stale` directive with the maximum staleness in\n * seconds:\n *\n * ```java\n * Request request = new Request.Builder()\n *     .cacheControl(new CacheControl.Builder()\n *         .maxStale(365, TimeUnit.DAYS)\n *         .build())\n *     .url(\"http://publicobject.com/helloworld.txt\")\n *     .build();\n * ```\n *\n * The [CacheControl] class can configure request caching directives and parse response caching\n * directives. It even offers convenient constants [CacheControl.FORCE_NETWORK] and\n * [CacheControl.FORCE_CACHE] that address the use cases above.\n *\n * [rfc_7234]: http://tools.ietf.org/html/rfc7234\n */\nclass Cache internal constructor(\n  directory: Path,\n  maxSize: Long,\n  fileSystem: FileSystem,\n  taskRunner: TaskRunner,\n) : Closeable,\n  Flushable {\n  /** Create a cache of at most [maxSize] bytes in [directory]. */\n  constructor(\n    fileSystem: FileSystem,\n    directory: Path,\n    maxSize: Long,\n  ) : this(\n    directory,\n    maxSize,\n    fileSystem,\n    TaskRunner.INSTANCE,\n  )\n\n  /** Create a cache of at most [maxSize] bytes in [directory]. */\n  constructor(directory: File, maxSize: Long) : this(\n    FileSystem.SYSTEM,\n    directory.toOkioPath(),\n    maxSize,\n  )\n\n  internal val cache =\n    DiskLruCache(\n      fileSystem = fileSystem,\n      directory = directory,\n      appVersion = VERSION,\n      valueCount = ENTRY_COUNT,\n      maxSize = maxSize,\n      taskRunner = taskRunner,\n    )\n\n  // read and write statistics, all guarded by 'this'.\n  internal var writeSuccessCount = 0\n  internal var writeAbortCount = 0\n  private var networkCount = 0\n  private var hitCount = 0\n  private var requestCount = 0\n\n  val isClosed: Boolean\n    get() = cache.isClosed()\n\n  internal fun get(request: Request): Response? {\n    val key = key(request.url)\n    val snapshot: DiskLruCache.Snapshot =\n      try {\n        cache[key] ?: return null\n      } catch (_: IOException) {\n        return null // Give up because the cache cannot be read.\n      }\n\n    val entry: Entry =\n      try {\n        Entry(snapshot.getSource(ENTRY_METADATA))\n      } catch (_: IOException) {\n        snapshot.closeQuietly()\n        return null\n      }\n\n    val response = entry.response(snapshot)\n    if (!entry.matches(request, response)) {\n      response.body.closeQuietly()\n      return null\n    }\n\n    return response\n  }\n\n  internal fun put(response: Response): CacheRequest? {\n    val requestMethod = response.request.method\n\n    if (HttpMethod.invalidatesCache(response.request.method)) {\n      try {\n        remove(response.request)\n      } catch (_: IOException) {\n        // The cache cannot be written.\n      }\n      return null\n    }\n\n    if (requestMethod != \"GET\") {\n      // Don't cache non-GET responses. We're technically allowed to cache HEAD, QUERY and some\n      // POST requests, but the complexity of doing so is high and the benefit is low.\n      return null\n    }\n\n    if (response.hasVaryAll()) {\n      return null\n    }\n\n    val entry = Entry(response)\n    var editor: DiskLruCache.Editor? = null\n    try {\n      editor = cache.edit(key(response.request.url)) ?: return null\n      entry.writeTo(editor)\n      return RealCacheRequest(editor)\n    } catch (_: IOException) {\n      abortQuietly(editor)\n      return null\n    }\n  }\n\n  @Throws(IOException::class)\n  internal fun remove(request: Request) {\n    cache.remove(key(request.url))\n  }\n\n  internal fun update(\n    cached: Response,\n    network: Response,\n  ) {\n    val entry = Entry(network)\n    val snapshot = (cached.body as CacheResponseBody).snapshot\n    var editor: DiskLruCache.Editor? = null\n    try {\n      editor = snapshot.edit() ?: return // edit() returns null if snapshot is not current.\n      entry.writeTo(editor)\n      editor.commit()\n    } catch (_: IOException) {\n      abortQuietly(editor)\n    }\n  }\n\n  private fun abortQuietly(editor: DiskLruCache.Editor?) {\n    // Give up because the cache cannot be written.\n    try {\n      editor?.abort()\n    } catch (_: IOException) {\n    }\n  }\n\n  /**\n   * Initialize the cache. This will include reading the journal files from the storage and building\n   * up the necessary in-memory cache information.\n   *\n   * The initialization time may vary depending on the journal file size and the current actual\n   * cache size. The application needs to be aware of calling this function during the\n   * initialization phase and preferably in a background worker thread.\n   *\n   * Note that if the application chooses to not call this method to initialize the cache. By\n   * default, OkHttp will perform lazy initialization upon the first usage of the cache.\n   */\n  @Throws(IOException::class)\n  fun initialize() {\n    cache.initialize()\n  }\n\n  /**\n   * Closes the cache and deletes all of its stored values. This will delete all files in the cache\n   * directory including files that weren't created by the cache.\n   */\n  @Throws(IOException::class)\n  fun delete() {\n    cache.delete()\n  }\n\n  /**\n   * Deletes all values stored in the cache. In-flight writes to the cache will complete normally,\n   * but the corresponding responses will not be stored.\n   */\n  @Throws(IOException::class)\n  fun evictAll() {\n    cache.evictAll()\n  }\n\n  /**\n   * Returns an iterator over the URLs in this cache. This iterator doesn't throw\n   * `ConcurrentModificationException`, but if new responses are added while iterating, their URLs\n   * will not be returned. If existing responses are evicted during iteration, they will be absent\n   * (unless they were already returned).\n   *\n   * The iterator supports [MutableIterator.remove]. Removing a URL from the iterator evicts the\n   * corresponding response from the cache. Use this to evict selected responses.\n   */\n  @Throws(IOException::class)\n  fun urls(): MutableIterator<String> {\n    return object : MutableIterator<String> {\n      private val delegate: MutableIterator<DiskLruCache.Snapshot> = cache.snapshots()\n      private var nextUrl: String? = null\n      private var canRemove = false\n\n      override fun hasNext(): Boolean {\n        if (nextUrl != null) return true\n\n        canRemove = false // Prevent delegate.remove() on the wrong item!\n        while (delegate.hasNext()) {\n          try {\n            delegate.next().use { snapshot ->\n              val metadata = snapshot.getSource(ENTRY_METADATA).buffer()\n              nextUrl = metadata.readUtf8LineStrict()\n              return true\n            }\n          } catch (_: IOException) {\n            // We couldn't read the metadata for this snapshot; possibly because the host filesystem\n            // has disappeared! Skip it.\n          }\n        }\n\n        return false\n      }\n\n      override fun next(): String {\n        if (!hasNext()) throw NoSuchElementException()\n        val result = nextUrl!!\n        nextUrl = null\n        canRemove = true\n        return result\n      }\n\n      override fun remove() {\n        check(canRemove) { \"remove() before next()\" }\n        delegate.remove()\n      }\n    }\n  }\n\n  @Synchronized fun writeAbortCount(): Int = writeAbortCount\n\n  @Synchronized fun writeSuccessCount(): Int = writeSuccessCount\n\n  @Throws(IOException::class)\n  fun size(): Long = cache.size()\n\n  /** Max size of the cache (in bytes). */\n  fun maxSize(): Long = cache.maxSize\n\n  @Throws(IOException::class)\n  override fun flush() {\n    cache.flush()\n  }\n\n  @Throws(IOException::class)\n  override fun close() {\n    cache.close()\n  }\n\n  @get:JvmName(\"directory\")\n  val directory: File\n    get() = cache.directory.toFile()\n\n  @get:JvmName(\"directoryPath\")\n  val directoryPath: Path\n    get() = cache.directory\n\n  @JvmName(\"-deprecated_directory\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"directory\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun directory(): File = cache.directory.toFile()\n\n  @Synchronized internal fun trackResponse(cacheStrategy: CacheStrategy) {\n    requestCount++\n\n    if (cacheStrategy.networkRequest != null) {\n      // If this is a conditional request, we'll increment hitCount if/when it hits.\n      networkCount++\n    } else if (cacheStrategy.cacheResponse != null) {\n      // This response uses the cache and not the network. That's a cache hit.\n      hitCount++\n    }\n  }\n\n  @Synchronized internal fun trackConditionalCacheHit() {\n    hitCount++\n  }\n\n  @Synchronized fun networkCount(): Int = networkCount\n\n  @Synchronized fun hitCount(): Int = hitCount\n\n  @Synchronized fun requestCount(): Int = requestCount\n\n  private inner class RealCacheRequest(\n    private val editor: DiskLruCache.Editor,\n  ) : CacheRequest {\n    private val cacheOut: Sink = editor.newSink(ENTRY_BODY)\n    private val body: Sink\n    var done = false\n\n    init {\n      this.body =\n        object : ForwardingSink(cacheOut) {\n          @Throws(IOException::class)\n          override fun close() {\n            synchronized(this@Cache) {\n              if (done) return\n              done = true\n              writeSuccessCount++\n            }\n            super.close()\n            editor.commit()\n          }\n        }\n    }\n\n    override fun abort() {\n      synchronized(this@Cache) {\n        if (done) return\n        done = true\n        writeAbortCount++\n      }\n      cacheOut.closeQuietly()\n      try {\n        editor.abort()\n      } catch (_: IOException) {\n      }\n    }\n\n    override fun body(): Sink = body\n  }\n\n  private class Entry {\n    private val url: HttpUrl\n    private val varyHeaders: Headers\n    private val requestMethod: String\n    private val protocol: Protocol\n    private val code: Int\n    private val message: String\n    private val responseHeaders: Headers\n    private val handshake: Handshake?\n    private val sentRequestMillis: Long\n    private val receivedResponseMillis: Long\n\n    /**\n     * Reads an entry from an input stream. A typical entry looks like this:\n     *\n     * ```\n     * http://google.com/foo\n     * GET\n     * 2\n     * Accept-Language: fr-CA\n     * Accept-Charset: UTF-8\n     * HTTP/1.1 200 OK\n     * 3\n     * Content-Type: image/png\n     * Content-Length: 100\n     * Cache-Control: max-age=600\n     * ```\n     *\n     * A typical HTTPS file looks like this:\n     *\n     * ```\n     * https://google.com/foo\n     * GET\n     * 2\n     * Accept-Language: fr-CA\n     * Accept-Charset: UTF-8\n     * HTTP/1.1 200 OK\n     * 3\n     * Content-Type: image/png\n     * Content-Length: 100\n     * Cache-Control: max-age=600\n     *\n     * AES_256_WITH_MD5\n     * 2\n     * base64-encoded peerCertificate[0]\n     * base64-encoded peerCertificate[1]\n     * -1\n     * TLSv1.2\n     * ```\n     *\n     * The file is newline separated. The first two lines are the URL and the request method. Next\n     * is the number of HTTP Vary request header lines, followed by those lines.\n     *\n     * Next is the response status line, followed by the number of HTTP response header lines,\n     * followed by those lines.\n     *\n     * HTTPS responses also contain SSL session information. This begins with a blank line, and then\n     * a line containing the cipher suite. Next is the length of the peer certificate chain. These\n     * certificates are base64-encoded and appear each on their own line. The next line contains the\n     * length of the local certificate chain. These certificates are also base64-encoded and appear\n     * each on their own line. A length of -1 is used to encode a null array. The last line is\n     * optional. If present, it contains the TLS version.\n     */\n    @Throws(IOException::class)\n    constructor(rawSource: Source) {\n      rawSource.use {\n        val source = rawSource.buffer()\n        val urlLine = source.readUtf8LineStrict()\n        // Choice here is between failing with a correct RuntimeException\n        // or mostly silently with an IOException\n        url = urlLine.toHttpUrlOrNull() ?: throw IOException(\"Cache corruption for $urlLine\").also {\n          Platform.get().log(\"cache corruption\", WARN, it)\n        }\n        requestMethod = source.readUtf8LineStrict()\n        val varyHeadersBuilder = Headers.Builder()\n        val varyRequestHeaderLineCount = readInt(source)\n        for (i in 0 until varyRequestHeaderLineCount) {\n          varyHeadersBuilder.addLenient(source.readUtf8LineStrict())\n        }\n        varyHeaders = varyHeadersBuilder.build()\n\n        val statusLine = StatusLine.parse(source.readUtf8LineStrict())\n        protocol = statusLine.protocol\n        code = statusLine.code\n        message = statusLine.message\n        val responseHeadersBuilder = Headers.Builder()\n        val responseHeaderLineCount = readInt(source)\n        for (i in 0 until responseHeaderLineCount) {\n          responseHeadersBuilder.addLenient(source.readUtf8LineStrict())\n        }\n        val sendRequestMillisString = responseHeadersBuilder[SENT_MILLIS]\n        val receivedResponseMillisString = responseHeadersBuilder[RECEIVED_MILLIS]\n        responseHeadersBuilder.removeAll(SENT_MILLIS)\n        responseHeadersBuilder.removeAll(RECEIVED_MILLIS)\n        sentRequestMillis = sendRequestMillisString?.toLong() ?: 0L\n        receivedResponseMillis = receivedResponseMillisString?.toLong() ?: 0L\n        responseHeaders = responseHeadersBuilder.build()\n\n        if (url.isHttps) {\n          val blank = source.readUtf8LineStrict()\n          if (blank.isNotEmpty()) {\n            throw IOException(\"expected \\\"\\\" but was \\\"$blank\\\"\")\n          }\n          val cipherSuiteString = source.readUtf8LineStrict()\n          val cipherSuite = CipherSuite.forJavaName(cipherSuiteString)\n          val peerCertificates = readCertificateList(source)\n          val localCertificates = readCertificateList(source)\n          val tlsVersion =\n            if (!source.exhausted()) {\n              TlsVersion.forJavaName(source.readUtf8LineStrict())\n            } else {\n              TlsVersion.SSL_3_0\n            }\n          handshake = Handshake.get(tlsVersion, cipherSuite, peerCertificates, localCertificates)\n        } else {\n          handshake = null\n        }\n      }\n    }\n\n    constructor(response: Response) {\n      this.url = response.request.url\n      this.varyHeaders = response.varyHeaders()\n      this.requestMethod = response.request.method\n      this.protocol = response.protocol\n      this.code = response.code\n      this.message = response.message\n      this.responseHeaders = response.headers\n      this.handshake = response.handshake\n      this.sentRequestMillis = response.sentRequestAtMillis\n      this.receivedResponseMillis = response.receivedResponseAtMillis\n    }\n\n    @Throws(IOException::class)\n    fun writeTo(editor: DiskLruCache.Editor) {\n      editor.newSink(ENTRY_METADATA).buffer().use { sink ->\n        sink.writeUtf8(url.toString()).writeByte('\\n'.code)\n        sink.writeUtf8(requestMethod).writeByte('\\n'.code)\n        sink.writeDecimalLong(varyHeaders.size.toLong()).writeByte('\\n'.code)\n        for (i in 0 until varyHeaders.size) {\n          sink\n            .writeUtf8(varyHeaders.name(i))\n            .writeUtf8(\": \")\n            .writeUtf8(varyHeaders.value(i))\n            .writeByte('\\n'.code)\n        }\n\n        sink.writeUtf8(StatusLine(protocol, code, message).toString()).writeByte('\\n'.code)\n        sink.writeDecimalLong((responseHeaders.size + 2).toLong()).writeByte('\\n'.code)\n        for (i in 0 until responseHeaders.size) {\n          sink\n            .writeUtf8(responseHeaders.name(i))\n            .writeUtf8(\": \")\n            .writeUtf8(responseHeaders.value(i))\n            .writeByte('\\n'.code)\n        }\n        sink\n          .writeUtf8(SENT_MILLIS)\n          .writeUtf8(\": \")\n          .writeDecimalLong(sentRequestMillis)\n          .writeByte('\\n'.code)\n        sink\n          .writeUtf8(RECEIVED_MILLIS)\n          .writeUtf8(\": \")\n          .writeDecimalLong(receivedResponseMillis)\n          .writeByte('\\n'.code)\n\n        if (url.isHttps) {\n          sink.writeByte('\\n'.code)\n          sink.writeUtf8(handshake!!.cipherSuite.javaName).writeByte('\\n'.code)\n          writeCertList(sink, handshake.peerCertificates)\n          writeCertList(sink, handshake.localCertificates)\n          sink.writeUtf8(handshake.tlsVersion.javaName).writeByte('\\n'.code)\n        }\n      }\n    }\n\n    @Throws(IOException::class)\n    private fun readCertificateList(source: BufferedSource): List<Certificate> {\n      val length = readInt(source)\n      if (length == -1) return emptyList() // OkHttp v1.2 used -1 to indicate null.\n\n      try {\n        val certificateFactory = CertificateFactory.getInstance(\"X.509\")\n        val result = ArrayList<Certificate>(length)\n        for (i in 0 until length) {\n          val line = source.readUtf8LineStrict()\n          val bytes = Buffer()\n          val certificateBytes = line.decodeBase64() ?: throw IOException(\"Corrupt certificate in cache entry\")\n          bytes.write(certificateBytes)\n          result.add(certificateFactory.generateCertificate(bytes.inputStream()))\n        }\n        return result\n      } catch (e: CertificateException) {\n        throw IOException(e.message)\n      }\n    }\n\n    @Throws(IOException::class)\n    private fun writeCertList(\n      sink: BufferedSink,\n      certificates: List<Certificate>,\n    ) {\n      try {\n        sink.writeDecimalLong(certificates.size.toLong()).writeByte('\\n'.code)\n        for (element in certificates) {\n          val bytes = element.encoded\n          val line = bytes.toByteString().base64()\n          sink.writeUtf8(line).writeByte('\\n'.code)\n        }\n      } catch (e: CertificateEncodingException) {\n        throw IOException(e.message)\n      }\n    }\n\n    fun matches(\n      request: Request,\n      response: Response,\n    ): Boolean =\n      url == request.url &&\n        requestMethod == request.method &&\n        varyMatches(response, varyHeaders, request)\n\n    fun response(snapshot: DiskLruCache.Snapshot): Response {\n      val contentType = responseHeaders[\"Content-Type\"]\n      val contentLength = responseHeaders[\"Content-Length\"]\n      val cacheRequest = Request(url, varyHeaders, requestMethod)\n      return Response\n        .Builder()\n        .request(cacheRequest)\n        .protocol(protocol)\n        .code(code)\n        .message(message)\n        .headers(responseHeaders)\n        .body(CacheResponseBody(snapshot, contentType, contentLength))\n        .handshake(handshake)\n        .sentRequestAtMillis(sentRequestMillis)\n        .receivedResponseAtMillis(receivedResponseMillis)\n        .build()\n    }\n\n    companion object {\n      /** Synthetic response header: the local time when the request was sent. */\n      private val SENT_MILLIS = \"${Platform.get().getPrefix()}-Sent-Millis\"\n\n      /** Synthetic response header: the local time when the response was received. */\n      private val RECEIVED_MILLIS = \"${Platform.get().getPrefix()}-Received-Millis\"\n    }\n  }\n\n  private class CacheResponseBody(\n    val snapshot: DiskLruCache.Snapshot,\n    private val contentType: String?,\n    private val contentLength: String?,\n  ) : ResponseBody() {\n    private val bodySource: BufferedSource\n\n    init {\n      val source = snapshot.getSource(ENTRY_BODY)\n      bodySource =\n        object : ForwardingSource(source) {\n          @Throws(IOException::class)\n          override fun close() {\n            snapshot.close()\n            super.close()\n          }\n        }.buffer()\n    }\n\n    override fun contentType(): MediaType? = contentType?.toMediaTypeOrNull()\n\n    override fun contentLength(): Long = contentLength?.toLongOrDefault(-1L) ?: -1L\n\n    override fun source(): BufferedSource = bodySource\n  }\n\n  companion object {\n    private const val VERSION = 201105\n    private const val ENTRY_METADATA = 0\n    private const val ENTRY_BODY = 1\n    private const val ENTRY_COUNT = 2\n\n    @JvmStatic\n    fun key(url: HttpUrl): String =\n      url\n        .toString()\n        .encodeUtf8()\n        .md5()\n        .hex()\n\n    @Throws(IOException::class)\n    internal fun readInt(source: BufferedSource): Int {\n      try {\n        val result = source.readDecimalLong()\n        val line = source.readUtf8LineStrict()\n        if (result < 0L || result > Integer.MAX_VALUE || line.isNotEmpty()) {\n          throw IOException(\"expected an int but was \\\"$result$line\\\"\")\n        }\n        return result.toInt()\n      } catch (e: NumberFormatException) {\n        throw IOException(e.message)\n      }\n    }\n\n    /**\n     * Returns true if none of the Vary headers have changed between [cachedRequest] and\n     * [newRequest].\n     */\n    fun varyMatches(\n      cachedResponse: Response,\n      cachedRequest: Headers,\n      newRequest: Request,\n    ): Boolean =\n      cachedResponse.headers.varyFields().none {\n        cachedRequest.values(it) != newRequest.headers(it)\n      }\n\n    /** Returns true if a Vary header contains an asterisk. Such responses cannot be cached. */\n    fun Response.hasVaryAll(): Boolean = \"*\" in headers.varyFields()\n\n    /**\n     * Returns the names of the request headers that need to be checked for equality when caching.\n     */\n    private fun Headers.varyFields(): Set<String> {\n      var result: MutableSet<String>? = null\n      for (i in 0 until size) {\n        if (!\"Vary\".equals(name(i), ignoreCase = true)) {\n          continue\n        }\n\n        val value = value(i)\n        if (result == null) {\n          result = TreeSet(String.CASE_INSENSITIVE_ORDER)\n        }\n        for (varyField in value.split(',')) {\n          result.add(varyField.trim())\n        }\n      }\n      return result ?: emptySet()\n    }\n\n    /**\n     * Returns the subset of the headers in this's request that impact the content of this's body.\n     */\n    fun Response.varyHeaders(): Headers {\n      // Use the request headers sent over the network, since that's what the response varies on.\n      // Otherwise OkHttp-supplied headers like \"Accept-Encoding: gzip\" may be lost.\n      val requestHeaders = networkResponse!!.request.headers\n      val responseHeaders = headers\n      return varyHeaders(requestHeaders, responseHeaders)\n    }\n\n    /**\n     * Returns the subset of the headers in [requestHeaders] that impact the content of the\n     * response's body.\n     */\n    private fun varyHeaders(\n      requestHeaders: Headers,\n      responseHeaders: Headers,\n    ): Headers {\n      val varyFields = responseHeaders.varyFields()\n      if (varyFields.isEmpty()) return Headers.EMPTY\n\n      val result = Headers.Builder()\n      for (i in 0 until requestHeaders.size) {\n        val fieldName = requestHeaders.name(i)\n        if (fieldName in varyFields) {\n          result.add(fieldName, requestHeaders.value(i))\n        }\n      }\n      return result.build()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/CacheControl.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.util.concurrent.TimeUnit\nimport kotlin.time.Duration\nimport okhttp3.internal.commonBuild\nimport okhttp3.internal.commonClampToInt\nimport okhttp3.internal.commonForceCache\nimport okhttp3.internal.commonForceNetwork\nimport okhttp3.internal.commonImmutable\nimport okhttp3.internal.commonNoCache\nimport okhttp3.internal.commonNoStore\nimport okhttp3.internal.commonNoTransform\nimport okhttp3.internal.commonOnlyIfCached\nimport okhttp3.internal.commonParse\nimport okhttp3.internal.commonToString\n\n/**\n * A Cache-Control header with cache directives from a server or client. These directives set policy\n * on what responses can be stored, and which requests can be satisfied by those stored responses.\n *\n * See [RFC 7234, 5.2](https://tools.ietf.org/html/rfc7234#section-5.2).\n */\nclass CacheControl internal constructor(\n  /**\n   * In a response, this field's name \"no-cache\" is misleading. It doesn't prevent us from caching\n   * the response; it only means we have to validate the response with the origin server before\n   * returning it. We can do this with a conditional GET.\n   *\n   * In a request, it means do not use a cache to satisfy the request.\n   */\n  @get:JvmName(\"noCache\") val noCache: Boolean,\n  /** If true, this response should not be cached. */\n  @get:JvmName(\"noStore\") val noStore: Boolean,\n  /** The duration past the response's served date that it can be served without validation. */\n  @get:JvmName(\"maxAgeSeconds\") val maxAgeSeconds: Int,\n  /**\n   * The \"s-maxage\" directive is the max age for shared caches. Not to be confused with \"max-age\"\n   * for non-shared caches, As in Firefox and Chrome, this directive is not honored by this cache.\n   */\n  @get:JvmName(\"sMaxAgeSeconds\") val sMaxAgeSeconds: Int,\n  val isPrivate: Boolean,\n  val isPublic: Boolean,\n  @get:JvmName(\"mustRevalidate\") val mustRevalidate: Boolean,\n  @get:JvmName(\"maxStaleSeconds\") val maxStaleSeconds: Int,\n  @get:JvmName(\"minFreshSeconds\") val minFreshSeconds: Int,\n  /**\n   * This field's name \"only-if-cached\" is misleading. It actually means \"do not use the network\".\n   * It is set by a client who only wants to make a request if it can be fully satisfied by the\n   * cache. Cached responses that would require validation (ie. conditional gets) are not permitted\n   * if this header is set.\n   */\n  @get:JvmName(\"onlyIfCached\") val onlyIfCached: Boolean,\n  @get:JvmName(\"noTransform\") val noTransform: Boolean,\n  @get:JvmName(\"immutable\") val immutable: Boolean,\n  internal var headerValue: String?,\n) {\n  @JvmName(\"-deprecated_noCache\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"noCache\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun noCache(): Boolean = noCache\n\n  @JvmName(\"-deprecated_noStore\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"noStore\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun noStore(): Boolean = noStore\n\n  @JvmName(\"-deprecated_maxAgeSeconds\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"maxAgeSeconds\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun maxAgeSeconds(): Int = maxAgeSeconds\n\n  @JvmName(\"-deprecated_sMaxAgeSeconds\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"sMaxAgeSeconds\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun sMaxAgeSeconds(): Int = sMaxAgeSeconds\n\n  @JvmName(\"-deprecated_mustRevalidate\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"mustRevalidate\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun mustRevalidate(): Boolean = mustRevalidate\n\n  @JvmName(\"-deprecated_maxStaleSeconds\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"maxStaleSeconds\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun maxStaleSeconds(): Int = maxStaleSeconds\n\n  @JvmName(\"-deprecated_minFreshSeconds\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"minFreshSeconds\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun minFreshSeconds(): Int = minFreshSeconds\n\n  @JvmName(\"-deprecated_onlyIfCached\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"onlyIfCached\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun onlyIfCached(): Boolean = onlyIfCached\n\n  @JvmName(\"-deprecated_noTransform\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"noTransform\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun noTransform(): Boolean = noTransform\n\n  @JvmName(\"-deprecated_immutable\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"immutable\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun immutable(): Boolean = immutable\n\n  override fun toString(): String = commonToString()\n\n  /** Builds a `Cache-Control` request header. */\n  class Builder {\n    internal var noCache: Boolean = false\n    internal var noStore: Boolean = false\n    internal var maxAgeSeconds = -1\n    internal var maxStaleSeconds = -1\n    internal var minFreshSeconds = -1\n    internal var onlyIfCached: Boolean = false\n    internal var noTransform: Boolean = false\n    internal var immutable: Boolean = false\n\n    /** Don't accept an unvalidated cached response. */\n    fun noCache() = commonNoCache()\n\n    /** Don't store the server's response in any cache. */\n    fun noStore() = commonNoStore()\n\n    /**\n     * Only accept the response if it is in the cache. If the response isn't cached, a `504\n     * Unsatisfiable Request` response will be returned.\n     */\n    fun onlyIfCached() = commonOnlyIfCached()\n\n    /** Don't accept a transformed response. */\n    fun noTransform() = commonNoTransform()\n\n    fun immutable() = commonImmutable()\n\n    /**\n     * Sets the maximum age of a cached response. If the cache response's age exceeds [maxAge], it\n     * will not be used and a network request will be made.\n     *\n     * @param maxAge a non-negative duration. This is stored and transmitted with [TimeUnit.SECONDS]\n     *     precision; finer precision will be lost.\n     */\n    fun maxAge(maxAge: Duration) =\n      apply {\n        val maxAgeSeconds = maxAge.inWholeSeconds\n        require(maxAgeSeconds >= 0) { \"maxAge < 0: $maxAgeSeconds\" }\n        this.maxAgeSeconds = maxAgeSeconds.commonClampToInt()\n      }\n\n    fun maxStale(maxStale: Duration) =\n      apply {\n        val maxStaleSeconds = maxStale.inWholeSeconds\n        require(maxStaleSeconds >= 0) { \"maxStale < 0: $maxStaleSeconds\" }\n        this.maxStaleSeconds = maxStaleSeconds.commonClampToInt()\n      }\n\n    fun minFresh(minFresh: Duration) =\n      apply {\n        val minFreshSeconds = minFresh.inWholeSeconds\n        require(minFreshSeconds >= 0) { \"minFresh < 0: $minFreshSeconds\" }\n        this.minFreshSeconds = minFreshSeconds.commonClampToInt()\n      }\n\n    /**\n     * Sets the maximum age of a cached response. If the cache response's age exceeds [maxAge], it\n     * will not be used and a network request will be made.\n     *\n     * @param maxAge a non-negative integer. This is stored and transmitted with [TimeUnit.SECONDS]\n     *     precision; finer precision will be lost.\n     */\n    fun maxAge(\n      maxAge: Int,\n      timeUnit: TimeUnit,\n    ) = apply {\n      require(maxAge >= 0) { \"maxAge < 0: $maxAge\" }\n      val maxAgeSecondsLong = timeUnit.toSeconds(maxAge.toLong())\n      this.maxAgeSeconds = maxAgeSecondsLong.commonClampToInt()\n    }\n\n    /**\n     * Accept cached responses that have exceeded their freshness lifetime by up to `maxStale`. If\n     * unspecified, stale cache responses will not be used.\n     *\n     * @param maxStale a non-negative integer. This is stored and transmitted with\n     *     [TimeUnit.SECONDS] precision; finer precision will be lost.\n     */\n    fun maxStale(\n      maxStale: Int,\n      timeUnit: TimeUnit,\n    ) = apply {\n      require(maxStale >= 0) { \"maxStale < 0: $maxStale\" }\n      val maxStaleSecondsLong = timeUnit.toSeconds(maxStale.toLong())\n      this.maxStaleSeconds = maxStaleSecondsLong.commonClampToInt()\n    }\n\n    /**\n     * Sets the minimum number of seconds that a response will continue to be fresh for. If the\n     * response will be stale when [minFresh] have elapsed, the cached response will not be used and\n     * a network request will be made.\n     *\n     * @param minFresh a non-negative integer. This is stored and transmitted with\n     *     [TimeUnit.SECONDS] precision; finer precision will be lost.\n     */\n    fun minFresh(\n      minFresh: Int,\n      timeUnit: TimeUnit,\n    ) = apply {\n      require(minFresh >= 0) { \"minFresh < 0: $minFresh\" }\n      val minFreshSecondsLong = timeUnit.toSeconds(minFresh.toLong())\n      this.minFreshSeconds = minFreshSecondsLong.commonClampToInt()\n    }\n\n    fun build(): CacheControl = commonBuild()\n  }\n\n  companion object {\n    /**\n     * Cache control request directives that require network validation of responses. Note that such\n     * requests may be assisted by the cache via conditional GET requests.\n     */\n    @JvmField\n    val FORCE_NETWORK = commonForceNetwork()\n\n    /**\n     * Cache control request directives that uses the cache only, even if the cached response is\n     * stale. If the response isn't available in the cache or requires server validation, the call\n     * will fail with a `504 Unsatisfiable Request`.\n     */\n    @JvmField\n    val FORCE_CACHE = commonForceCache()\n\n    /**\n     * Returns the cache directives of [headers]. This honors both Cache-Control and Pragma headers\n     * if they are present.\n     */\n    @JvmStatic\n    fun parse(headers: Headers): CacheControl = commonParse(headers)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Call.kt",
    "content": "/*\n * Copyright (c) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport kotlin.reflect.KClass\nimport okio.IOException\nimport okio.Timeout\n\n/**\n * A call is a request that has been prepared for execution. A call can be canceled. As this object\n * represents a single request/response pair (stream), it cannot be executed twice.\n */\ninterface Call : Cloneable {\n  /** Returns the original request that initiated this call. */\n  fun request(): Request\n\n  /**\n   * Invokes the request immediately, and blocks until the response can be processed or is in error.\n   *\n   * To avoid leaking resources callers should close the [Response] which in turn will close the\n   * underlying [ResponseBody].\n   *\n   * ```java\n   * // ensure the response (and underlying response body) is closed\n   * try (Response response = client.newCall(request).execute()) {\n   *   ...\n   * }\n   * ```\n   *\n   * The caller may read the response body with the response's [Response.body] method. To avoid\n   * leaking resources callers must [close the response body][ResponseBody] or the response.\n   *\n   * Note that transport-layer success (receiving a HTTP response code, headers and body) does not\n   * necessarily indicate application-layer success: `response` may still indicate an unhappy HTTP\n   * response code like 404 or 500.\n   *\n   * @throws IOException if the request could not be executed due to cancellation, a connectivity\n   *     problem or timeout. Because networks can fail during an exchange, it is possible that the\n   *     remote server accepted the request before the failure.\n   * @throws IllegalStateException when the call has already been executed.\n   */\n  @Throws(IOException::class)\n  fun execute(): Response\n\n  /**\n   * Schedules the request to be executed at some point in the future.\n   *\n   * The [dispatcher][OkHttpClient.dispatcher] defines when the request will run: usually\n   * immediately unless there are several other requests currently being executed.\n   *\n   * This client will later call back `responseCallback` with either an HTTP response or a failure\n   * exception.\n   *\n   * @throws IllegalStateException when the call has already been executed.\n   */\n  fun enqueue(responseCallback: Callback)\n\n  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */\n  fun cancel()\n\n  /**\n   * Returns true if this call has been either [executed][execute] or [enqueued][enqueue]. It is an\n   * error to execute a call more than once.\n   */\n  fun isExecuted(): Boolean\n\n  fun isCanceled(): Boolean\n\n  /**\n   * Returns a timeout that spans the entire call: resolving DNS, connecting, writing the request\n   * body, server processing, and reading the response body. If the call requires redirects or\n   * retries all must complete within one timeout period.\n   *\n   * Configure the client's default timeout with [OkHttpClient.Builder.callTimeout].\n   */\n  fun timeout(): Timeout\n\n  /**\n   * Configure this call to publish all future events to [eventListener], in addition to the\n   * listeners configured by [OkHttpClient.Builder.eventListener] and other calls to this function.\n   *\n   * If this call is later [cloned][clone], [eventListener] will not be notified of its events.\n   *\n   * There is no mechanism to remove an event listener. Implementations should instead ignore events\n   * that they are not interested in.\n   *\n   * @see EventListener for semantics and restrictions on listener implementations.\n   */\n  fun addEventListener(eventListener: EventListener)\n\n  /**\n   * Returns the tag attached with [type] as a key, or null if no tag is attached with that key.\n   *\n   * The tags on a call are seeded from the [request tags][Request.tag]. This set will grow if new\n   * tags are computed.\n   */\n  fun <T : Any> tag(type: KClass<T>): T?\n\n  /**\n   * Returns the tag attached with [type] as a key, or null if no tag is attached with that key.\n   *\n   * The tags on a call are seeded from the [request tags][Request.tag]. This set will grow if new\n   * tags are computed.\n   */\n  fun <T> tag(type: Class<out T>): T?\n\n  /**\n   * Returns the tag attached with [type] as a key. If it is absent, then [computeIfAbsent] is\n   * called and that value is both inserted and returned.\n   *\n   * If multiple calls to this function are made concurrently with the same [type], multiple values\n   * may be computed. But only one value will be inserted, and that inserted value will be returned\n   * to all callers.\n   *\n   * If computing multiple values is problematic, use an appropriate concurrency mechanism in your\n   * [computeIfAbsent] implementation. No locks are held while calling this function.\n   */\n  fun <T : Any> tag(\n    type: KClass<T>,\n    computeIfAbsent: () -> T,\n  ): T\n\n  /**\n   * Returns the tag attached with [type] as a key. If it is absent, then [computeIfAbsent] is\n   * called and that value is both inserted and returned.\n   *\n   * If multiple calls to this function are made concurrently with the same [type], multiple values\n   * may be computed. But only one value will be inserted, and that inserted value will be returned\n   * to all callers.\n   *\n   * If computing multiple values is problematic, use an appropriate concurrency mechanism in your\n   * [computeIfAbsent] implementation. No locks are held while calling this function.\n   */\n  fun <T : Any> tag(\n    type: Class<T>,\n    computeIfAbsent: () -> T,\n  ): T\n\n  /**\n   * Create a new, identical call to this one which can be enqueued or executed even if this call\n   * has already been.\n   *\n   * The tags on the returned call will equal the tags as on [request]. Any tags that were computed\n   * for this call will not be included on the cloned call. If necessary you may manually copy over\n   * specific tags by re-computing them:\n   *\n   * ```kotlin\n   * val copy = original.clone()\n   *\n   * val myTag = original.tag(MyTag::class)\n   * if (myTag != null) {\n   *   copy.tag(MyTag::class) { myTag }\n   * }\n   * ```\n   *\n   * ```java\n   * Call copy = original.clone();\n   *\n   * MyTag myTag = original.tag(MyTag.class);\n   * if (myTag != null) {\n   *   copy.tag(MyTag.class, () -> myTag);\n   * }\n   * ```\n   *\n   * If any event listeners were installed on this call with [addEventListener], they will not be\n   * installed on this copy.\n   */\n  public override fun clone(): Call\n\n  fun interface Factory {\n    fun newCall(request: Request): Call\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Callback.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okio.IOException\n\ninterface Callback {\n  /**\n   * Called when the request could not be executed due to cancellation, a connectivity problem or\n   * timeout. Because networks can fail during an exchange, it is possible that the remote server\n   * accepted the request before the failure.\n   */\n  fun onFailure(\n    call: Call,\n    e: IOException,\n  )\n\n  /**\n   * Called when the HTTP response was successfully returned by the remote server. The callback may\n   * proceed to read the response body with [Response.body]. The response is still live until its\n   * response body is [closed][ResponseBody]. The recipient of the callback may consume the response\n   * body on another thread.\n   *\n   * Note that transport-layer success (receiving a HTTP response code, headers and body) does not\n   * necessarily indicate application-layer success: `response` may still indicate an unhappy HTTP\n   * response code like 404 or 500.\n   */\n  @Throws(IOException::class)\n  fun onResponse(\n    call: Call,\n    response: Response,\n  )\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/CertificatePinner.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport okhttp3.internal.filterList\nimport okhttp3.internal.tls.CertificateChainCleaner\nimport okhttp3.internal.toCanonicalHost\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeBase64\nimport okio.ByteString.Companion.toByteString\n\n/**\n * Constrains which certificates are trusted. Pinning certificates defends against attacks on\n * certificate authorities. It also prevents connections through man-in-the-middle certificate\n * authorities either known or unknown to the application's user.\n * This class currently pins a certificate's Subject Public Key Info as described on\n * [Adam Langley's Weblog][langley]. Pins are either base64 SHA-256 hashes as in\n * [HTTP Public Key Pinning (HPKP)][rfc_7469] or SHA-1 base64 hashes as in Chromium's\n * [static certificates][static_certificates].\n *\n * ## Setting up Certificate Pinning\n *\n * The easiest way to pin a host is turn on pinning with a broken configuration and read the\n * expected configuration when the connection fails. Be sure to do this on a trusted network, and\n * without man-in-the-middle tools like [Charles][charles] or [Fiddler][fiddler].\n *\n * For example, to pin `https://publicobject.com`, start with a broken configuration:\n *\n * ```java\n * String hostname = \"publicobject.com\";\n * CertificatePinner certificatePinner = new CertificatePinner.Builder()\n *     .add(hostname, \"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\")\n *     .build();\n * OkHttpClient client = OkHttpClient.Builder()\n *     .certificatePinner(certificatePinner)\n *     .build();\n *\n * Request request = new Request.Builder()\n *     .url(\"https://\" + hostname)\n *     .build();\n * client.newCall(request).execute();\n * ```\n *\n * As expected, this fails with a certificate pinning exception:\n *\n * ```java\n * javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!\n * Peer certificate chain:\n *     sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=: CN=publicobject.com, OU=PositiveSSL\n *     sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=: CN=COMODO RSA Secure Server CA\n *     sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=: CN=COMODO RSA Certification Authority\n *     sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=: CN=AddTrust External CA Root\n * Pinned certificates for publicobject.com:\n *     sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n *   at okhttp3.CertificatePinner.check(CertificatePinner.java)\n *   at okhttp3.Connection.upgradeToTls(Connection.java)\n *   at okhttp3.Connection.connect(Connection.java)\n *   at okhttp3.Connection.connectAndSetOwner(Connection.java)\n * ```\n *\n * Follow up by pasting the public key hashes from the exception into the\n * certificate pinner's configuration:\n *\n * ```java\n * CertificatePinner certificatePinner = new CertificatePinner.Builder()\n *     .add(\"publicobject.com\", \"sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=\")\n *     .add(\"publicobject.com\", \"sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=\")\n *     .add(\"publicobject.com\", \"sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\")\n *     .add(\"publicobject.com\", \"sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=\")\n *     .build();\n * ```\n *\n * ## Domain Patterns\n *\n * Pinning is per-hostname and/or per-wildcard pattern. To pin both `publicobject.com` and\n * `www.publicobject.com` you must configure both hostnames. Or you may use patterns to match\n * sets of related domain names. The following forms are permitted:\n *\n *  * **Full domain name**: you may pin an exact domain name like `www.publicobject.com`. It won't\n *    match additional prefixes (`us-west.www.publicobject.com`) or suffixes (`publicobject.com`).\n *\n *  * **Any number of subdomains**: Use two asterisks to like `**.publicobject.com` to match any\n *    number of prefixes (`us-west.www.publicobject.com`, `www.publicobject.com`) including no\n *    prefix at all (`publicobject.com`). For most applications this is the best way to configure\n *    certificate pinning.\n *\n *  * **Exactly one subdomain**: Use a single asterisk like `*.publicobject.com` to match exactly\n *    one prefix (`www.publicobject.com`, `api.publicobject.com`). Be careful with this approach as\n *    no pinning will be enforced if additional prefixes are present, or if no prefixes are present.\n *\n * Note that any other form is unsupported. You may not use asterisks in any position other than\n * the leftmost label.\n *\n * If multiple patterns match a hostname, any match is sufficient. For example, suppose pin A\n * applies to `*.publicobject.com` and pin B applies to `api.publicobject.com`. Handshakes for\n * `api.publicobject.com` are valid if either A's or B's certificate is in the chain.\n *\n * ## Warning: Certificate Pinning is Dangerous!\n *\n * Pinning certificates limits your server team's abilities to update their TLS certificates. By\n * pinning certificates, you take on additional operational complexity and limit your ability to\n * migrate between certificate authorities. Do not use certificate pinning without the blessing of\n * your server's TLS administrator!\n *\n * ### Note about self-signed certificates\n *\n * [CertificatePinner] can not be used to pin self-signed certificate if such certificate is not\n * accepted by [javax.net.ssl.TrustManager].\n *\n * See also [OWASP: Certificate and Public Key Pinning][owasp].\n *\n * [charles]: http://charlesproxy.com\n * [fiddler]: http://fiddlertool.com\n * [langley]: http://goo.gl/AIx3e5\n * [owasp]: https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning\n * [rfc_7469]: http://tools.ietf.org/html/rfc7469\n * [static_certificates]: http://goo.gl/XDh6je\n */\n@Suppress(\"NAME_SHADOWING\")\nclass CertificatePinner internal constructor(\n  val pins: Set<Pin>,\n  internal val certificateChainCleaner: CertificateChainCleaner? = null,\n) {\n  /**\n   * Confirms that at least one of the certificates pinned for `hostname` is in `peerCertificates`.\n   * Does nothing if there are no certificates pinned for `hostname`. OkHttp calls this after a\n   * successful TLS handshake, but before the connection is used.\n   *\n   * @throws SSLPeerUnverifiedException if `peerCertificates` don't match the certificates pinned\n   *     for `hostname`.\n   */\n  @Throws(SSLPeerUnverifiedException::class)\n  fun check(\n    hostname: String,\n    peerCertificates: List<Certificate>,\n  ) = check(hostname) {\n    (certificateChainCleaner?.clean(peerCertificates, hostname) ?: peerCertificates)\n      .map { it as X509Certificate }\n  }\n\n  internal fun check(\n    hostname: String,\n    cleanedPeerCertificatesFn: () -> List<X509Certificate>,\n  ) {\n    val pins = findMatchingPins(hostname)\n    if (pins.isEmpty()) return\n\n    val peerCertificates = cleanedPeerCertificatesFn()\n\n    for (peerCertificate in peerCertificates) {\n      // Lazily compute the hashes for each certificate.\n      var sha1: ByteString? = null\n      var sha256: ByteString? = null\n\n      for (pin in pins) {\n        when (pin.hashAlgorithm) {\n          \"sha256\" -> {\n            if (sha256 == null) sha256 = peerCertificate.sha256Hash()\n            if (pin.hash == sha256) return // Success!\n          }\n\n          \"sha1\" -> {\n            if (sha1 == null) sha1 = peerCertificate.sha1Hash()\n            if (pin.hash == sha1) return // Success!\n          }\n\n          else -> {\n            throw AssertionError(\"unsupported hashAlgorithm: ${pin.hashAlgorithm}\")\n          }\n        }\n      }\n    }\n\n    // If we couldn't find a matching pin, format a nice exception.\n    val message =\n      buildString {\n        append(\"Certificate pinning failure!\")\n        append(\"\\n  Peer certificate chain:\")\n        for (element in peerCertificates) {\n          append(\"\\n    \")\n          append(pin(element))\n          append(\": \")\n          append(element.subjectDN.name)\n        }\n        append(\"\\n  Pinned certificates for \")\n        append(hostname)\n        append(\":\")\n        for (pin in pins) {\n          append(\"\\n    \")\n          append(pin)\n        }\n      }\n    throw SSLPeerUnverifiedException(message)\n  }\n\n  @Deprecated(\n    \"replaced with {@link #check(String, List)}.\",\n    ReplaceWith(\"check(hostname, peerCertificates.toList())\"),\n  )\n  @Throws(SSLPeerUnverifiedException::class)\n  fun check(\n    hostname: String,\n    vararg peerCertificates: Certificate,\n  ) {\n    check(hostname, peerCertificates.toList())\n  }\n\n  /**\n   * Returns list of matching certificates' pins for the hostname. Returns an empty list if the\n   * hostname does not have pinned certificates.\n   */\n  fun findMatchingPins(hostname: String): List<Pin> = pins.filterList { matchesHostname(hostname) }\n\n  /** Returns a certificate pinner that uses `certificateChainCleaner`. */\n  internal fun withCertificateChainCleaner(certificateChainCleaner: CertificateChainCleaner): CertificatePinner =\n    if (this.certificateChainCleaner == certificateChainCleaner) {\n      this\n    } else {\n      CertificatePinner(pins, certificateChainCleaner)\n    }\n\n  override fun equals(other: Any?): Boolean =\n    other is CertificatePinner &&\n      other.pins == pins &&\n      other.certificateChainCleaner == certificateChainCleaner\n\n  override fun hashCode(): Int {\n    var result = 37\n    result = 41 * result + pins.hashCode()\n    result = 41 * result + certificateChainCleaner.hashCode()\n    return result\n  }\n\n  /** A hostname pattern and certificate hash for Certificate Pinning. */\n  class Pin(\n    pattern: String,\n    pin: String,\n  ) {\n    /** A hostname like `example.com` or a pattern like `*.example.com` (canonical form). */\n    val pattern: String\n\n    /** Either `sha1` or `sha256`. */\n    val hashAlgorithm: String\n\n    /** The hash of the pinned certificate using [hashAlgorithm]. */\n    val hash: ByteString\n\n    init {\n      require(\n        (pattern.startsWith(\"*.\") && pattern.indexOf(\"*\", 1) == -1) ||\n          (pattern.startsWith(\"**.\") && pattern.indexOf(\"*\", 2) == -1) ||\n          pattern.indexOf(\"*\") == -1,\n      ) {\n        \"Unexpected pattern: $pattern\"\n      }\n\n      this.pattern =\n        pattern.toCanonicalHost() ?: throw IllegalArgumentException(\"Invalid pattern: $pattern\")\n\n      when {\n        pin.startsWith(\"sha1/\") -> {\n          this.hashAlgorithm = \"sha1\"\n          this.hash = pin.substring(\"sha1/\".length).decodeBase64() ?: throw IllegalArgumentException(\"Invalid pin hash: $pin\")\n        }\n\n        pin.startsWith(\"sha256/\") -> {\n          this.hashAlgorithm = \"sha256\"\n          this.hash = pin.substring(\"sha256/\".length).decodeBase64() ?: throw IllegalArgumentException(\"Invalid pin hash: $pin\")\n        }\n\n        else -> {\n          throw IllegalArgumentException(\"pins must start with 'sha256/' or 'sha1/': $pin\")\n        }\n      }\n    }\n\n    fun matchesHostname(hostname: String): Boolean =\n      when {\n        pattern.startsWith(\"**.\") -> {\n          // With ** empty prefixes match so exclude the dot from regionMatches().\n          val suffixLength = pattern.length - 3\n          val prefixLength = hostname.length - suffixLength\n          hostname.regionMatches(hostname.length - suffixLength, pattern, 3, suffixLength) &&\n            (prefixLength == 0 || hostname[prefixLength - 1] == '.')\n        }\n\n        pattern.startsWith(\"*.\") -> {\n          // With * there must be a prefix so include the dot in regionMatches().\n          val suffixLength = pattern.length - 1\n          val prefixLength = hostname.length - suffixLength\n          hostname.regionMatches(hostname.length - suffixLength, pattern, 1, suffixLength) &&\n            hostname.lastIndexOf('.', prefixLength - 1) == -1\n        }\n\n        else -> {\n          hostname == pattern\n        }\n      }\n\n    fun matchesCertificate(certificate: X509Certificate): Boolean =\n      when (hashAlgorithm) {\n        \"sha256\" -> hash == certificate.sha256Hash()\n        \"sha1\" -> hash == certificate.sha1Hash()\n        else -> false\n      }\n\n    override fun toString(): String = \"$hashAlgorithm/${hash.base64()}\"\n\n    override fun equals(other: Any?): Boolean {\n      if (this === other) return true\n      if (other !is Pin) return false\n\n      if (pattern != other.pattern) return false\n      if (hashAlgorithm != other.hashAlgorithm) return false\n      if (hash != other.hash) return false\n\n      return true\n    }\n\n    override fun hashCode(): Int {\n      var result = pattern.hashCode()\n      result = 31 * result + hashAlgorithm.hashCode()\n      result = 31 * result + hash.hashCode()\n      return result\n    }\n  }\n\n  /** Builds a configured certificate pinner. */\n  class Builder {\n    val pins = mutableListOf<Pin>()\n\n    /**\n     * Pins certificates for `pattern`.\n     *\n     * @param pattern lower-case host name or wildcard pattern such as `*.example.com`.\n     * @param pins SHA-256 or SHA-1 hashes. Each pin is a hash of a certificate's Subject Public Key\n     *     Info, base64-encoded and prefixed with either `sha256/` or `sha1/`.\n     */\n    fun add(\n      pattern: String,\n      vararg pins: String,\n    ) = apply {\n      for (pin in pins) {\n        this.pins.add(Pin(pattern, pin))\n      }\n    }\n\n    fun build(): CertificatePinner = CertificatePinner(pins.toSet())\n  }\n\n  companion object {\n    @JvmField\n    val DEFAULT = Builder().build()\n\n    @JvmStatic\n    fun X509Certificate.sha1Hash(): ByteString = publicKey.encoded.toByteString().sha1()\n\n    @JvmStatic\n    fun X509Certificate.sha256Hash(): ByteString = publicKey.encoded.toByteString().sha256()\n\n    /**\n     * Returns the SHA-256 of `certificate`'s public key.\n     *\n     * In OkHttp 3.1.2 and earlier, this returned a SHA-1 hash of the public key. Both types are\n     * supported, but SHA-256 is preferred.\n     */\n    @JvmStatic\n    fun pin(certificate: Certificate): String {\n      require(certificate is X509Certificate) { \"Certificate pinning requires X509 certificates\" }\n      return \"sha256/${certificate.sha256Hash().base64()}\"\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Challenge.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.nio.charset.Charset\nimport java.util.Collections.singletonMap\nimport java.util.Locale.US\nimport kotlin.text.Charsets.ISO_8859_1\nimport okhttp3.internal.unmodifiable\n\n/**\n * An [RFC 7235][rfc_7235] challenge.\n *\n * [rfc_7235]: https://tools.ietf.org/html/rfc7235\n */\nclass Challenge(\n  /** Returns the authentication scheme, like `Basic`. */\n  @get:JvmName(\"scheme\") val scheme: String,\n  authParams: Map<String?, String>,\n) {\n  /**\n   * Returns the auth params, including [realm] and [charset] if present, but as\n   * strings. The map's keys are lowercase and should be treated case-insensitively.\n   */\n  @get:JvmName(\"authParams\")\n  val authParams: Map<String?, String>\n\n  /** Returns the protection space. */\n  @get:JvmName(\"realm\")\n  val realm: String?\n    get() = authParams[\"realm\"]\n\n  /** The charset that should be used to encode the credentials. */\n  @get:JvmName(\"charset\")\n  val charset: Charset\n    get() {\n      val charset = authParams[\"charset\"]\n      if (charset != null) {\n        try {\n          return Charset.forName(charset)\n        } catch (ignore: Exception) {\n        }\n      }\n      return ISO_8859_1\n    }\n\n  constructor(scheme: String, realm: String) : this(scheme, singletonMap(\"realm\", realm))\n\n  init {\n    val newAuthParams = mutableMapOf<String?, String>()\n    for ((key, value) in authParams) {\n      val newKey = key?.lowercase(US)\n      newAuthParams[newKey] = value\n    }\n    this.authParams = newAuthParams.unmodifiable()\n  }\n\n  /** Returns a copy of this charset that expects a credential encoded with [charset]. */\n  fun withCharset(charset: Charset): Challenge {\n    val authParams = this.authParams.toMutableMap()\n    authParams[\"charset\"] = charset.name()\n    return Challenge(scheme, authParams)\n  }\n\n  @JvmName(\"-deprecated_scheme\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"scheme\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun scheme(): String = scheme\n\n  @JvmName(\"-deprecated_authParams\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"authParams\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun authParams(): Map<String?, String> = authParams\n\n  @JvmName(\"-deprecated_realm\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"realm\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun realm(): String? = realm\n\n  @JvmName(\"-deprecated_charset\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"charset\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun charset(): Charset = charset\n\n  override fun equals(other: Any?): Boolean =\n    other is Challenge &&\n      other.scheme == scheme &&\n      other.authParams == authParams\n\n  override fun hashCode(): Int {\n    var result = 29\n    result = 31 * result + scheme.hashCode()\n    result = 31 * result + authParams.hashCode()\n    return result\n  }\n\n  override fun toString(): String = \"$scheme authParams=$authParams\"\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/CipherSuite.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\n/**\n * [TLS cipher suites][iana_tls_parameters].\n *\n * **Not all cipher suites are supported on all platforms.** As newer cipher suites are created (for\n * stronger privacy, better performance, etc.) they will be adopted by the platform and then exposed\n * here. Cipher suites that are not available on either Android (through API level 24) or Java\n * (through JDK 9) are omitted for brevity.\n *\n * See [Android SSLEngine][sslengine] which lists the cipher suites supported by Android.\n *\n * See [JDK Providers][oracle_providers] which lists the cipher suites supported by Oracle.\n *\n * See [NativeCrypto.java][conscrypt_providers] which lists the cipher suites supported by\n * Conscrypt.\n *\n * [iana_tls_parameters]: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\n * [sslengine]: https://developer.android.com/reference/javax/net/ssl/SSLEngine.html\n * [oracle_providers]: https://docs.oracle.com/javase/10/security/oracle-providers.htm\n * [conscrypt_providers]: https://github.com/google/conscrypt/blob/master/common/src/main/java/org/conscrypt/NativeCrypto.java\n */\nclass CipherSuite private constructor(\n  /**\n   * Returns the Java name of this cipher suite. For some older cipher suites the Java name has the\n   * prefix `SSL_`, causing the Java name to be different from the instance name which is always\n   * prefixed `TLS_`. For example, `TLS_RSA_EXPORT_WITH_RC4_40_MD5.javaName()` is\n   * `\"SSL_RSA_EXPORT_WITH_RC4_40_MD5\"`.\n   */\n  @get:JvmName(\"javaName\") val javaName: String,\n) {\n  @JvmName(\"-deprecated_javaName\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"javaName\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun javaName(): String = javaName\n\n  override fun toString(): String = javaName\n\n  companion object {\n    /**\n     * Compares cipher suites names like \"TLS_RSA_WITH_NULL_MD5\" and \"SSL_RSA_WITH_NULL_MD5\",\n     * ignoring the \"TLS_\" or \"SSL_\" prefix which is not consistent across platforms. In particular\n     * some IBM JVMs use the \"SSL_\" prefix everywhere whereas Oracle JVMs mix \"TLS_\" and \"SSL_\".\n     */\n    internal val ORDER_BY_NAME =\n      object : Comparator<String> {\n        override fun compare(\n          a: String,\n          b: String,\n        ): Int {\n          var i = 4\n          val limit = minOf(a.length, b.length)\n          while (i < limit) {\n            val charA = a[i]\n            val charB = b[i]\n            if (charA != charB) return if (charA < charB) -1 else 1\n            i++\n          }\n          val lengthA = a.length\n          val lengthB = b.length\n          if (lengthA != lengthB) return if (lengthA < lengthB) -1 else 1\n          return 0\n        }\n      }\n\n    /**\n     * Holds interned instances. This needs to be above the init() calls below so that it's\n     * initialized by the time those parts of `<clinit>()` run. Guarded by CipherSuite.class.\n     */\n    private val INSTANCES = mutableMapOf<String, CipherSuite>()\n\n    // Last updated 2016-07-03 using cipher suites from Android 24 and Java 9.\n\n    // @JvmField val TLS_NULL_WITH_NULL_NULL = init(\"TLS_NULL_WITH_NULL_NULL\", 0x0000)\n    @JvmField val TLS_RSA_WITH_NULL_MD5 = init(\"SSL_RSA_WITH_NULL_MD5\", 0x0001)\n\n    @JvmField val TLS_RSA_WITH_NULL_SHA = init(\"SSL_RSA_WITH_NULL_SHA\", 0x0002)\n\n    @JvmField val TLS_RSA_EXPORT_WITH_RC4_40_MD5 = init(\"SSL_RSA_EXPORT_WITH_RC4_40_MD5\", 0x0003)\n\n    @JvmField val TLS_RSA_WITH_RC4_128_MD5 = init(\"SSL_RSA_WITH_RC4_128_MD5\", 0x0004)\n\n    @JvmField val TLS_RSA_WITH_RC4_128_SHA = init(\"SSL_RSA_WITH_RC4_128_SHA\", 0x0005)\n\n    // @JvmField val TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = init(\"SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5\", 0x0006)\n    // @JvmField val TLS_RSA_WITH_IDEA_CBC_SHA = init(\"TLS_RSA_WITH_IDEA_CBC_SHA\", 0x0007)\n    @JvmField val TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = init(\"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA\", 0x0008)\n\n    @JvmField val TLS_RSA_WITH_DES_CBC_SHA = init(\"SSL_RSA_WITH_DES_CBC_SHA\", 0x0009)\n\n    @JvmField val TLS_RSA_WITH_3DES_EDE_CBC_SHA = init(\"SSL_RSA_WITH_3DES_EDE_CBC_SHA\", 0x000a)\n\n    // @JvmField val TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = init(\"SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA\", 0x000b)\n    // @JvmField val TLS_DH_DSS_WITH_DES_CBC_SHA = init(\"TLS_DH_DSS_WITH_DES_CBC_SHA\", 0x000c)\n    // @JvmField val TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = init(\"TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA\", 0x000d)\n    // @JvmField val TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = init(\"SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA\", 0x000e)\n    // @JvmField val TLS_DH_RSA_WITH_DES_CBC_SHA = init(\"TLS_DH_RSA_WITH_DES_CBC_SHA\", 0x000f)\n    // @JvmField val TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = init(\"TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA\", 0x0010)\n    @JvmField val TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = init(\"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA\", 0x0011)\n\n    @JvmField val TLS_DHE_DSS_WITH_DES_CBC_SHA = init(\"SSL_DHE_DSS_WITH_DES_CBC_SHA\", 0x0012)\n\n    @JvmField val TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = init(\"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA\", 0x0013)\n\n    @JvmField val TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = init(\"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA\", 0x0014)\n\n    @JvmField val TLS_DHE_RSA_WITH_DES_CBC_SHA = init(\"SSL_DHE_RSA_WITH_DES_CBC_SHA\", 0x0015)\n\n    @JvmField val TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = init(\"SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA\", 0x0016)\n\n    @JvmField val TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = init(\"SSL_DH_anon_EXPORT_WITH_RC4_40_MD5\", 0x0017)\n\n    @JvmField val TLS_DH_anon_WITH_RC4_128_MD5 = init(\"SSL_DH_anon_WITH_RC4_128_MD5\", 0x0018)\n\n    @JvmField val TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = init(\"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA\", 0x0019)\n\n    @JvmField val TLS_DH_anon_WITH_DES_CBC_SHA = init(\"SSL_DH_anon_WITH_DES_CBC_SHA\", 0x001a)\n\n    @JvmField val TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = init(\"SSL_DH_anon_WITH_3DES_EDE_CBC_SHA\", 0x001b)\n\n    @JvmField val TLS_KRB5_WITH_DES_CBC_SHA = init(\"TLS_KRB5_WITH_DES_CBC_SHA\", 0x001e)\n\n    @JvmField val TLS_KRB5_WITH_3DES_EDE_CBC_SHA = init(\"TLS_KRB5_WITH_3DES_EDE_CBC_SHA\", 0x001f)\n\n    @JvmField val TLS_KRB5_WITH_RC4_128_SHA = init(\"TLS_KRB5_WITH_RC4_128_SHA\", 0x0020)\n\n    // @JvmField val TLS_KRB5_WITH_IDEA_CBC_SHA = init(\"TLS_KRB5_WITH_IDEA_CBC_SHA\", 0x0021)\n    @JvmField val TLS_KRB5_WITH_DES_CBC_MD5 = init(\"TLS_KRB5_WITH_DES_CBC_MD5\", 0x0022)\n\n    @JvmField val TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = init(\"TLS_KRB5_WITH_3DES_EDE_CBC_MD5\", 0x0023)\n\n    @JvmField val TLS_KRB5_WITH_RC4_128_MD5 = init(\"TLS_KRB5_WITH_RC4_128_MD5\", 0x0024)\n\n    // @JvmField val TLS_KRB5_WITH_IDEA_CBC_MD5 = init(\"TLS_KRB5_WITH_IDEA_CBC_MD5\", 0x0025)\n    @JvmField val TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = init(\"TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA\", 0x0026)\n\n    // @JvmField val TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = init(\"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA\", 0x0027)\n    @JvmField val TLS_KRB5_EXPORT_WITH_RC4_40_SHA = init(\"TLS_KRB5_EXPORT_WITH_RC4_40_SHA\", 0x0028)\n\n    @JvmField val TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = init(\"TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5\", 0x0029)\n\n    // @JvmField val TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = init(\"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5\", 0x002a)\n    @JvmField val TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = init(\"TLS_KRB5_EXPORT_WITH_RC4_40_MD5\", 0x002b)\n\n    // @JvmField val TLS_PSK_WITH_NULL_SHA = init(\"TLS_PSK_WITH_NULL_SHA\", 0x002c)\n    // @JvmField val TLS_DHE_PSK_WITH_NULL_SHA = init(\"TLS_DHE_PSK_WITH_NULL_SHA\", 0x002d)\n    // @JvmField val TLS_RSA_PSK_WITH_NULL_SHA = init(\"TLS_RSA_PSK_WITH_NULL_SHA\", 0x002e)\n    @JvmField val TLS_RSA_WITH_AES_128_CBC_SHA = init(\"TLS_RSA_WITH_AES_128_CBC_SHA\", 0x002f)\n\n    // @JvmField val TLS_DH_DSS_WITH_AES_128_CBC_SHA = init(\"TLS_DH_DSS_WITH_AES_128_CBC_SHA\", 0x0030)\n    // @JvmField val TLS_DH_RSA_WITH_AES_128_CBC_SHA = init(\"TLS_DH_RSA_WITH_AES_128_CBC_SHA\", 0x0031)\n    @JvmField val TLS_DHE_DSS_WITH_AES_128_CBC_SHA = init(\"TLS_DHE_DSS_WITH_AES_128_CBC_SHA\", 0x0032)\n\n    @JvmField val TLS_DHE_RSA_WITH_AES_128_CBC_SHA = init(\"TLS_DHE_RSA_WITH_AES_128_CBC_SHA\", 0x0033)\n\n    @JvmField val TLS_DH_anon_WITH_AES_128_CBC_SHA = init(\"TLS_DH_anon_WITH_AES_128_CBC_SHA\", 0x0034)\n\n    @JvmField val TLS_RSA_WITH_AES_256_CBC_SHA = init(\"TLS_RSA_WITH_AES_256_CBC_SHA\", 0x0035)\n\n    // @JvmField val TLS_DH_DSS_WITH_AES_256_CBC_SHA = init(\"TLS_DH_DSS_WITH_AES_256_CBC_SHA\", 0x0036)\n    // @JvmField val TLS_DH_RSA_WITH_AES_256_CBC_SHA = init(\"TLS_DH_RSA_WITH_AES_256_CBC_SHA\", 0x0037)\n    @JvmField val TLS_DHE_DSS_WITH_AES_256_CBC_SHA = init(\"TLS_DHE_DSS_WITH_AES_256_CBC_SHA\", 0x0038)\n\n    @JvmField val TLS_DHE_RSA_WITH_AES_256_CBC_SHA = init(\"TLS_DHE_RSA_WITH_AES_256_CBC_SHA\", 0x0039)\n\n    @JvmField val TLS_DH_anon_WITH_AES_256_CBC_SHA = init(\"TLS_DH_anon_WITH_AES_256_CBC_SHA\", 0x003a)\n\n    @JvmField val TLS_RSA_WITH_NULL_SHA256 = init(\"TLS_RSA_WITH_NULL_SHA256\", 0x003b)\n\n    @JvmField val TLS_RSA_WITH_AES_128_CBC_SHA256 = init(\"TLS_RSA_WITH_AES_128_CBC_SHA256\", 0x003c)\n\n    @JvmField val TLS_RSA_WITH_AES_256_CBC_SHA256 = init(\"TLS_RSA_WITH_AES_256_CBC_SHA256\", 0x003d)\n\n    // @JvmField val TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = init(\"TLS_DH_DSS_WITH_AES_128_CBC_SHA256\", 0x003e)\n    // @JvmField val TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = init(\"TLS_DH_RSA_WITH_AES_128_CBC_SHA256\", 0x003f)\n    @JvmField val TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = init(\"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256\", 0x0040)\n\n    @JvmField val TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = init(\"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA\", 0x0041)\n\n    // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = init(\"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA\", 0x0042)\n    // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = init(\"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA\", 0x0043)\n    @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = init(\"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA\", 0x0044)\n\n    @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = init(\"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA\", 0x0045)\n\n    // @JvmField val TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = init(\"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA\", 0x0046)\n    @JvmField val TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = init(\"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256\", 0x0067)\n\n    // @JvmField val TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = init(\"TLS_DH_DSS_WITH_AES_256_CBC_SHA256\", 0x0068)\n    // @JvmField val TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = init(\"TLS_DH_RSA_WITH_AES_256_CBC_SHA256\", 0x0069)\n    @JvmField val TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = init(\"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256\", 0x006a)\n\n    @JvmField val TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = init(\"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256\", 0x006b)\n\n    @JvmField val TLS_DH_anon_WITH_AES_128_CBC_SHA256 = init(\"TLS_DH_anon_WITH_AES_128_CBC_SHA256\", 0x006c)\n\n    @JvmField val TLS_DH_anon_WITH_AES_256_CBC_SHA256 = init(\"TLS_DH_anon_WITH_AES_256_CBC_SHA256\", 0x006d)\n\n    @JvmField val TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = init(\"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA\", 0x0084)\n\n    // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = init(\"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA\", 0x0085)\n    // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = init(\"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA\", 0x0086)\n    @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = init(\"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA\", 0x0087)\n\n    @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = init(\"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA\", 0x0088)\n\n    // @JvmField val TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = init(\"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA\", 0x0089)\n    @JvmField val TLS_PSK_WITH_RC4_128_SHA = init(\"TLS_PSK_WITH_RC4_128_SHA\", 0x008a)\n\n    @JvmField val TLS_PSK_WITH_3DES_EDE_CBC_SHA = init(\"TLS_PSK_WITH_3DES_EDE_CBC_SHA\", 0x008b)\n\n    @JvmField val TLS_PSK_WITH_AES_128_CBC_SHA = init(\"TLS_PSK_WITH_AES_128_CBC_SHA\", 0x008c)\n\n    @JvmField val TLS_PSK_WITH_AES_256_CBC_SHA = init(\"TLS_PSK_WITH_AES_256_CBC_SHA\", 0x008d)\n\n    // @JvmField val TLS_DHE_PSK_WITH_RC4_128_SHA = init(\"TLS_DHE_PSK_WITH_RC4_128_SHA\", 0x008e)\n    // @JvmField val TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = init(\"TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA\", 0x008f)\n    // @JvmField val TLS_DHE_PSK_WITH_AES_128_CBC_SHA = init(\"TLS_DHE_PSK_WITH_AES_128_CBC_SHA\", 0x0090)\n    // @JvmField val TLS_DHE_PSK_WITH_AES_256_CBC_SHA = init(\"TLS_DHE_PSK_WITH_AES_256_CBC_SHA\", 0x0091)\n    // @JvmField val TLS_RSA_PSK_WITH_RC4_128_SHA = init(\"TLS_RSA_PSK_WITH_RC4_128_SHA\", 0x0092)\n    // @JvmField val TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = init(\"TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA\", 0x0093)\n    // @JvmField val TLS_RSA_PSK_WITH_AES_128_CBC_SHA = init(\"TLS_RSA_PSK_WITH_AES_128_CBC_SHA\", 0x0094)\n    // @JvmField val TLS_RSA_PSK_WITH_AES_256_CBC_SHA = init(\"TLS_RSA_PSK_WITH_AES_256_CBC_SHA\", 0x0095)\n    @JvmField val TLS_RSA_WITH_SEED_CBC_SHA = init(\"TLS_RSA_WITH_SEED_CBC_SHA\", 0x0096)\n\n    // @JvmField val TLS_DH_DSS_WITH_SEED_CBC_SHA = init(\"TLS_DH_DSS_WITH_SEED_CBC_SHA\", 0x0097)\n    // @JvmField val TLS_DH_RSA_WITH_SEED_CBC_SHA = init(\"TLS_DH_RSA_WITH_SEED_CBC_SHA\", 0x0098)\n    // @JvmField val TLS_DHE_DSS_WITH_SEED_CBC_SHA = init(\"TLS_DHE_DSS_WITH_SEED_CBC_SHA\", 0x0099)\n    // @JvmField val TLS_DHE_RSA_WITH_SEED_CBC_SHA = init(\"TLS_DHE_RSA_WITH_SEED_CBC_SHA\", 0x009a)\n    // @JvmField val TLS_DH_anon_WITH_SEED_CBC_SHA = init(\"TLS_DH_anon_WITH_SEED_CBC_SHA\", 0x009b)\n    @JvmField val TLS_RSA_WITH_AES_128_GCM_SHA256 = init(\"TLS_RSA_WITH_AES_128_GCM_SHA256\", 0x009c)\n\n    @JvmField val TLS_RSA_WITH_AES_256_GCM_SHA384 = init(\"TLS_RSA_WITH_AES_256_GCM_SHA384\", 0x009d)\n\n    @JvmField val TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = init(\"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\", 0x009e)\n\n    @JvmField val TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = init(\"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\", 0x009f)\n\n    // @JvmField val TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = init(\"TLS_DH_RSA_WITH_AES_128_GCM_SHA256\", 0x00a0)\n    // @JvmField val TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = init(\"TLS_DH_RSA_WITH_AES_256_GCM_SHA384\", 0x00a1)\n    @JvmField val TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = init(\"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256\", 0x00a2)\n\n    @JvmField val TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = init(\"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384\", 0x00a3)\n\n    // @JvmField val TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = init(\"TLS_DH_DSS_WITH_AES_128_GCM_SHA256\", 0x00a4)\n    // @JvmField val TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = init(\"TLS_DH_DSS_WITH_AES_256_GCM_SHA384\", 0x00a5)\n    @JvmField val TLS_DH_anon_WITH_AES_128_GCM_SHA256 = init(\"TLS_DH_anon_WITH_AES_128_GCM_SHA256\", 0x00a6)\n\n    @JvmField val TLS_DH_anon_WITH_AES_256_GCM_SHA384 = init(\"TLS_DH_anon_WITH_AES_256_GCM_SHA384\", 0x00a7)\n\n    // @JvmField val TLS_PSK_WITH_AES_128_GCM_SHA256 = init(\"TLS_PSK_WITH_AES_128_GCM_SHA256\", 0x00a8)\n    // @JvmField val TLS_PSK_WITH_AES_256_GCM_SHA384 = init(\"TLS_PSK_WITH_AES_256_GCM_SHA384\", 0x00a9)\n    // @JvmField val TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = init(\"TLS_DHE_PSK_WITH_AES_128_GCM_SHA256\", 0x00aa)\n    // @JvmField val TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = init(\"TLS_DHE_PSK_WITH_AES_256_GCM_SHA384\", 0x00ab)\n    // @JvmField val TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = init(\"TLS_RSA_PSK_WITH_AES_128_GCM_SHA256\", 0x00ac)\n    // @JvmField val TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = init(\"TLS_RSA_PSK_WITH_AES_256_GCM_SHA384\", 0x00ad)\n    // @JvmField val TLS_PSK_WITH_AES_128_CBC_SHA256 = init(\"TLS_PSK_WITH_AES_128_CBC_SHA256\", 0x00ae)\n    // @JvmField val TLS_PSK_WITH_AES_256_CBC_SHA384 = init(\"TLS_PSK_WITH_AES_256_CBC_SHA384\", 0x00af)\n    // @JvmField val TLS_PSK_WITH_NULL_SHA256 = init(\"TLS_PSK_WITH_NULL_SHA256\", 0x00b0)\n    // @JvmField val TLS_PSK_WITH_NULL_SHA384 = init(\"TLS_PSK_WITH_NULL_SHA384\", 0x00b1)\n    // @JvmField val TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = init(\"TLS_DHE_PSK_WITH_AES_128_CBC_SHA256\", 0x00b2)\n    // @JvmField val TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = init(\"TLS_DHE_PSK_WITH_AES_256_CBC_SHA384\", 0x00b3)\n    // @JvmField val TLS_DHE_PSK_WITH_NULL_SHA256 = init(\"TLS_DHE_PSK_WITH_NULL_SHA256\", 0x00b4)\n    // @JvmField val TLS_DHE_PSK_WITH_NULL_SHA384 = init(\"TLS_DHE_PSK_WITH_NULL_SHA384\", 0x00b5)\n    // @JvmField val TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = init(\"TLS_RSA_PSK_WITH_AES_128_CBC_SHA256\", 0x00b6)\n    // @JvmField val TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = init(\"TLS_RSA_PSK_WITH_AES_256_CBC_SHA384\", 0x00b7)\n    // @JvmField val TLS_RSA_PSK_WITH_NULL_SHA256 = init(\"TLS_RSA_PSK_WITH_NULL_SHA256\", 0x00b8)\n    // @JvmField val TLS_RSA_PSK_WITH_NULL_SHA384 = init(\"TLS_RSA_PSK_WITH_NULL_SHA384\", 0x00b9)\n    // @JvmField val TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256\", 0x00ba)\n    // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256\", 0x00bb)\n    // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256\", 0x00bc)\n    // @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256\", 0x00bd)\n    // @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256\", 0x00be)\n    // @JvmField val TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256\", 0x00bf)\n    // @JvmField val TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = init(\"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256\", 0x00c0)\n    // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = init(\"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256\", 0x00c1)\n    // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = init(\"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256\", 0x00c2)\n    // @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = init(\"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256\", 0x00c3)\n    // @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = init(\"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256\", 0x00c4)\n    // @JvmField val TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = init(\"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256\", 0x00c5)\n    @JvmField val TLS_EMPTY_RENEGOTIATION_INFO_SCSV = init(\"TLS_EMPTY_RENEGOTIATION_INFO_SCSV\", 0x00ff)\n\n    @JvmField val TLS_FALLBACK_SCSV = init(\"TLS_FALLBACK_SCSV\", 0x5600)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_NULL_SHA = init(\"TLS_ECDH_ECDSA_WITH_NULL_SHA\", 0xc001)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_RC4_128_SHA = init(\"TLS_ECDH_ECDSA_WITH_RC4_128_SHA\", 0xc002)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = init(\"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA\", 0xc003)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = init(\"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA\", 0xc004)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = init(\"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA\", 0xc005)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_NULL_SHA = init(\"TLS_ECDHE_ECDSA_WITH_NULL_SHA\", 0xc006)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = init(\"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA\", 0xc007)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = init(\"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA\", 0xc008)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = init(\"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA\", 0xc009)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = init(\"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA\", 0xc00a)\n\n    @JvmField val TLS_ECDH_RSA_WITH_NULL_SHA = init(\"TLS_ECDH_RSA_WITH_NULL_SHA\", 0xc00b)\n\n    @JvmField val TLS_ECDH_RSA_WITH_RC4_128_SHA = init(\"TLS_ECDH_RSA_WITH_RC4_128_SHA\", 0xc00c)\n\n    @JvmField val TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = init(\"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA\", 0xc00d)\n\n    @JvmField val TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = init(\"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA\", 0xc00e)\n\n    @JvmField val TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = init(\"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA\", 0xc00f)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_NULL_SHA = init(\"TLS_ECDHE_RSA_WITH_NULL_SHA\", 0xc010)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_RC4_128_SHA = init(\"TLS_ECDHE_RSA_WITH_RC4_128_SHA\", 0xc011)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = init(\"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA\", 0xc012)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = init(\"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\", 0xc013)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = init(\"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\", 0xc014)\n\n    @JvmField val TLS_ECDH_anon_WITH_NULL_SHA = init(\"TLS_ECDH_anon_WITH_NULL_SHA\", 0xc015)\n\n    @JvmField val TLS_ECDH_anon_WITH_RC4_128_SHA = init(\"TLS_ECDH_anon_WITH_RC4_128_SHA\", 0xc016)\n\n    @JvmField val TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = init(\"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA\", 0xc017)\n\n    @JvmField val TLS_ECDH_anon_WITH_AES_128_CBC_SHA = init(\"TLS_ECDH_anon_WITH_AES_128_CBC_SHA\", 0xc018)\n\n    @JvmField val TLS_ECDH_anon_WITH_AES_256_CBC_SHA = init(\"TLS_ECDH_anon_WITH_AES_256_CBC_SHA\", 0xc019)\n\n    // @JvmField val TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = init(\"TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA\", 0xc01a)\n    // @JvmField val TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = init(\"TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA\", 0xc01b)\n    // @JvmField val TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = init(\"TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA\", 0xc01c)\n    // @JvmField val TLS_SRP_SHA_WITH_AES_128_CBC_SHA = init(\"TLS_SRP_SHA_WITH_AES_128_CBC_SHA\", 0xc01d)\n    // @JvmField val TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = init(\"TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA\", 0xc01e)\n    // @JvmField val TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = init(\"TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA\", 0xc01f)\n    // @JvmField val TLS_SRP_SHA_WITH_AES_256_CBC_SHA = init(\"TLS_SRP_SHA_WITH_AES_256_CBC_SHA\", 0xc020)\n    // @JvmField val TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = init(\"TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA\", 0xc021)\n    // @JvmField val TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = init(\"TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA\", 0xc022)\n    @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = init(\"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256\", 0xc023)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = init(\"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384\", 0xc024)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = init(\"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256\", 0xc025)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = init(\"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384\", 0xc026)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = init(\"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256\", 0xc027)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = init(\"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384\", 0xc028)\n\n    @JvmField val TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = init(\"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256\", 0xc029)\n\n    @JvmField val TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = init(\"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384\", 0xc02a)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = init(\"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\", 0xc02b)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = init(\"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\", 0xc02c)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = init(\"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256\", 0xc02d)\n\n    @JvmField val TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = init(\"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384\", 0xc02e)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = init(\"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\", 0xc02f)\n\n    @JvmField val TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = init(\"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\", 0xc030)\n\n    @JvmField val TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = init(\"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256\", 0xc031)\n\n    @JvmField val TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = init(\"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384\", 0xc032)\n\n    // @JvmField val TLS_ECDHE_PSK_WITH_RC4_128_SHA = init(\"TLS_ECDHE_PSK_WITH_RC4_128_SHA\", 0xc033)\n    // @JvmField val TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = init(\"TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA\", 0xc034)\n    @JvmField val TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = init(\"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA\", 0xc035)\n\n    @JvmField val TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = init(\"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA\", 0xc036)\n\n    // @JvmField val TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = init(\"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256\", 0xc037)\n    // @JvmField val TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = init(\"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384\", 0xc038)\n    // @JvmField val TLS_ECDHE_PSK_WITH_NULL_SHA = init(\"TLS_ECDHE_PSK_WITH_NULL_SHA\", 0xc039)\n    // @JvmField val TLS_ECDHE_PSK_WITH_NULL_SHA256 = init(\"TLS_ECDHE_PSK_WITH_NULL_SHA256\", 0xc03a)\n    // @JvmField val TLS_ECDHE_PSK_WITH_NULL_SHA384 = init(\"TLS_ECDHE_PSK_WITH_NULL_SHA384\", 0xc03b)\n    // @JvmField val TLS_RSA_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_RSA_WITH_ARIA_128_CBC_SHA256\", 0xc03c)\n    // @JvmField val TLS_RSA_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_RSA_WITH_ARIA_256_CBC_SHA384\", 0xc03d)\n    // @JvmField val TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256\", 0xc03e)\n    // @JvmField val TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384\", 0xc03f)\n    // @JvmField val TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256\", 0xc040)\n    // @JvmField val TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384\", 0xc041)\n    // @JvmField val TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256\", 0xc042)\n    // @JvmField val TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384\", 0xc043)\n    // @JvmField val TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256\", 0xc044)\n    // @JvmField val TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384\", 0xc045)\n    // @JvmField val TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_DH_anon_WITH_ARIA_128_CBC_SHA256\", 0xc046)\n    // @JvmField val TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_DH_anon_WITH_ARIA_256_CBC_SHA384\", 0xc047)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256\", 0xc048)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384\", 0xc049)\n    // @JvmField val TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256\", 0xc04a)\n    // @JvmField val TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384\", 0xc04b)\n    // @JvmField val TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256\", 0xc04c)\n    // @JvmField val TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384\", 0xc04d)\n    // @JvmField val TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256\", 0xc04e)\n    // @JvmField val TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384\", 0xc04f)\n    // @JvmField val TLS_RSA_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_RSA_WITH_ARIA_128_GCM_SHA256\", 0xc050)\n    // @JvmField val TLS_RSA_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_RSA_WITH_ARIA_256_GCM_SHA384\", 0xc051)\n    // @JvmField val TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256\", 0xc052)\n    // @JvmField val TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384\", 0xc053)\n    // @JvmField val TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256\", 0xc054)\n    // @JvmField val TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384\", 0xc055)\n    // @JvmField val TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256\", 0xc056)\n    // @JvmField val TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384\", 0xc057)\n    // @JvmField val TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256\", 0xc058)\n    // @JvmField val TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384\", 0xc059)\n    // @JvmField val TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_DH_anon_WITH_ARIA_128_GCM_SHA256\", 0xc05a)\n    // @JvmField val TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_DH_anon_WITH_ARIA_256_GCM_SHA384\", 0xc05b)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256\", 0xc05c)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384\", 0xc05d)\n    // @JvmField val TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256\", 0xc05e)\n    // @JvmField val TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384\", 0xc05f)\n    // @JvmField val TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256\", 0xc060)\n    // @JvmField val TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384\", 0xc061)\n    // @JvmField val TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256\", 0xc062)\n    // @JvmField val TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384\", 0xc063)\n    // @JvmField val TLS_PSK_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_PSK_WITH_ARIA_128_CBC_SHA256\", 0xc064)\n    // @JvmField val TLS_PSK_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_PSK_WITH_ARIA_256_CBC_SHA384\", 0xc065)\n    // @JvmField val TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256\", 0xc066)\n    // @JvmField val TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384\", 0xc067)\n    // @JvmField val TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256\", 0xc068)\n    // @JvmField val TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384\", 0xc069)\n    // @JvmField val TLS_PSK_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_PSK_WITH_ARIA_128_GCM_SHA256\", 0xc06a)\n    // @JvmField val TLS_PSK_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_PSK_WITH_ARIA_256_GCM_SHA384\", 0xc06b)\n    // @JvmField val TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256\", 0xc06c)\n    // @JvmField val TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384\", 0xc06d)\n    // @JvmField val TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 = init(\"TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256\", 0xc06e)\n    // @JvmField val TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 = init(\"TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384\", 0xc06f)\n    // @JvmField val TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 = init(\"TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256\", 0xc070)\n    // @JvmField val TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 = init(\"TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384\", 0xc071)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256\", 0xc072)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = init(\"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384\", 0xc073)\n    // @JvmField val TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256\", 0xc074)\n    // @JvmField val TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = init(\"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384\", 0xc075)\n    // @JvmField val TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256\", 0xc076)\n    // @JvmField val TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = init(\"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384\", 0xc077)\n    // @JvmField val TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256\", 0xc078)\n    // @JvmField val TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = init(\"TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384\", 0xc079)\n    // @JvmField val TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256\", 0xc07a)\n    // @JvmField val TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384\", 0xc07b)\n    // @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256\", 0xc07c)\n    // @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384\", 0xc07d)\n    // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256\", 0xc07e)\n    // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384\", 0xc07f)\n    // @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256\", 0xc080)\n    // @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384\", 0xc081)\n    // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256\", 0xc082)\n    // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384\", 0xc083)\n    // @JvmField val TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256\", 0xc084)\n    // @JvmField val TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384\", 0xc085)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256\", 0xc086)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384\", 0xc087)\n    // @JvmField val TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256\", 0xc088)\n    // @JvmField val TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384\", 0xc089)\n    // @JvmField val TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256\", 0xc08a)\n    // @JvmField val TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384\", 0xc08b)\n    // @JvmField val TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256\", 0xc08c)\n    // @JvmField val TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384\", 0xc08d)\n    // @JvmField val TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256\", 0xc08e)\n    // @JvmField val TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384\", 0xc08f)\n    // @JvmField val TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256\", 0xc090)\n    // @JvmField val TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384\", 0xc091)\n    // @JvmField val TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = init(\"TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256\", 0xc092)\n    // @JvmField val TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = init(\"TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384\", 0xc093)\n    // @JvmField val TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256\", 0xc094)\n    // @JvmField val TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = init(\"TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384\", 0xc095)\n    // @JvmField val TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256\", 0xc096)\n    // @JvmField val TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = init(\"TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384\", 0xc097)\n    // @JvmField val TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256\", 0xc098)\n    // @JvmField val TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = init(\"TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384\", 0xc099)\n    // @JvmField val TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = init(\"TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256\", 0xc09a)\n    // @JvmField val TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = init(\"TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384\", 0xc09b)\n    // @JvmField val TLS_RSA_WITH_AES_128_CCM = init(\"TLS_RSA_WITH_AES_128_CCM\", 0xc09c)\n    // @JvmField val TLS_RSA_WITH_AES_256_CCM = init(\"TLS_RSA_WITH_AES_256_CCM\", 0xc09d)\n    // @JvmField val TLS_DHE_RSA_WITH_AES_128_CCM = init(\"TLS_DHE_RSA_WITH_AES_128_CCM\", 0xc09e)\n    // @JvmField val TLS_DHE_RSA_WITH_AES_256_CCM = init(\"TLS_DHE_RSA_WITH_AES_256_CCM\", 0xc09f)\n    // @JvmField val TLS_RSA_WITH_AES_128_CCM_8 = init(\"TLS_RSA_WITH_AES_128_CCM_8\", 0xc0a0)\n    // @JvmField val TLS_RSA_WITH_AES_256_CCM_8 = init(\"TLS_RSA_WITH_AES_256_CCM_8\", 0xc0a1)\n    // @JvmField val TLS_DHE_RSA_WITH_AES_128_CCM_8 = init(\"TLS_DHE_RSA_WITH_AES_128_CCM_8\", 0xc0a2)\n    // @JvmField val TLS_DHE_RSA_WITH_AES_256_CCM_8 = init(\"TLS_DHE_RSA_WITH_AES_256_CCM_8\", 0xc0a3)\n    // @JvmField val TLS_PSK_WITH_AES_128_CCM = init(\"TLS_PSK_WITH_AES_128_CCM\", 0xc0a4)\n    // @JvmField val TLS_PSK_WITH_AES_256_CCM = init(\"TLS_PSK_WITH_AES_256_CCM\", 0xc0a5)\n    // @JvmField val TLS_DHE_PSK_WITH_AES_128_CCM = init(\"TLS_DHE_PSK_WITH_AES_128_CCM\", 0xc0a6)\n    // @JvmField val TLS_DHE_PSK_WITH_AES_256_CCM = init(\"TLS_DHE_PSK_WITH_AES_256_CCM\", 0xc0a7)\n    // @JvmField val TLS_PSK_WITH_AES_128_CCM_8 = init(\"TLS_PSK_WITH_AES_128_CCM_8\", 0xc0a8)\n    // @JvmField val TLS_PSK_WITH_AES_256_CCM_8 = init(\"TLS_PSK_WITH_AES_256_CCM_8\", 0xc0a9)\n    // @JvmField val TLS_PSK_DHE_WITH_AES_128_CCM_8 = init(\"TLS_PSK_DHE_WITH_AES_128_CCM_8\", 0xc0aa)\n    // @JvmField val TLS_PSK_DHE_WITH_AES_256_CCM_8 = init(\"TLS_PSK_DHE_WITH_AES_256_CCM_8\", 0xc0ab)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_CCM = init(\"TLS_ECDHE_ECDSA_WITH_AES_128_CCM\", 0xc0ac)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_CCM = init(\"TLS_ECDHE_ECDSA_WITH_AES_256_CCM\", 0xc0ad)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = init(\"TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8\", 0xc0ae)\n    // @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = init(\"TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8\", 0xc0af)\n    @JvmField val TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = init(\"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\", 0xcca8)\n\n    @JvmField val TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = init(\"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\", 0xcca9)\n\n    @JvmField val TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = init(\"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256\", 0xccaa)\n\n    // @JvmField val TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 = init(\"TLS_PSK_WITH_CHACHA20_POLY1305_SHA256\", 0xccab)\n    @JvmField val TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = init(\"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256\", 0xccac)\n    // @JvmField val TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = init(\"TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256\", 0xccad)\n    // @JvmField val TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = init(\"TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256\", 0xccae)\n\n    // TLS 1.3 https://tools.ietf.org/html/rfc8446\n    @JvmField val TLS_AES_128_GCM_SHA256 = init(\"TLS_AES_128_GCM_SHA256\", 0x1301)\n\n    @JvmField val TLS_AES_256_GCM_SHA384 = init(\"TLS_AES_256_GCM_SHA384\", 0x1302)\n\n    @JvmField val TLS_CHACHA20_POLY1305_SHA256 = init(\"TLS_CHACHA20_POLY1305_SHA256\", 0x1303)\n\n    @JvmField val TLS_AES_128_CCM_SHA256 = init(\"TLS_AES_128_CCM_SHA256\", 0x1304)\n\n    @JvmField val TLS_AES_128_CCM_8_SHA256 = init(\"TLS_AES_128_CCM_8_SHA256\", 0x1305)\n\n    /**\n     * @param javaName the name used by Java APIs for this cipher suite. Different than the IANA\n     *     name for older cipher suites because the prefix is `SSL_` instead of `TLS_`.\n     */\n    @JvmStatic\n    @Synchronized fun forJavaName(javaName: String): CipherSuite {\n      var result: CipherSuite? = INSTANCES[javaName]\n      if (result == null) {\n        result = INSTANCES[secondaryName(javaName)]\n\n        if (result == null) {\n          result = CipherSuite(javaName)\n        }\n\n        // Add the new cipher suite, or a confirmed alias.\n        INSTANCES[javaName] = result\n      }\n      return result\n    }\n\n    private fun secondaryName(javaName: String): String =\n      when {\n        javaName.startsWith(\"TLS_\") -> \"SSL_\" + javaName.substring(4)\n        javaName.startsWith(\"SSL_\") -> \"TLS_\" + javaName.substring(4)\n        else -> javaName\n      }\n\n    /**\n     * @param javaName the name used by Java APIs for this cipher suite. Different than the IANA\n     *     name for older cipher suites because the prefix is `SSL_` instead of `TLS_`.\n     * @param value the integer identifier for this cipher suite. (Documentation only.)\n     */\n    private fun init(\n      javaName: String,\n      value: Int,\n    ): CipherSuite {\n      val suite = CipherSuite(javaName)\n      INSTANCES[javaName] = suite\n      return suite\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/CompressionInterceptor.kt",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okhttp3.ResponseBody.Companion.asResponseBody\nimport okhttp3.internal.http.promisesBody\nimport okio.BufferedSource\nimport okio.Source\nimport okio.buffer\n\n/**\n * Transparent Compressed response support.\n *\n * The algorithm map will be turned into a heading such as \"Accept-Encoding: br, gzip\"\n *\n * If [algorithms] is empty this interceptor has no effect. To disable compression set\n * a specific \"Accept-Encoding: identity\" or similar.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding\n */\nopen class CompressionInterceptor(\n  vararg val algorithms: DecompressionAlgorithm,\n) : Interceptor {\n  internal val acceptEncoding =\n    algorithms\n      .map {\n        it.encoding\n      }.joinToString(separator = \", \")\n\n  override fun intercept(chain: Interceptor.Chain): Response =\n    if (algorithms.isNotEmpty() && chain.request().header(\"Accept-Encoding\") == null) {\n      val request =\n        chain\n          .request()\n          .newBuilder()\n          .header(\"Accept-Encoding\", acceptEncoding)\n          .build()\n\n      val response = chain.proceed(request)\n\n      decompress(response)\n    } else {\n      chain.proceed(chain.request())\n    }\n\n  /**\n   * Returns a decompressed copy of the Response, typically via a streaming Source.\n   * If no known decompression or the response is not compressed, returns the response unmodified.\n   */\n  internal fun decompress(response: Response): Response {\n    if (!response.promisesBody()) {\n      return response\n    }\n    val body = response.body\n    val encoding = response.header(\"Content-Encoding\") ?: return response\n\n    val algorithm = lookupDecompressor(encoding) ?: return response\n\n    val decompressedSource = algorithm.decompress(body.source()).buffer()\n\n    return response\n      .newBuilder()\n      .removeHeader(\"Content-Encoding\")\n      .removeHeader(\"Content-Length\")\n      .body(decompressedSource.asResponseBody(body.contentType(), -1))\n      .build()\n  }\n\n  internal fun lookupDecompressor(encoding: String): DecompressionAlgorithm? =\n    algorithms.find {\n      it.encoding.equals(encoding, ignoreCase = true)\n    }\n\n  /**\n   * A decompression algorithm such as Gzip. Must provide the Accept-Encoding value and decompress a Source.\n   */\n  interface DecompressionAlgorithm {\n    val encoding: String\n\n    fun decompress(compressedSource: BufferedSource): Source\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Connection.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3\n\nimport java.net.Socket\n\n/**\n * The sockets and streams of an HTTP, HTTPS, or HTTPS+HTTP/2 connection. May be used for multiple\n * HTTP request/response exchanges. Connections may be direct to the origin server or via a proxy.\n *\n * Typically instances of this class are created, connected and exercised automatically by the HTTP\n * client. Applications may use this class to monitor HTTP connections as members of a\n * [connection pool][ConnectionPool].\n *\n * Do not confuse this class with the misnamed `HttpURLConnection`, which isn't so much a connection\n * as a single request/response exchange.\n *\n * ## Modern TLS\n *\n * There are trade-offs when selecting which options to include when negotiating a secure connection\n * to a remote host. Newer TLS options are quite useful:\n *\n *  * Server Name Indication (SNI) enables one IP address to negotiate secure connections for\n *    multiple domain names.\n *\n *  * Application Layer Protocol Negotiation (ALPN) enables the HTTPS port (443) to be used to\n *    negotiate HTTP/2.\n *\n * Unfortunately, older HTTPS servers refuse to connect when such options are presented. Rather than\n * avoiding these options entirely, this class allows a connection to be attempted with modern\n * options and then retried without them should the attempt fail.\n *\n * ## Connection Reuse\n *\n * Each connection can carry a varying number of streams, depending on the underlying protocol being\n * used. HTTP/1.x connections can carry either zero or one streams. HTTP/2 connections can carry any\n * number of streams, dynamically configured with `SETTINGS_MAX_CONCURRENT_STREAMS`. A connection\n * currently carrying zero streams is an idle stream. We keep it alive because reusing an existing\n * connection is typically faster than establishing a new one.\n *\n * When a single logical call requires multiple streams due to redirects or authorization\n * challenges, we prefer to use the same physical connection for all streams in the sequence. There\n * are potential performance and behavior consequences to this preference. To support this feature,\n * this class separates _allocations_ from _streams_. An allocation is created by a call, used for\n * one or more streams, and then released. An allocated connection won't be stolen by other calls\n * while a redirect or authorization challenge is being handled.\n *\n * When the maximum concurrent streams limit is reduced, some allocations will be rescinded.\n * Attempting to create new streams on these allocations will fail.\n *\n * Note that an allocation may be released before its stream is completed. This is intended to make\n * bookkeeping easier for the caller: releasing the allocation as soon as the terminal stream has\n * been found. But only complete the stream once its data stream has been exhausted.\n */\ninterface Connection {\n  /** Returns the route used by this connection. */\n  fun route(): Route\n\n  /**\n   * Returns the socket that this connection is using. Returns an\n   * [SSL socket][javax.net.ssl.SSLSocket] if this connection is HTTPS. If this is an HTTP/2\n   * connection the socket may be shared by multiple concurrent calls.\n   */\n  fun socket(): Socket\n\n  /**\n   * Returns the TLS handshake used to establish this connection, or null if the connection is not\n   * HTTPS.\n   */\n  fun handshake(): Handshake?\n\n  /**\n   * Returns the protocol negotiated by this connection, or [Protocol.HTTP_1_1] if no protocol\n   * has been negotiated. This method returns [Protocol.HTTP_1_1] even if the remote peer is using\n   * [Protocol.HTTP_1_0].\n   */\n  fun protocol(): Protocol\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/ConnectionPool.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3\n\nimport java.util.concurrent.TimeUnit\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.connection.ConnectionListener\nimport okhttp3.internal.connection.RealConnectionPool\n\n/**\n * Manages reuse of HTTP and HTTP/2 connections for reduced network latency. HTTP requests that\n * share the same [Address] may share a [Connection]. This class implements the policy\n * of which connections to keep open for future use.\n *\n * @constructor Create a new connection pool with tuning parameters appropriate for a single-user\n * application. The tuning parameters in this pool are subject to change in future OkHttp releases.\n * Currently this pool holds up to 5 idle connections which will be evicted after 5 minutes of\n * inactivity.\n */\nclass ConnectionPool internal constructor(\n  internal val delegate: RealConnectionPool,\n) {\n  internal constructor(\n    maxIdleConnections: Int = 5,\n    keepAliveDuration: Long = 5,\n    timeUnit: TimeUnit = TimeUnit.MINUTES,\n    taskRunner: TaskRunner = TaskRunner.INSTANCE,\n    connectionListener: ConnectionListener = ConnectionListener.NONE,\n  ) : this(\n    RealConnectionPool(\n      taskRunner = taskRunner,\n      maxIdleConnections = maxIdleConnections,\n      keepAliveDuration = keepAliveDuration,\n      timeUnit = timeUnit,\n      connectionListener = connectionListener,\n    ),\n  )\n\n  // Internal until we promote ConnectionListener to be a public API.\n  internal constructor(\n    maxIdleConnections: Int = 5,\n    keepAliveDuration: Long = 5,\n    timeUnit: TimeUnit = TimeUnit.MINUTES,\n    connectionListener: ConnectionListener = ConnectionListener.NONE,\n  ) : this(\n    taskRunner = TaskRunner.INSTANCE,\n    maxIdleConnections = maxIdleConnections,\n    keepAliveDuration = keepAliveDuration,\n    timeUnit = timeUnit,\n    connectionListener = connectionListener,\n  )\n\n  // Public API\n  constructor(\n    maxIdleConnections: Int,\n    keepAliveDuration: Long,\n    timeUnit: TimeUnit,\n  ) : this(\n    maxIdleConnections = maxIdleConnections,\n    keepAliveDuration = keepAliveDuration,\n    timeUnit = timeUnit,\n    taskRunner = TaskRunner.INSTANCE,\n    connectionListener = ConnectionListener.NONE,\n  )\n\n  constructor() : this(5, 5, TimeUnit.MINUTES)\n\n  /** Returns the number of idle connections in the pool. */\n  fun idleConnectionCount(): Int = delegate.idleConnectionCount()\n\n  /** Returns total number of connections in the pool. */\n  fun connectionCount(): Int = delegate.connectionCount()\n\n  internal val connectionListener: ConnectionListener\n    get() = delegate.connectionListener\n\n  /** Close and remove all idle connections in the pool. */\n  fun evictAll() {\n    delegate.evictAll()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/ConnectionSpec.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.util.Arrays\nimport java.util.Objects\nimport javax.net.ssl.SSLSocket\nimport okhttp3.ConnectionSpec.Builder\nimport okhttp3.internal.concat\nimport okhttp3.internal.effectiveCipherSuites\nimport okhttp3.internal.hasIntersection\nimport okhttp3.internal.indexOf\nimport okhttp3.internal.intersect\n\n/**\n * Specifies configuration for the socket connection that HTTP traffic travels through. For `https:`\n * URLs, this includes the TLS version and cipher suites to use when negotiating a secure\n * connection.\n *\n * The TLS versions configured in a connection spec are only be used if they are also enabled in the\n * SSL socket. For example, if an SSL socket does not have TLS 1.3 enabled, it will not be used even\n * if it is present on the connection spec. The same policy also applies to cipher suites.\n *\n * Use [Builder.allEnabledTlsVersions] and [Builder.allEnabledCipherSuites] to defer all feature\n * selection to the underlying SSL socket.\n *\n * The configuration of each spec changes with each OkHttp release. This is annoying: upgrading\n * your OkHttp library can break connectivity to certain web servers! But it’s a necessary annoyance\n * because the TLS ecosystem is dynamic and staying up to date is necessary to stay secure. See\n * [OkHttp's TLS Configuration History][tls_history] to track these changes.\n *\n * [tls_history]: https://square.github.io/okhttp/tls_configuration_history/\n */\nclass ConnectionSpec internal constructor(\n  @get:JvmName(\"isTls\") val isTls: Boolean,\n  @get:JvmName(\"supportsTlsExtensions\") val supportsTlsExtensions: Boolean,\n  internal val cipherSuitesAsString: Array<String>?,\n  private val tlsVersionsAsString: Array<String>?,\n) {\n  /**\n   * Returns the cipher suites to use for a connection. Returns null if all of the SSL socket's\n   * enabled cipher suites should be used.\n   */\n  @get:JvmName(\"cipherSuites\")\n  val cipherSuites: List<CipherSuite>?\n    get() {\n      return cipherSuitesAsString?.map { CipherSuite.forJavaName(it) }\n    }\n\n  @JvmName(\"-deprecated_cipherSuites\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"cipherSuites\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun cipherSuites(): List<CipherSuite>? = cipherSuites\n\n  /**\n   * Returns the TLS versions to use when negotiating a connection. Returns null if all of the SSL\n   * socket's enabled TLS versions should be used.\n   */\n  @get:JvmName(\"tlsVersions\")\n  val tlsVersions: List<TlsVersion>?\n    get() {\n      return tlsVersionsAsString?.map { TlsVersion.forJavaName(it) }\n    }\n\n  @JvmName(\"-deprecated_tlsVersions\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"tlsVersions\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun tlsVersions(): List<TlsVersion>? = tlsVersions\n\n  @JvmName(\"-deprecated_supportsTlsExtensions\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"supportsTlsExtensions\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun supportsTlsExtensions(): Boolean = supportsTlsExtensions\n\n  /** Applies this spec to [sslSocket]. */\n  internal fun apply(\n    sslSocket: SSLSocket,\n    isFallback: Boolean,\n  ) {\n    val specToApply = supportedSpec(sslSocket, isFallback)\n\n    if (specToApply.tlsVersions != null) {\n      sslSocket.enabledProtocols = specToApply.tlsVersionsAsString\n    }\n\n    if (specToApply.cipherSuites != null) {\n      sslSocket.enabledCipherSuites = specToApply.cipherSuitesAsString\n    }\n  }\n\n  /**\n   * Returns a copy of this that omits cipher suites and TLS versions not enabled by [sslSocket].\n   */\n  private fun supportedSpec(\n    sslSocket: SSLSocket,\n    isFallback: Boolean,\n  ): ConnectionSpec {\n    val socketEnabledCipherSuites = sslSocket.enabledCipherSuites\n    var cipherSuitesIntersection: Array<String> = effectiveCipherSuites(socketEnabledCipherSuites)\n\n    val tlsVersionsIntersection =\n      if (tlsVersionsAsString != null) {\n        sslSocket.enabledProtocols.intersect(tlsVersionsAsString, naturalOrder())\n      } else {\n        sslSocket.enabledProtocols\n      }\n\n    // In accordance with https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 the SCSV\n    // cipher is added to signal that a protocol fallback has taken place.\n    val supportedCipherSuites = sslSocket.supportedCipherSuites\n    val indexOfFallbackScsv =\n      supportedCipherSuites.indexOf(\n        \"TLS_FALLBACK_SCSV\",\n        CipherSuite.ORDER_BY_NAME,\n      )\n    if (isFallback && indexOfFallbackScsv != -1) {\n      cipherSuitesIntersection =\n        cipherSuitesIntersection.concat(\n          supportedCipherSuites[indexOfFallbackScsv],\n        )\n    }\n\n    return Builder(this)\n      .cipherSuites(*cipherSuitesIntersection)\n      .tlsVersions(*tlsVersionsIntersection)\n      .build()\n  }\n\n  /**\n   * Returns `true` if the socket, as currently configured, supports this connection spec. In\n   * order for a socket to be compatible the enabled cipher suites and protocols must intersect.\n   *\n   * For cipher suites, at least one of the [required cipher suites][cipherSuites] must match the\n   * socket's enabled cipher suites. If there are no required cipher suites the socket must have at\n   * least one cipher suite enabled.\n   *\n   * For protocols, at least one of the [required protocols][tlsVersions] must match the socket's\n   * enabled protocols.\n   */\n  fun isCompatible(socket: SSLSocket): Boolean {\n    if (!isTls) {\n      return false\n    }\n\n    if (tlsVersionsAsString != null &&\n      !tlsVersionsAsString.hasIntersection(socket.enabledProtocols, naturalOrder())\n    ) {\n      return false\n    }\n\n    if (cipherSuitesAsString != null &&\n      !cipherSuitesAsString.hasIntersection(\n        socket.enabledCipherSuites,\n        CipherSuite.ORDER_BY_NAME,\n      )\n    ) {\n      return false\n    }\n\n    return true\n  }\n\n  override fun equals(other: Any?): Boolean {\n    if (other !is ConnectionSpec) return false\n    if (other === this) return true\n\n    if (this.isTls != other.isTls) return false\n\n    if (isTls) {\n      if (!Arrays.equals(this.cipherSuitesAsString, other.cipherSuitesAsString)) return false\n      if (!Arrays.equals(this.tlsVersionsAsString, other.tlsVersionsAsString)) return false\n      if (this.supportsTlsExtensions != other.supportsTlsExtensions) return false\n    }\n\n    return true\n  }\n\n  override fun hashCode(): Int {\n    var result = 17\n    if (isTls) {\n      result = 31 * result + (cipherSuitesAsString?.contentHashCode() ?: 0)\n      result = 31 * result + (tlsVersionsAsString?.contentHashCode() ?: 0)\n      result = 31 * result + if (supportsTlsExtensions) 0 else 1\n    }\n    return result\n  }\n\n  override fun toString(): String {\n    if (!isTls) return \"ConnectionSpec()\"\n\n    return (\n      \"ConnectionSpec(\" +\n        \"cipherSuites=${Objects.toString(cipherSuites, \"[all enabled]\")}, \" +\n        \"tlsVersions=${Objects.toString(tlsVersions, \"[all enabled]\")}, \" +\n        \"supportsTlsExtensions=$supportsTlsExtensions)\"\n    )\n  }\n\n  class Builder {\n    internal var tls: Boolean = false\n    internal var cipherSuites: Array<String>? = null\n    internal var tlsVersions: Array<String>? = null\n    internal var supportsTlsExtensions: Boolean = false\n\n    internal constructor(tls: Boolean) {\n      this.tls = tls\n    }\n\n    constructor(connectionSpec: ConnectionSpec) {\n      this.tls = connectionSpec.isTls\n      this.cipherSuites = connectionSpec.cipherSuitesAsString\n      this.tlsVersions = connectionSpec.tlsVersionsAsString\n      this.supportsTlsExtensions = connectionSpec.supportsTlsExtensions\n    }\n\n    fun allEnabledCipherSuites() =\n      apply {\n        require(tls) { \"no cipher suites for cleartext connections\" }\n        this.cipherSuites = null\n      }\n\n    fun cipherSuites(vararg cipherSuites: CipherSuite): Builder =\n      apply {\n        require(tls) { \"no cipher suites for cleartext connections\" }\n        val strings = cipherSuites.map { it.javaName }.toTypedArray()\n        return cipherSuites(*strings)\n      }\n\n    fun cipherSuites(vararg cipherSuites: String) =\n      apply {\n        require(tls) { \"no cipher suites for cleartext connections\" }\n        require(cipherSuites.isNotEmpty()) { \"At least one cipher suite is required\" }\n\n        @Suppress(\"UNCHECKED_CAST\")\n        this.cipherSuites = cipherSuites.copyOf() as Array<String> // Defensive copy.\n      }\n\n    fun allEnabledTlsVersions() =\n      apply {\n        require(tls) { \"no TLS versions for cleartext connections\" }\n        this.tlsVersions = null\n      }\n\n    fun tlsVersions(vararg tlsVersions: TlsVersion): Builder =\n      apply {\n        require(tls) { \"no TLS versions for cleartext connections\" }\n\n        val strings = tlsVersions.map { it.javaName }.toTypedArray()\n        return tlsVersions(*strings)\n      }\n\n    fun tlsVersions(vararg tlsVersions: String) =\n      apply {\n        require(tls) { \"no TLS versions for cleartext connections\" }\n        require(tlsVersions.isNotEmpty()) { \"At least one TLS version is required\" }\n\n        @Suppress(\"UNCHECKED_CAST\")\n        this.tlsVersions = tlsVersions.copyOf() as Array<String> // Defensive copy.\n      }\n\n    @Deprecated(\n      \"since OkHttp 3.13 all TLS-connections are expected to support TLS extensions.\\n\" +\n        \"In a future release setting this to true will be unnecessary and setting it to false\\n\" +\n        \"will have no effect.\",\n    )\n    fun supportsTlsExtensions(supportsTlsExtensions: Boolean) =\n      apply {\n        require(tls) { \"no TLS extensions for cleartext connections\" }\n        this.supportsTlsExtensions = supportsTlsExtensions\n      }\n\n    fun build(): ConnectionSpec =\n      ConnectionSpec(\n        tls,\n        supportsTlsExtensions,\n        cipherSuites,\n        tlsVersions,\n      )\n  }\n\n  @Suppress(\"DEPRECATION\")\n  companion object {\n    // Most secure but generally supported list.\n    private val RESTRICTED_CIPHER_SUITES =\n      listOf(\n        // TLSv1.3.\n        CipherSuite.TLS_AES_128_GCM_SHA256,\n        CipherSuite.TLS_AES_256_GCM_SHA384,\n        CipherSuite.TLS_CHACHA20_POLY1305_SHA256,\n        // TLSv1.0, TLSv1.1, TLSv1.2.\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\n        CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,\n        CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,\n      )\n\n    // This is nearly equal to the cipher suites supported in Chrome 72, current as of 2019-02-24.\n    // See https://tinyurl.com/okhttp-cipher-suites for availability.\n    private val APPROVED_CIPHER_SUITES =\n      listOf(\n        // TLSv1.3.\n        CipherSuite.TLS_AES_128_GCM_SHA256,\n        CipherSuite.TLS_AES_256_GCM_SHA384,\n        CipherSuite.TLS_CHACHA20_POLY1305_SHA256,\n        // TLSv1.0, TLSv1.1, TLSv1.2.\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\n        CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,\n        CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,\n        // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll\n        // continue to include them until better suites are commonly available.\n        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,\n        CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,\n        CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,\n        CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,\n        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,\n        CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,\n        CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,\n      )\n\n    /** A secure TLS connection that requires a recent client platform and a recent server. */\n    @JvmField\n    val RESTRICTED_TLS =\n      Builder(true)\n        .cipherSuites(*RESTRICTED_CIPHER_SUITES.toTypedArray())\n        .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(true)\n        .build()\n\n    /**\n     * A modern TLS configuration that works on most client platforms and can connect to most servers.\n     * This is OkHttp's default configuration.\n     */\n    @JvmField\n    val MODERN_TLS =\n      Builder(true)\n        .cipherSuites(*APPROVED_CIPHER_SUITES.toTypedArray())\n        .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(true)\n        .build()\n\n    /**\n     * A backwards-compatible fallback configuration that works on obsolete client platforms and can\n     * connect to obsolete servers. When possible, prefer to upgrade your client platform or server\n     * rather than using this configuration.\n     */\n    @JvmField\n    val COMPATIBLE_TLS =\n      Builder(true)\n        .cipherSuites(*APPROVED_CIPHER_SUITES.toTypedArray())\n        .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)\n        .supportsTlsExtensions(true)\n        .build()\n\n    /** Unencrypted, unauthenticated connections for `http:` URLs. */\n    @JvmField\n    val CLEARTEXT = Builder(false).build()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Cookie.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.util.Calendar\nimport java.util.Date\nimport java.util.GregorianCalendar\nimport java.util.Locale\nimport java.util.regex.Pattern\nimport okhttp3.internal.UTC\nimport okhttp3.internal.canParseAsIpAddress\nimport okhttp3.internal.delimiterOffset\nimport okhttp3.internal.http.MAX_DATE\nimport okhttp3.internal.http.toHttpDateString\nimport okhttp3.internal.indexOfControlOrNonAscii\nimport okhttp3.internal.publicsuffix.PublicSuffixDatabase\nimport okhttp3.internal.toCanonicalHost\nimport okhttp3.internal.trimSubstring\nimport okhttp3.internal.unmodifiable\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n\n/**\n * An [RFC 6265](http://tools.ietf.org/html/rfc6265) Cookie.\n *\n * This class doesn't support additional attributes on cookies, like\n * [Chromium's Priority=HIGH extension][chromium_extension].\n *\n * [chromium_extension]: https://code.google.com/p/chromium/issues/detail?id=232693\n */\n@Suppress(\"NAME_SHADOWING\")\nclass Cookie private constructor(\n  /** Returns a non-empty string with this cookie's name. */\n  @get:JvmName(\"name\") val name: String,\n  /** Returns a possibly-empty string with this cookie's value. */\n  @get:JvmName(\"value\") val value: String,\n  /**\n   * Returns the time that this cookie expires, in the same format as [System.currentTimeMillis].\n   * This is December 31, 9999 if the cookie is not [persistent], in which case it will expire at the\n   * end of the current session.\n   *\n   * This may return a value less than the current time, in which case the cookie is already\n   * expired. Webservers may return expired cookies as a mechanism to delete previously set cookies\n   * that may or may not themselves be expired.\n   */\n  @get:JvmName(\"expiresAt\") val expiresAt: Long,\n  /**\n   * Returns the cookie's domain. If [hostOnly] returns true this is the only domain that matches\n   * this cookie; otherwise it matches this domain and all subdomains.\n   */\n  @get:JvmName(\"domain\") val domain: String,\n  /**\n   * Returns this cookie's path. This cookie matches URLs prefixed with path segments that match\n   * this path's segments. For example, if this path is `/foo` this cookie matches requests to\n   * `/foo` and `/foo/bar`, but not `/` or `/football`.\n   */\n  @get:JvmName(\"path\") val path: String,\n  /** Returns true if this cookie should be limited to only HTTPS requests. */\n  @get:JvmName(\"secure\") val secure: Boolean,\n  /**\n   * Returns true if this cookie should be limited to only HTTP APIs. In web browsers this prevents\n   * the cookie from being accessible to scripts.\n   */\n  @get:JvmName(\"httpOnly\") val httpOnly: Boolean,\n  /**\n   * Returns true if this cookie does not expire at the end of the current session.\n   *\n   * This is true if either 'expires' or 'max-age' is present.\n   */\n  @get:JvmName(\"persistent\") val persistent: Boolean,\n  /**\n   * Returns true if this cookie's domain should be interpreted as a single host name, or false if\n   * it should be interpreted as a pattern. This flag will be false if its `Set-Cookie` header\n   * included a `domain` attribute.\n   *\n   * For example, suppose the cookie's domain is `example.com`. If this flag is true it matches\n   * **only** `example.com`. If this flag is false it matches `example.com` and all subdomains\n   * including `api.example.com`, `www.example.com`, and `beta.api.example.com`.\n   *\n   * This is true unless 'domain' is present.\n   */\n  @get:JvmName(\"hostOnly\") val hostOnly: Boolean,\n  /**\n   * Returns a string describing whether this cookie is sent for cross-site calls.\n   *\n   * Two URLs are on the same site if they share a [top private domain][HttpUrl.topPrivateDomain].\n   * Otherwise, they are cross-site URLs.\n   *\n   * When a URL is requested, it may be in the context of another URL.\n   *\n   *  * **Embedded resources like images and iframes** in browsers use the context as the page in\n   *    the address bar and the subject is the URL of an embedded resource.\n   *\n   *  * **Potentially-destructive navigations such as HTTP POST calls** use the context as the page\n   *    originating the navigation, and the subject is the page being navigated to.\n   *\n   * The values of this attribute determine whether this cookie is sent for cross-site calls:\n   *\n   *  - \"Strict\": the cookie is omitted when the subject URL is an embedded resource or a\n   *    potentially-destructive navigation.\n   *\n   *  - \"Lax\": the cookie is omitted when the subject URL is an embedded resource. It is sent for\n   *    potentially-destructive navigation. This is the default value.\n   *\n   *  - \"None\": the cookie is always sent. The \"Secure\" attribute must also be set when setting this\n   *    value.\n   */\n  @get:JvmName(\"sameSite\")\n  val sameSite: String?,\n) {\n  /**\n   * Returns true if this cookie should be included on a request to [url]. In addition to this\n   * check callers should also confirm that this cookie has not expired.\n   */\n  fun matches(url: HttpUrl): Boolean {\n    val domainMatch =\n      if (hostOnly) {\n        url.host == domain\n      } else {\n        domainMatch(url.host, domain)\n      }\n    if (!domainMatch) return false\n\n    if (!pathMatch(url, path)) return false\n\n    return !secure || url.isHttps\n  }\n\n  override fun equals(other: Any?): Boolean =\n    other is Cookie &&\n      other.name == name &&\n      other.value == value &&\n      other.expiresAt == expiresAt &&\n      other.domain == domain &&\n      other.path == path &&\n      other.secure == secure &&\n      other.httpOnly == httpOnly &&\n      other.persistent == persistent &&\n      other.hostOnly == hostOnly &&\n      other.sameSite == sameSite\n\n  @IgnoreJRERequirement // As of AGP 3.4.1, D8 desugars API 24 hashCode methods.\n  override fun hashCode(): Int {\n    var result = 17\n    result = 31 * result + name.hashCode()\n    result = 31 * result + value.hashCode()\n    result = 31 * result + expiresAt.hashCode()\n    result = 31 * result + domain.hashCode()\n    result = 31 * result + path.hashCode()\n    result = 31 * result + secure.hashCode()\n    result = 31 * result + httpOnly.hashCode()\n    result = 31 * result + persistent.hashCode()\n    result = 31 * result + hostOnly.hashCode()\n    result = 31 * result + sameSite.hashCode()\n    return result\n  }\n\n  override fun toString(): String = toString(false)\n\n  @JvmName(\"-deprecated_name\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"name\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun name(): String = name\n\n  @JvmName(\"-deprecated_value\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"value\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun value(): String = value\n\n  @JvmName(\"-deprecated_persistent\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"persistent\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun persistent(): Boolean = persistent\n\n  @JvmName(\"-deprecated_expiresAt\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"expiresAt\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun expiresAt(): Long = expiresAt\n\n  @JvmName(\"-deprecated_hostOnly\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"hostOnly\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun hostOnly(): Boolean = hostOnly\n\n  @JvmName(\"-deprecated_domain\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"domain\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun domain(): String = domain\n\n  @JvmName(\"-deprecated_path\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"path\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun path(): String = path\n\n  @JvmName(\"-deprecated_httpOnly\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"httpOnly\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun httpOnly(): Boolean = httpOnly\n\n  @JvmName(\"-deprecated_secure\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"secure\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun secure(): Boolean = secure\n\n  /**\n   * @param forObsoleteRfc2965 true to include a leading `.` on the domain pattern. This is\n   *     necessary for `example.com` to match `www.example.com` under RFC 2965. This extra dot is\n   *     ignored by more recent specifications.\n   */\n  internal fun toString(forObsoleteRfc2965: Boolean): String {\n    return buildString {\n      append(name)\n      append('=')\n      append(value)\n\n      if (persistent) {\n        if (expiresAt == Long.MIN_VALUE) {\n          append(\"; max-age=0\")\n        } else {\n          append(\"; expires=\").append(Date(expiresAt).toHttpDateString())\n        }\n      }\n\n      if (!hostOnly) {\n        append(\"; domain=\")\n        if (forObsoleteRfc2965) {\n          append(\".\")\n        }\n        append(domain)\n      }\n\n      append(\"; path=\").append(path)\n\n      if (secure) {\n        append(\"; secure\")\n      }\n\n      if (httpOnly) {\n        append(\"; httponly\")\n      }\n\n      if (sameSite != null) {\n        append(\"; samesite=\").append(sameSite)\n      }\n\n      return toString()\n    }\n  }\n\n  fun newBuilder(): Builder = Builder(this)\n\n  /**\n   * Builds a cookie. The [name], [value], and [domain] values must all be set before calling\n   * [build].\n   */\n  class Builder() {\n    private var name: String? = null\n    private var value: String? = null\n    private var expiresAt = MAX_DATE\n    private var domain: String? = null\n    private var path = \"/\"\n    private var secure = false\n    private var httpOnly = false\n    private var persistent = false\n    private var hostOnly = false\n    private var sameSite: String? = null\n\n    internal constructor(cookie: Cookie) : this() {\n      this.name = cookie.name\n      this.value = cookie.value\n      this.expiresAt = cookie.expiresAt\n      this.domain = cookie.domain\n      this.path = cookie.path\n      this.secure = cookie.secure\n      this.httpOnly = cookie.httpOnly\n      this.persistent = cookie.persistent\n      this.hostOnly = cookie.hostOnly\n      this.sameSite = cookie.sameSite\n    }\n\n    fun name(name: String) =\n      apply {\n        require(name.trim() == name) { \"name is not trimmed\" }\n        this.name = name\n      }\n\n    fun value(value: String) =\n      apply {\n        require(value.trim() == value) { \"value is not trimmed\" }\n        this.value = value\n      }\n\n    fun expiresAt(expiresAt: Long) =\n      apply {\n        var expiresAt = expiresAt\n        if (expiresAt <= 0L) expiresAt = Long.MIN_VALUE\n        if (expiresAt > MAX_DATE) expiresAt = MAX_DATE\n        this.expiresAt = expiresAt\n        this.persistent = true\n      }\n\n    /**\n     * Set the domain pattern for this cookie. The cookie will match [domain] and all of its\n     * subdomains.\n     */\n    fun domain(domain: String): Builder = domain(domain, false)\n\n    /**\n     * Set the host-only domain for this cookie. The cookie will match [domain] but none of\n     * its subdomains.\n     */\n    fun hostOnlyDomain(domain: String): Builder = domain(domain, true)\n\n    private fun domain(\n      domain: String,\n      hostOnly: Boolean,\n    ) = apply {\n      val canonicalDomain =\n        domain.toCanonicalHost()\n          ?: throw IllegalArgumentException(\"unexpected domain: $domain\")\n      this.domain = canonicalDomain\n      this.hostOnly = hostOnly\n    }\n\n    fun path(path: String) =\n      apply {\n        require(path.startsWith(\"/\")) { \"path must start with '/'\" }\n        this.path = path\n      }\n\n    fun secure() =\n      apply {\n        this.secure = true\n      }\n\n    fun httpOnly() =\n      apply {\n        this.httpOnly = true\n      }\n\n    fun sameSite(sameSite: String) =\n      apply {\n        require(sameSite.trim() == sameSite) { \"sameSite is not trimmed\" }\n        this.sameSite = sameSite\n      }\n\n    fun build(): Cookie =\n      Cookie(\n        name ?: throw NullPointerException(\"builder.name == null\"),\n        value ?: throw NullPointerException(\"builder.value == null\"),\n        expiresAt,\n        domain ?: throw NullPointerException(\"builder.domain == null\"),\n        path,\n        secure,\n        httpOnly,\n        persistent,\n        hostOnly,\n        sameSite,\n      )\n  }\n\n  @Suppress(\"NAME_SHADOWING\")\n  companion object {\n    private val YEAR_PATTERN = Pattern.compile(\"(\\\\d{2,4})[^\\\\d]*\")\n    private val MONTH_PATTERN =\n      Pattern.compile(\"(?i)(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec).*\")\n    private val DAY_OF_MONTH_PATTERN = Pattern.compile(\"(\\\\d{1,2})[^\\\\d]*\")\n    private val TIME_PATTERN = Pattern.compile(\"(\\\\d{1,2}):(\\\\d{1,2}):(\\\\d{1,2})[^\\\\d]*\")\n\n    private fun domainMatch(\n      urlHost: String,\n      domain: String,\n    ): Boolean {\n      if (urlHost == domain) {\n        return true // As in 'example.com' matching 'example.com'.\n      }\n\n      return urlHost.endsWith(domain) &&\n        urlHost[urlHost.length - domain.length - 1] == '.' &&\n        !urlHost.canParseAsIpAddress()\n    }\n\n    private fun pathMatch(\n      url: HttpUrl,\n      path: String,\n    ): Boolean {\n      val urlPath = url.encodedPath\n\n      if (urlPath == path) {\n        return true // As in '/foo' matching '/foo'.\n      }\n\n      if (urlPath.startsWith(path)) {\n        if (path.endsWith(\"/\")) return true // As in '/' matching '/foo'.\n        if (urlPath[path.length] == '/') return true // As in '/foo' matching '/foo/bar'.\n      }\n\n      return false\n    }\n\n    /**\n     * Attempt to parse a `Set-Cookie` HTTP header value [setCookie] as a cookie. Returns null if\n     * [setCookie] is not a well-formed cookie.\n     */\n    @JvmStatic\n    fun parse(\n      url: HttpUrl,\n      setCookie: String,\n    ): Cookie? = parse(System.currentTimeMillis(), url, setCookie)\n\n    internal fun parse(\n      currentTimeMillis: Long,\n      url: HttpUrl,\n      setCookie: String,\n    ): Cookie? {\n      val cookiePairEnd = setCookie.delimiterOffset(';')\n\n      val pairEqualsSign = setCookie.delimiterOffset('=', endIndex = cookiePairEnd)\n      if (pairEqualsSign == cookiePairEnd) return null\n\n      val cookieName = setCookie.trimSubstring(endIndex = pairEqualsSign)\n      if (cookieName.isEmpty() || cookieName.indexOfControlOrNonAscii() != -1) return null\n\n      val cookieValue = setCookie.trimSubstring(pairEqualsSign + 1, cookiePairEnd)\n      if (cookieValue.indexOfControlOrNonAscii() != -1) return null\n\n      var expiresAt = MAX_DATE\n      var deltaSeconds = -1L\n      var domain: String? = null\n      var path: String? = null\n      var secureOnly = false\n      var httpOnly = false\n      var hostOnly = true\n      var persistent = false\n      var sameSite: String? = null\n\n      var pos = cookiePairEnd + 1\n      val limit = setCookie.length\n      while (pos < limit) {\n        val attributePairEnd = setCookie.delimiterOffset(';', pos, limit)\n\n        val attributeEqualsSign = setCookie.delimiterOffset('=', pos, attributePairEnd)\n        val attributeName = setCookie.trimSubstring(pos, attributeEqualsSign)\n        val attributeValue =\n          if (attributeEqualsSign < attributePairEnd) {\n            setCookie.trimSubstring(attributeEqualsSign + 1, attributePairEnd)\n          } else {\n            \"\"\n          }\n\n        when {\n          attributeName.equals(\"expires\", ignoreCase = true) -> {\n            try {\n              expiresAt = parseExpires(attributeValue, 0, attributeValue.length)\n              persistent = true\n            } catch (_: IllegalArgumentException) {\n              // Ignore this attribute, it isn't recognizable as a date.\n            }\n          }\n\n          attributeName.equals(\"max-age\", ignoreCase = true) -> {\n            try {\n              deltaSeconds = parseMaxAge(attributeValue)\n              persistent = true\n            } catch (_: NumberFormatException) {\n              // Ignore this attribute, it isn't recognizable as a max age.\n            }\n          }\n\n          attributeName.equals(\"domain\", ignoreCase = true) -> {\n            try {\n              domain = parseDomain(attributeValue)\n              hostOnly = false\n            } catch (_: IllegalArgumentException) {\n              // Ignore this attribute, it isn't recognizable as a domain.\n            }\n          }\n\n          attributeName.equals(\"path\", ignoreCase = true) -> {\n            path = attributeValue\n          }\n\n          attributeName.equals(\"secure\", ignoreCase = true) -> {\n            secureOnly = true\n          }\n\n          attributeName.equals(\"httponly\", ignoreCase = true) -> {\n            httpOnly = true\n          }\n\n          attributeName.equals(\"samesite\", ignoreCase = true) -> {\n            sameSite = attributeValue\n          }\n        }\n\n        pos = attributePairEnd + 1\n      }\n\n      // If 'Max-Age' is present, it takes precedence over 'Expires', regardless of the order the two\n      // attributes are declared in the cookie string.\n      if (deltaSeconds == Long.MIN_VALUE) {\n        expiresAt = Long.MIN_VALUE\n      } else if (deltaSeconds != -1L) {\n        val deltaMilliseconds =\n          if (deltaSeconds <= Long.MAX_VALUE / 1000) {\n            deltaSeconds * 1000\n          } else {\n            Long.MAX_VALUE\n          }\n        expiresAt = currentTimeMillis + deltaMilliseconds\n        if (expiresAt < currentTimeMillis || expiresAt > MAX_DATE) {\n          expiresAt = MAX_DATE // Handle overflow & limit the date range.\n        }\n      }\n\n      // If the domain is present, it must domain match. Otherwise we have a host-only cookie.\n      val urlHost = url.host\n      if (domain == null) {\n        domain = urlHost\n      } else if (!domainMatch(urlHost, domain)) {\n        return null // No domain match? This is either incompetence or malice!\n      }\n\n      // If the domain is a suffix of the url host, it must not be a public suffix.\n      if (urlHost.length != domain.length &&\n        PublicSuffixDatabase.get().getEffectiveTldPlusOne(domain) == null\n      ) {\n        return null\n      }\n\n      // If the path is absent or didn't start with '/', use the default path. It's a string like\n      // '/foo/bar' for a URL like 'http://example.com/foo/bar/baz'. It always starts with '/'.\n      if (path == null || !path.startsWith(\"/\")) {\n        val encodedPath = url.encodedPath\n        val lastSlash = encodedPath.lastIndexOf('/')\n        path = if (lastSlash != 0) encodedPath.substring(0, lastSlash) else \"/\"\n      }\n\n      return Cookie(\n        cookieName,\n        cookieValue,\n        expiresAt,\n        domain,\n        path,\n        secureOnly,\n        httpOnly,\n        persistent,\n        hostOnly,\n        sameSite,\n      )\n    }\n\n    /** Parse a date as specified in RFC 6265, section 5.1.1. */\n    private fun parseExpires(\n      s: String,\n      pos: Int,\n      limit: Int,\n    ): Long {\n      var pos = pos\n      pos = dateCharacterOffset(s, pos, limit, false)\n\n      var hour = -1\n      var minute = -1\n      var second = -1\n      var dayOfMonth = -1\n      var month = -1\n      var year = -1\n      val matcher = TIME_PATTERN.matcher(s)\n\n      while (pos < limit) {\n        val end = dateCharacterOffset(s, pos + 1, limit, true)\n        matcher.region(pos, end)\n\n        when {\n          hour == -1 && matcher.usePattern(TIME_PATTERN).matches() -> {\n            hour = matcher.group(1).toInt()\n            minute = matcher.group(2).toInt()\n            second = matcher.group(3).toInt()\n          }\n\n          dayOfMonth == -1 && matcher.usePattern(DAY_OF_MONTH_PATTERN).matches() -> {\n            dayOfMonth = matcher.group(1).toInt()\n          }\n\n          month == -1 && matcher.usePattern(MONTH_PATTERN).matches() -> {\n            val monthString = matcher.group(1).lowercase(Locale.US)\n            month = MONTH_PATTERN.pattern().indexOf(monthString) / 4 // Sneaky! jan=1, dec=12.\n          }\n\n          year == -1 && matcher.usePattern(YEAR_PATTERN).matches() -> {\n            year = matcher.group(1).toInt()\n          }\n        }\n\n        pos = dateCharacterOffset(s, end + 1, limit, false)\n      }\n\n      // Convert two-digit years into four-digit years. 99 becomes 1999, 15 becomes 2015.\n      if (year in 70..99) year += 1900\n      if (year in 0..69) year += 2000\n\n      // If any partial is omitted or out of range, return -1. The date is impossible. Note that leap\n      // seconds are not supported by this syntax.\n      require(year >= 1601)\n      require(month != -1)\n      require(dayOfMonth in 1..31)\n      require(hour in 0..23)\n      require(minute in 0..59)\n      require(second in 0..59)\n\n      GregorianCalendar(UTC).apply {\n        isLenient = false\n        set(Calendar.YEAR, year)\n        set(Calendar.MONTH, month - 1)\n        set(Calendar.DAY_OF_MONTH, dayOfMonth)\n        set(Calendar.HOUR_OF_DAY, hour)\n        set(Calendar.MINUTE, minute)\n        set(Calendar.SECOND, second)\n        set(Calendar.MILLISECOND, 0)\n        return timeInMillis\n      }\n    }\n\n    /**\n     * Returns the index of the next date character in `input`, or if `invert` the index\n     * of the next non-date character in `input`.\n     */\n    private fun dateCharacterOffset(\n      input: String,\n      pos: Int,\n      limit: Int,\n      invert: Boolean,\n    ): Int {\n      for (i in pos until limit) {\n        val c = input[i].code\n        val dateCharacter =\n          (\n            (\n              ((c < ' '.code) && (c != '\\t'.code)) || (c >= '\\u007f'.code) || (c in ('0'.code..'9'.code)) || (c in ('a'.code..'z'.code)) ||\n                (c in ('A'.code..'Z'.code)) ||\n                (c == ':'.code)\n            )\n          )\n        if (dateCharacter == !invert) return i\n      }\n      return limit\n    }\n\n    /**\n     * Returns the positive value if [s] is positive, or [Long.MIN_VALUE] if it is either 0 or\n     * negative. If the value is positive but out of range, this returns [Long.MAX_VALUE].\n     *\n     * @throws NumberFormatException if [s] is not an integer of any precision.\n     */\n    private fun parseMaxAge(s: String): Long {\n      try {\n        val parsed = s.toLong()\n        return if (parsed <= 0L) Long.MIN_VALUE else parsed\n      } catch (e: NumberFormatException) {\n        // Check if the value is an integer (positive or negative) that's too big for a long.\n        if (s.matches(\"-?\\\\d+\".toRegex())) {\n          return if (s.startsWith(\"-\")) Long.MIN_VALUE else Long.MAX_VALUE\n        }\n        throw e\n      }\n    }\n\n    /**\n     * Returns a domain string like `example.com` for an input domain like `EXAMPLE.COM`\n     * or `.example.com`.\n     */\n    private fun parseDomain(s: String): String {\n      require(!s.endsWith(\".\"))\n      return s.removePrefix(\".\").toCanonicalHost() ?: throw IllegalArgumentException()\n    }\n\n    /** Returns all of the cookies from a set of HTTP response headers. */\n    @JvmStatic\n    fun parseAll(\n      url: HttpUrl,\n      headers: Headers,\n    ): List<Cookie> {\n      val cookieStrings = headers.values(\"Set-Cookie\")\n      var cookies: MutableList<Cookie>? = null\n\n      for (i in 0 until cookieStrings.size) {\n        val cookie = parse(url, cookieStrings[i]) ?: continue\n        if (cookies == null) cookies = mutableListOf()\n        cookies.add(cookie)\n      }\n\n      return cookies?.unmodifiable().orEmpty()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/CookieJar.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\n/**\n * Provides **policy** and **persistence** for HTTP cookies.\n *\n * As policy, implementations of this interface are responsible for selecting which cookies to\n * accept and which to reject. A reasonable policy is to reject all cookies, though that may\n * interfere with session-based authentication schemes that require cookies.\n *\n * As persistence, implementations of this interface must also provide storage of cookies. Simple\n * implementations may store cookies in memory; sophisticated ones may use the file system or\n * database to hold accepted cookies. The [cookie storage model][rfc_6265_53] specifies policies for\n * updating and expiring cookies.\n *\n * [rfc_6265_53]: https://tools.ietf.org/html/rfc6265#section-5.3\n */\ninterface CookieJar {\n  /**\n   * Saves [cookies] from an HTTP response to this store according to this jar's policy.\n   *\n   * Note that this method may be called a second time for a single HTTP response if the response\n   * includes a trailer. For this obscure HTTP feature, [cookies] contains only the trailer's\n   * cookies.\n   */\n  fun saveFromResponse(\n    url: HttpUrl,\n    cookies: List<Cookie>,\n  )\n\n  /**\n   * Load cookies from the jar for an HTTP request to [url]. This method returns a possibly\n   * empty list of cookies for the network request.\n   *\n   * Simple implementations will return the accepted cookies that have not yet expired and that\n   * [match][Cookie.matches] [url].\n   */\n  fun loadForRequest(url: HttpUrl): List<Cookie>\n\n  companion object {\n    /** A cookie jar that never accepts any cookies. */\n    @JvmField\n    val NO_COOKIES: CookieJar = NoCookies()\n\n    private class NoCookies : CookieJar {\n      override fun saveFromResponse(\n        url: HttpUrl,\n        cookies: List<Cookie>,\n      ) {\n      }\n\n      override fun loadForRequest(url: HttpUrl): List<Cookie> = emptyList()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Credentials.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.nio.charset.Charset\nimport kotlin.text.Charsets.ISO_8859_1\nimport okio.ByteString.Companion.encode\n\n/** Factory for HTTP authorization credentials. */\nobject Credentials {\n  /** Returns an auth credential for the Basic scheme. */\n  @JvmStatic @JvmOverloads\n  fun basic(\n    username: String,\n    password: String,\n    charset: Charset = ISO_8859_1,\n  ): String {\n    val usernameAndPassword = \"$username:$password\"\n    val encoded = usernameAndPassword.encode(charset).base64()\n    return \"Basic $encoded\"\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.util.ArrayDeque\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.SynchronousQueue\nimport java.util.concurrent.ThreadPoolExecutor\nimport java.util.concurrent.TimeUnit\nimport okhttp3.internal.assertLockNotHeld\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.connection.RealCall.AsyncCall\nimport okhttp3.internal.okHttpName\nimport okhttp3.internal.threadFactory\nimport okhttp3.internal.unmodifiable\n\n/**\n * Policy on when async requests are executed.\n *\n * Each dispatcher uses an [ExecutorService] to run calls internally. If you supply your own\n * executor, it should be able to run [the configured maximum][maxRequests] number of calls\n * concurrently.\n */\nclass Dispatcher() {\n  /**\n   * The maximum number of requests to execute concurrently. Above this requests queue in memory,\n   * waiting for the running calls to complete.\n   *\n   * If more than [maxRequests] requests are in flight when this is invoked, those requests will\n   * remain in flight.\n   */\n  @get:Synchronized\n  var maxRequests = 64\n    set(maxRequests) {\n      require(maxRequests >= 1) { \"max < 1: $maxRequests\" }\n      synchronized(this) {\n        field = maxRequests\n      }\n      promoteAndExecute()\n    }\n\n  /**\n   * The maximum number of requests for each host to execute concurrently. This limits requests by\n   * the URL's host name. Note that concurrent requests to a single IP address may still exceed this\n   * limit: multiple hostnames may share an IP address or be routed through the same HTTP proxy.\n   *\n   * If more than [maxRequestsPerHost] requests are in flight when this is invoked, those requests\n   * will remain in flight.\n   *\n   * WebSocket connections to hosts **do not** count against this limit.\n   */\n  @get:Synchronized\n  var maxRequestsPerHost = 5\n    set(maxRequestsPerHost) {\n      require(maxRequestsPerHost >= 1) { \"max < 1: $maxRequestsPerHost\" }\n      synchronized(this) {\n        field = maxRequestsPerHost\n      }\n      promoteAndExecute()\n    }\n\n  /**\n   * A callback to be invoked each time the dispatcher becomes idle (when the number of running\n   * calls returns to zero).\n   *\n   * Note: The time at which a [call][Call] is considered idle is different depending on whether it\n   * was run [asynchronously][Call.enqueue] or [synchronously][Call.execute]. Asynchronous calls\n   * become idle after the [onResponse][Callback.onResponse] or [onFailure][Callback.onFailure]\n   * callback has returned. Synchronous calls become idle once [execute()][Call.execute] returns.\n   * This means that if you are doing synchronous calls the network layer will not truly be idle\n   * until every returned [Response] has been closed.\n   */\n  @get:Synchronized\n  @set:Synchronized\n  var idleCallback: Runnable? = null\n\n  private var executorServiceOrNull: ExecutorService? = null\n\n  @get:JvmName(\"executorService\")\n  @get:Synchronized\n  val executorService: ExecutorService\n    get() {\n      if (executorServiceOrNull == null) {\n        executorServiceOrNull =\n          ThreadPoolExecutor(\n            0,\n            Int.MAX_VALUE,\n            60,\n            TimeUnit.SECONDS,\n            SynchronousQueue(),\n            threadFactory(\"$okHttpName Dispatcher\", false),\n          )\n      }\n      return executorServiceOrNull!!\n    }\n\n  /** Ready async calls in the order they'll be run. */\n  private val readyAsyncCalls = ArrayDeque<AsyncCall>()\n\n  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */\n  private val runningAsyncCalls = ArrayDeque<AsyncCall>()\n\n  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */\n  private val runningSyncCalls = ArrayDeque<RealCall>()\n\n  constructor(executorService: ExecutorService?) : this() {\n    this.executorServiceOrNull = executorService\n  }\n\n  internal fun enqueue(call: AsyncCall) {\n    promoteAndExecute(enqueuedCall = call)\n  }\n\n  private fun findExistingCallWithHost(host: String): AsyncCall? {\n    for (existingCall in runningAsyncCalls) {\n      if (existingCall.host == host) return existingCall\n    }\n    for (existingCall in readyAsyncCalls) {\n      if (existingCall.host == host) return existingCall\n    }\n    return null\n  }\n\n  /**\n   * Cancel all calls currently enqueued or executing. Includes calls executed both\n   * [synchronously][Call.execute] and [asynchronously][Call.enqueue].\n   */\n  @Synchronized\n  fun cancelAll() {\n    for (call in readyAsyncCalls) {\n      call.call.cancel()\n    }\n    for (call in runningAsyncCalls) {\n      call.call.cancel()\n    }\n    for (call in runningSyncCalls) {\n      call.cancel()\n    }\n  }\n\n  /**\n   * Promotes eligible calls from [readyAsyncCalls] to [runningAsyncCalls] and runs them on the\n   * executor service. Must not be called with synchronization because executing calls can call\n   * into user code.\n   *\n   * @param enqueuedCall a call to enqueue in the synchronized block\n   * @param finishedCall a call to finish in the synchronized block\n   * @param finishedAsyncCall an async call to finish in the synchronized block\n   */\n  private fun promoteAndExecute(\n    enqueuedCall: AsyncCall? = null,\n    finishedCall: RealCall? = null,\n    finishedAsyncCall: AsyncCall? = null,\n  ) {\n    assertLockNotHeld()\n    val executorIsShutdown = executorService.isShutdown\n\n    // Actions to take outside the synchronized block.\n    class Effects(\n      val callsToExecute: List<AsyncCall>,\n      val idleCallbackToRun: Runnable?,\n    )\n\n    val effects =\n      synchronized(this) {\n        if (finishedCall != null) {\n          check(runningSyncCalls.remove(finishedCall)) { \"Call wasn't in-flight!\" }\n        }\n\n        if (finishedAsyncCall != null) {\n          finishedAsyncCall.callsPerHost.decrementAndGet()\n          check(runningAsyncCalls.remove(finishedAsyncCall)) { \"Call wasn't in-flight!\" }\n        }\n\n        if (enqueuedCall != null) {\n          readyAsyncCalls.add(enqueuedCall)\n\n          // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to\n          // the same host.\n          if (!enqueuedCall.call.forWebSocket) {\n            val existingCall = findExistingCallWithHost(enqueuedCall.host)\n            if (existingCall != null) enqueuedCall.reuseCallsPerHostFrom(existingCall)\n          }\n        }\n\n        val becameIdle =\n          (finishedCall != null || finishedAsyncCall != null) &&\n            (executorIsShutdown || runningAsyncCalls.isEmpty()) &&\n            runningSyncCalls.isEmpty()\n        val idleCallbackToRun = if (becameIdle) idleCallback else null\n\n        if (executorIsShutdown) {\n          return@synchronized Effects(\n            callsToExecute =\n              readyAsyncCalls\n                .toList()\n                .also { readyAsyncCalls.clear() },\n            idleCallbackToRun = idleCallbackToRun,\n          )\n        }\n\n        val callsToExecute = mutableListOf<AsyncCall>()\n        val i = readyAsyncCalls.iterator()\n        while (i.hasNext()) {\n          val asyncCall = i.next()\n\n          if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.\n          if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.\n\n          i.remove()\n\n          asyncCall.callsPerHost.incrementAndGet()\n          callsToExecute.add(asyncCall)\n          runningAsyncCalls.add(asyncCall)\n        }\n\n        return@synchronized Effects(\n          callsToExecute = callsToExecute,\n          idleCallbackToRun = idleCallbackToRun,\n        )\n      }\n\n    var callDispatcherQueueStart = true\n\n    for (i in 0 until effects.callsToExecute.size) {\n      val call = effects.callsToExecute[i]\n\n      // If the newly-enqueued call is already out, skip its dispatcher queue events. We only\n      // publish those events for calls that have to wait.\n      if (call === enqueuedCall) {\n        callDispatcherQueueStart = false\n      } else {\n        call.call.eventListener.dispatcherQueueEnd(call.call, this)\n      }\n\n      if (executorIsShutdown) {\n        call.failRejected()\n      } else {\n        call.executeOn(executorService)\n      }\n    }\n\n    if (callDispatcherQueueStart && enqueuedCall != null) {\n      enqueuedCall.call.eventListener.dispatcherQueueStart(enqueuedCall.call, this)\n    }\n\n    effects.idleCallbackToRun?.run()\n  }\n\n  /** Used by [Call.execute] to signal it is in-flight. */\n  @Synchronized\n  internal fun executed(call: RealCall) = runningSyncCalls.add(call)\n\n  /** Used by [AsyncCall.run] to signal completion. */\n  internal fun finished(call: AsyncCall) {\n    promoteAndExecute(finishedAsyncCall = call)\n  }\n\n  /** Used by [Call.execute] to signal completion. */\n  internal fun finished(call: RealCall) {\n    promoteAndExecute(finishedCall = call)\n  }\n\n  /** Returns a snapshot of the calls currently awaiting execution. */\n  @Synchronized\n  fun queuedCalls(): List<Call> = readyAsyncCalls.map { it.call }.unmodifiable()\n\n  /** Returns a snapshot of the calls currently being executed. */\n  @Synchronized\n  fun runningCalls(): List<Call> = (runningSyncCalls + runningAsyncCalls.map { it.call }).unmodifiable()\n\n  @Synchronized\n  fun queuedCallsCount(): Int = readyAsyncCalls.size\n\n  @Synchronized\n  fun runningCallsCount(): Int = runningAsyncCalls.size + runningSyncCalls.size\n\n  @JvmName(\"-deprecated_executorService\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"executorService\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun executorService(): ExecutorService = executorService\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dns.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport okhttp3.Dns.Companion.SYSTEM\n\n/**\n * A domain name service that resolves IP addresses for host names. Most applications will use the\n * [system DNS service][SYSTEM], which is the default. Some applications may provide their own\n * implementation to use a different DNS server, to prefer IPv6 addresses, to prefer IPv4 addresses,\n * or to force a specific known IP address.\n *\n * Implementations of this interface must be safe for concurrent use.\n */\nfun interface Dns {\n  /**\n   * Returns the IP addresses of `hostname`, in the order they will be attempted by OkHttp. If a\n   * connection to an address fails, OkHttp will retry the connection with the next address until\n   * either a connection is made, the set of IP addresses is exhausted, or a limit is exceeded.\n   */\n  @Throws(UnknownHostException::class)\n  fun lookup(hostname: String): List<InetAddress>\n\n  companion object {\n    /**\n     * A DNS that uses [InetAddress.getAllByName] to ask the underlying operating system to\n     * lookup IP addresses. Most custom [Dns] implementations should delegate to this instance.\n     */\n    @JvmField\n    val SYSTEM: Dns = DnsSystem()\n\n    private class DnsSystem : Dns {\n      override fun lookup(hostname: String): List<InetAddress> {\n        try {\n          return InetAddress.getAllByName(hostname).toList()\n        } catch (e: NullPointerException) {\n          throw UnknownHostException(\"Broken system behaviour for dns lookup of $hostname\").apply {\n            initCause(e)\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/EventListener.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.ProtocolException\nimport java.net.Proxy\n\n/**\n * Listener for metrics events. Extend this class to monitor the quantity, size, and duration of\n * your application's HTTP calls.\n *\n * All start/connect/acquire events will eventually receive a matching end/release event, either\n * successful (non-null parameters), or failed (non-null throwable). The first common parameters of\n * each event pair are used to link the event in case of concurrent or repeated events e.g.\n * `dnsStart(call, domainName)` → `dnsEnd(call, domainName, inetAddressList)`.\n *\n * Events are typically nested with this structure:\n *\n *  * call ([callStart], [callEnd], [callFailed])\n *    * dispatcher queue ([dispatcherQueueStart], [dispatcherQueueEnd])\n *    * proxy selection ([proxySelectStart], [proxySelectEnd])\n *    * dns ([dnsStart], [dnsEnd])\n *    * connect ([connectStart], [connectEnd], [connectFailed])\n *      * secure connect ([secureConnectStart], [secureConnectEnd])\n *    * connection held ([connectionAcquired], [connectionReleased])\n *      * request ([requestFailed])\n *        * headers ([requestHeadersStart], [requestHeadersEnd])\n *        * body ([requestBodyStart], [requestBodyEnd])\n *      * response ([responseFailed])\n *        * headers ([responseHeadersStart], [responseHeadersEnd])\n *        * body ([responseBodyStart], [responseBodyEnd])\n *\n * This nesting is typical but not strict. For example, when calls use \"Expect: continue\" the\n * request body start and end events occur within the response header events. Similarly,\n * [duplex calls][RequestBody.isDuplex] interleave the request and response bodies.\n *\n * Since connections may be reused, the proxy selection, DNS, and connect events may not be present\n * for a call. In future releases of OkHttp these events may also occur concurrently to permit\n * multiple routes to be attempted simultaneously.\n *\n * Events and sequences of events may be repeated for retries and follow-ups.\n *\n * All event methods must execute fast, without external locking, cannot throw exceptions, attempt\n * to mutate the event parameters, or be re-entrant back into the client. Any IO - writing to files\n * or network should be done asynchronously.\n */\nabstract class EventListener {\n  /**\n   * Invoked as soon as a call is enqueued or executed by a client. In case of thread or stream\n   * limits, this call may be executed well before processing the request is able to begin.\n   *\n   * This will be invoked only once for a single [Call]. Retries of different routes or redirects\n   * will be handled within the boundaries of a single [callStart] and [callEnd]/[callFailed] pair.\n   */\n  open fun callStart(call: Call) {\n  }\n\n  /**\n   * Invoked for calls that were not executed immediately because resources weren't available. The\n   * call will remain in the queue until resources are available.\n   *\n   * Use [Dispatcher.maxRequests] and [Dispatcher.maxRequestsPerHost] to configure how many calls\n   * OkHttp performs concurrently.\n   */\n  open fun dispatcherQueueStart(\n    call: Call,\n    dispatcher: Dispatcher,\n  ) {\n  }\n\n  /**\n   * Invoked when [call] will be executed immediately.\n   *\n   * This method is invoked after [dispatcherQueueStart].\n   */\n  open fun dispatcherQueueEnd(\n    call: Call,\n    dispatcher: Dispatcher,\n  ) {\n  }\n\n  /**\n   * Invoked prior to a proxy selection.\n   *\n   * This will be invoked for route selection regardless of whether the client\n   * is configured with a single proxy, a proxy selector, or neither.\n   *\n   * @param url a URL with only the scheme, hostname, and port specified.\n   */\n  open fun proxySelectStart(\n    call: Call,\n    url: HttpUrl,\n  ) {\n  }\n\n  /**\n   * Invoked after proxy selection.\n   *\n   * Note that the list of proxies is never null, but it may be a list containing\n   * only [Proxy.NO_PROXY]. This comes up in several situations:\n   *\n   * * If neither a proxy nor proxy selector is configured.\n   * * If the proxy is configured explicitly as [Proxy.NO_PROXY].\n   * * If the proxy selector returns only [Proxy.NO_PROXY].\n   * * If the proxy selector returns an empty list or null.\n   *\n   * Otherwise it lists the proxies in the order they will be attempted.\n   *\n   * @param url a URL with only the scheme, hostname, and port specified.\n   */\n  open fun proxySelectEnd(\n    call: Call,\n    url: HttpUrl,\n    proxies: List<@JvmSuppressWildcards Proxy>,\n  ) {\n  }\n\n  /**\n   * Invoked just prior to a DNS lookup. See [Dns.lookup].\n   *\n   * This can be invoked more than 1 time for a single [Call]. For example, if the response to the\n   * [Call.request] is a redirect to a different host.\n   *\n   * If the [Call] is able to reuse an existing pooled connection, this method will not be invoked.\n   * See [ConnectionPool].\n   */\n  open fun dnsStart(\n    call: Call,\n    domainName: String,\n  ) {\n  }\n\n  /**\n   * Invoked immediately after a DNS lookup.\n   *\n   * This method is invoked after [dnsStart].\n   */\n  open fun dnsEnd(\n    call: Call,\n    domainName: String,\n    inetAddressList: List<@JvmSuppressWildcards InetAddress>,\n  ) {\n  }\n\n  /**\n   * Invoked just prior to initiating a socket connection.\n   *\n   * This method will be invoked if no existing connection in the [ConnectionPool] can be reused.\n   *\n   * This can be invoked more than 1 time for a single [Call]. For example, if the response to the\n   * [Call.request] is a redirect to a different address, or a connection is retried.\n   */\n  open fun connectStart(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n  ) {\n  }\n\n  /**\n   * Invoked just prior to initiating a TLS connection.\n   *\n   * This method is invoked if the following conditions are met:\n   *\n   *  * The [Call.request] requires TLS.\n   *\n   *  * No existing connection from the [ConnectionPool] can be reused.\n   *\n   * This can be invoked more than 1 time for a single [Call]. For example, if the response to the\n   * [Call.request] is a redirect to a different address, or a connection is retried.\n   */\n  open fun secureConnectStart(call: Call) {\n  }\n\n  /**\n   * Invoked immediately after a TLS connection was attempted.\n   *\n   * This method is invoked after [secureConnectStart].\n   */\n  open fun secureConnectEnd(\n    call: Call,\n    handshake: Handshake?,\n  ) {\n  }\n\n  /**\n   * Invoked immediately after a socket connection was attempted.\n   *\n   * If the `call` uses HTTPS, this will be invoked after [secureConnectEnd], otherwise it will\n   * invoked after [connectStart].\n   */\n  open fun connectEnd(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n    protocol: Protocol?,\n  ) {\n  }\n\n  /**\n   * Invoked when a connection attempt fails. This failure is not terminal if further routes are\n   * available and failure recovery is enabled.\n   *\n   * If the `call` uses HTTPS, this will be invoked after [secureConnectStart], otherwise it will\n   * invoked after [connectStart].\n   */\n  open fun connectFailed(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n    protocol: Protocol?,\n    ioe: IOException,\n  ) {\n  }\n\n  /**\n   * Invoked after a connection has been acquired for the `call`.\n   *\n   * This can be invoked more than 1 time for a single [Call]. For example, if the response\n   * to the [Call.request] is a redirect to a different address.\n   */\n  open fun connectionAcquired(\n    call: Call,\n    connection: Connection,\n  ) {\n  }\n\n  /**\n   * Invoked after a connection has been released for the `call`.\n   *\n   * This method is always invoked after [connectionAcquired].\n   *\n   * This can be invoked more than 1 time for a single [Call]. For example, if the response to the\n   * [Call.request] is a redirect to a different address.\n   */\n  open fun connectionReleased(\n    call: Call,\n    connection: Connection,\n  ) {\n  }\n\n  /**\n   * Invoked just prior to sending request headers.\n   *\n   * The connection is implicit, and will generally relate to the last [connectionAcquired] event.\n   *\n   * This can be invoked more than 1 time for a single [Call]. For example, if the response to the\n   * [Call.request] is a redirect to a different address.\n   */\n  open fun requestHeadersStart(call: Call) {\n  }\n\n  /**\n   * Invoked immediately after sending request headers.\n   *\n   * This method is always invoked after [requestHeadersStart].\n   *\n   * @param request the request sent over the network. It is an error to access the body of this\n   *     request.\n   */\n  open fun requestHeadersEnd(\n    call: Call,\n    request: Request,\n  ) {\n  }\n\n  /**\n   * Invoked just prior to sending a request body.  Will only be invoked for request allowing and\n   * having a request body to send.\n   *\n   * The connection is implicit, and will generally relate to the last [connectionAcquired] event.\n   *\n   * This can be invoked more than 1 time for a single [Call]. For example, if the response to the\n   * [Call.request] is a redirect to a different address.\n   */\n  open fun requestBodyStart(call: Call) {\n  }\n\n  /**\n   * Invoked immediately after sending a request body.\n   *\n   * This method is always invoked after [requestBodyStart].\n   */\n  open fun requestBodyEnd(\n    call: Call,\n    byteCount: Long,\n  ) {\n  }\n\n  /**\n   * Invoked when a request fails to be written.\n   *\n   * This method is invoked after [requestHeadersStart] or [requestBodyStart]. Note that request\n   * failures do not necessarily fail the entire call.\n   */\n  open fun requestFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n  }\n\n  /**\n   * Invoked when response headers are first returned from the server.\n   *\n   * The connection is implicit, and will generally relate to the last [connectionAcquired] event.\n   *\n   * This can be invoked more than 1 time for a single [Call]. For example, if the response to the\n   * [Call.request] is a redirect to a different address.\n   *\n   * Prior to OkHttp 4.3 this was incorrectly invoked when the client was ready to read headers.\n   * This was misleading for tracing because it was too early.\n   */\n  open fun responseHeadersStart(call: Call) {\n  }\n\n  /**\n   * Invoked immediately after receiving response headers.\n   *\n   * This method is always invoked after [responseHeadersStart].\n   *\n   * @param response the response received over the network. It is an error to access the body of\n   *     this response.\n   */\n  open fun responseHeadersEnd(\n    call: Call,\n    response: Response,\n  ) {\n  }\n\n  /**\n   * Invoked when data from the response body is first available to the application.\n   *\n   * This is typically invoked immediately before bytes are returned to the application. If the\n   * response body is empty this is invoked immediately before returning that to the application.\n   *\n   * If the application closes the response body before attempting a read, this is invoked at the\n   * time it is closed.\n   *\n   * The connection is implicit, and will generally relate to the last [connectionAcquired] event.\n   *\n   * This will usually be invoked only 1 time for a single [Call], exceptions are a limited set of\n   * cases including failure recovery.\n   *\n   * Prior to OkHttp 4.3 this was incorrectly invoked when the client was ready to read the response\n   * body. This was misleading for tracing because it was too early.\n   */\n  open fun responseBodyStart(call: Call) {\n  }\n\n  /**\n   * Invoked immediately after receiving a response body and completing reading it.\n   *\n   * Will only be invoked for requests having a response body e.g. won't be invoked for a web socket\n   * upgrade.\n   *\n   * If the response body is closed before the response body is exhausted, this is invoked at the\n   * time it is closed. In such calls [byteCount] is the number of bytes returned to the\n   * application. This may be smaller than the resource's byte count if were read to completion.\n   *\n   * This method is always invoked after [responseBodyStart].\n   */\n  open fun responseBodyEnd(\n    call: Call,\n    byteCount: Long,\n  ) {\n  }\n\n  /**\n   * Invoked when a response fails to be read.\n   *\n   * Note that response failures do not necessarily fail the entire call.\n   *\n   * Starting with OkHttp 4.3 this may be invoked without a prior call to [responseHeadersStart]\n   * or [responseBodyStart]. In earlier releases this method was documented to only be invoked after\n   * one of those methods.\n   */\n  open fun responseFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n  }\n\n  /**\n   * Invoked immediately after a call has completely ended.  This includes delayed consumption\n   * of response body by the caller.\n   *\n   * This method is always invoked after [callStart].\n   */\n  open fun callEnd(call: Call) {\n  }\n\n  /**\n   * Invoked when a call fails permanently.\n   *\n   * This method is always invoked after [callStart].\n   */\n  open fun callFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n  }\n\n  /**\n   * Invoked when a call is canceled.\n   *\n   * Like all methods in this interface, this is invoked on the thread that triggered the event. But\n   * while other events occur sequentially; cancels may occur concurrently with other events. For\n   * example, thread A may be executing [responseBodyStart] while thread B executes [canceled].\n   * Implementations must support such concurrent calls.\n   *\n   * Note that cancellation is best-effort and that a call may proceed normally after it has been\n   * canceled. For example, happy-path events like [requestHeadersStart] and [requestHeadersEnd] may\n   * occur after a call is canceled. Typically cancellation takes effect when an expensive I/O\n   * operation is required.\n   *\n   * This is invoked at most once, even if [Call.cancel] is invoked multiple times. It may be\n   * invoked at any point in a call's life, including before [callStart] and after [callEnd].\n   */\n  open fun canceled(call: Call) {\n  }\n\n  /**\n   * Invoked when a call fails due to cache rules.\n   * For example, we're forbidden from using the network and the cache is insufficient\n   */\n  open fun satisfactionFailure(\n    call: Call,\n    response: Response,\n  ) {\n  }\n\n  /**\n   * Invoked when a result is served from the cache. The Response provided is the top level\n   * Response and normal event sequences will not be received.\n   *\n   * This event will only be received when a Cache is configured for the client.\n   */\n  open fun cacheHit(\n    call: Call,\n    response: Response,\n  ) {\n  }\n\n  /**\n   * Invoked when a response will be served from the network. The Response will be\n   * available from normal event sequences.\n   *\n   * This event will only be received when a Cache is configured for the client.\n   */\n  open fun cacheMiss(call: Call) {\n  }\n\n  /**\n   * Invoked when a response will be served from the cache or network based on validating the\n   * cached Response freshness. Will be followed by cacheHit or cacheMiss after the network\n   * Response is available.\n   *\n   * This event will only be received when a Cache is configured for the client.\n   */\n  open fun cacheConditionalHit(\n    call: Call,\n    cachedResponse: Response,\n  ) {\n  }\n\n  /**\n   * Invoked when OkHttp decides whether to retry after a connectivity failure.\n   *\n   * OkHttp won't retry when it is configured not to:\n   *\n   *  * If retries are forbidden with [OkHttpClient.retryOnConnectionFailure]. (OkHttp's defaults\n   *    permit retries.)\n   *  * If OkHttp already attempted to transmit the request body, and [RequestBody.isOneShot] is\n   *    true.\n   *\n   * It won't retry if the exception is a bug or a configuration problem, such as:\n   *\n   *  * If the remote peer is untrusted: [exception] is an [SSLPeerUnverifiedException].\n   *  * If received data is unexpected: [exception] is a [ProtocolException].\n   *\n   * Each call is made on either a reused [Connection] from a pool, or on a new connection\n   * established from a planned [Route]. OkHttp won't retry if it's already attempted all\n   * available routes.\n   *\n   * @param retry true if OkHttp will make another attempt\n   */\n  open fun retryDecision(\n    call: Call,\n    exception: IOException,\n    retry: Boolean,\n  ) {\n  }\n\n  /**\n   * Invoked when OkHttp decides whether to perform a follow-up request.\n   *\n   * The network response's status code is most influential when deciding how to follow up:\n   *\n   *  * For redirects (301: Moved Permanently, 302: Temporary Redirect, etc.)\n   *  * For auth challenges (401: Unauthorized, 407: Proxy Authentication Required.)\n   *  * For client timeouts (408: Request Time-Out.)\n   *  * For server failures (503: Service Unavailable.)\n   *\n   * Response header values like `Location` and `Retry-After` are also considered.\n   *\n   * Client configuration may be used to make follow-up decisions, such as:\n   *\n   *  * [OkHttpClient.followRedirects] must be true to follow redirects.\n   *  * [OkHttpClient.followSslRedirects] must be true to follow redirects that add or remove HTTPS.\n   *  * [OkHttpClient.authenticator] must respond to an authorization challenge.\n   *\n   * @param networkResponse the intermediate response that may require a follow-up request.\n   * @param nextRequest the follow-up request that will be made. Null if no follow-up will be made.\n   */\n  open fun followUpDecision(\n    call: Call,\n    networkResponse: Response,\n    nextRequest: Request?,\n  ) {\n  }\n\n  /** Returns a new `EventListener` that publishes events to this and then `other`. */\n  operator fun plus(other: EventListener): EventListener {\n    val left =\n      when {\n        this === NONE -> return other\n        this is AggregateEventListener -> this.eventListeners\n        else -> arrayOf(this)\n      }\n    val right =\n      when {\n        other === NONE -> return this\n        other is AggregateEventListener -> other.eventListeners\n        else -> arrayOf(other)\n      }\n\n    return AggregateEventListener(left + right)\n  }\n\n  fun interface Factory {\n    /**\n     * Creates an instance of the [EventListener] for a particular [Call]. The returned\n     * [EventListener] instance will be used during the lifecycle of [call].\n     *\n     * This method is invoked after [call] is created. See [OkHttpClient.newCall].\n     *\n     * **It is an error for implementations to issue any mutating operations on the [call] instance\n     * from this method.**\n     */\n    fun create(call: Call): EventListener\n  }\n\n  companion object {\n    @JvmField\n    val NONE: EventListener =\n      object : EventListener() {\n      }\n  }\n\n  private class AggregateEventListener(\n    val eventListeners: Array<EventListener>,\n  ) : EventListener() {\n    override fun callStart(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.callStart(call)\n      }\n    }\n\n    override fun dispatcherQueueStart(\n      call: Call,\n      dispatcher: Dispatcher,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.dispatcherQueueStart(call, dispatcher)\n      }\n    }\n\n    override fun dispatcherQueueEnd(\n      call: Call,\n      dispatcher: Dispatcher,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.dispatcherQueueEnd(call, dispatcher)\n      }\n    }\n\n    override fun proxySelectStart(\n      call: Call,\n      url: HttpUrl,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.proxySelectStart(call, url)\n      }\n    }\n\n    override fun proxySelectEnd(\n      call: Call,\n      url: HttpUrl,\n      proxies: List<@JvmSuppressWildcards Proxy>,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.proxySelectEnd(call, url, proxies)\n      }\n    }\n\n    override fun dnsStart(\n      call: Call,\n      domainName: String,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.dnsStart(call, domainName)\n      }\n    }\n\n    override fun dnsEnd(\n      call: Call,\n      domainName: String,\n      inetAddressList: List<@JvmSuppressWildcards InetAddress>,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.dnsEnd(call, domainName, inetAddressList)\n      }\n    }\n\n    override fun connectStart(\n      call: Call,\n      inetSocketAddress: InetSocketAddress,\n      proxy: Proxy,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.connectStart(call, inetSocketAddress, proxy)\n      }\n    }\n\n    override fun secureConnectStart(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.secureConnectStart(call)\n      }\n    }\n\n    override fun secureConnectEnd(\n      call: Call,\n      handshake: Handshake?,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.secureConnectEnd(call, handshake)\n      }\n    }\n\n    override fun connectEnd(\n      call: Call,\n      inetSocketAddress: InetSocketAddress,\n      proxy: Proxy,\n      protocol: Protocol?,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.connectEnd(call, inetSocketAddress, proxy, protocol)\n      }\n    }\n\n    override fun connectFailed(\n      call: Call,\n      inetSocketAddress: InetSocketAddress,\n      proxy: Proxy,\n      protocol: Protocol?,\n      ioe: IOException,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.connectFailed(call, inetSocketAddress, proxy, protocol, ioe)\n      }\n    }\n\n    override fun connectionAcquired(\n      call: Call,\n      connection: Connection,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.connectionAcquired(call, connection)\n      }\n    }\n\n    override fun connectionReleased(\n      call: Call,\n      connection: Connection,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.connectionReleased(call, connection)\n      }\n    }\n\n    override fun requestHeadersStart(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.requestHeadersStart(call)\n      }\n    }\n\n    override fun requestHeadersEnd(\n      call: Call,\n      request: Request,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.requestHeadersEnd(call, request)\n      }\n    }\n\n    override fun requestBodyStart(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.requestBodyStart(call)\n      }\n    }\n\n    override fun requestBodyEnd(\n      call: Call,\n      byteCount: Long,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.requestBodyEnd(call, byteCount)\n      }\n    }\n\n    override fun requestFailed(\n      call: Call,\n      ioe: IOException,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.requestFailed(call, ioe)\n      }\n    }\n\n    override fun responseHeadersStart(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.responseHeadersStart(call)\n      }\n    }\n\n    override fun responseHeadersEnd(\n      call: Call,\n      response: Response,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.responseHeadersEnd(call, response)\n      }\n    }\n\n    override fun responseBodyStart(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.responseBodyStart(call)\n      }\n    }\n\n    override fun responseBodyEnd(\n      call: Call,\n      byteCount: Long,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.responseBodyEnd(call, byteCount)\n      }\n    }\n\n    override fun responseFailed(\n      call: Call,\n      ioe: IOException,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.responseFailed(call, ioe)\n      }\n    }\n\n    override fun callEnd(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.callEnd(call)\n      }\n    }\n\n    override fun callFailed(\n      call: Call,\n      ioe: IOException,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.callFailed(call, ioe)\n      }\n    }\n\n    override fun canceled(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.canceled(call)\n      }\n    }\n\n    override fun satisfactionFailure(\n      call: Call,\n      response: Response,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.satisfactionFailure(call, response)\n      }\n    }\n\n    override fun cacheHit(\n      call: Call,\n      response: Response,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.cacheHit(call, response)\n      }\n    }\n\n    override fun cacheMiss(call: Call) {\n      for (delegate in eventListeners) {\n        delegate.cacheMiss(call)\n      }\n    }\n\n    override fun cacheConditionalHit(\n      call: Call,\n      cachedResponse: Response,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.cacheConditionalHit(call, cachedResponse)\n      }\n    }\n\n    override fun retryDecision(\n      call: Call,\n      exception: IOException,\n      retry: Boolean,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.retryDecision(call, exception, retry)\n      }\n    }\n\n    override fun followUpDecision(\n      call: Call,\n      networkResponse: Response,\n      nextRequest: Request?,\n    ) {\n      for (delegate in eventListeners) {\n        delegate.followUpDecision(call, networkResponse, nextRequest)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/FormBody.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.nio.charset.Charset\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.internal.toImmutableList\nimport okhttp3.internal.url.FORM_ENCODE_SET\nimport okhttp3.internal.url.canonicalizeWithCharset\nimport okhttp3.internal.url.percentDecode\nimport okio.Buffer\nimport okio.BufferedSink\n\nclass FormBody internal constructor(\n  encodedNames: List<String>,\n  encodedValues: List<String>,\n) : RequestBody() {\n  private val encodedNames: List<String> = encodedNames.toImmutableList()\n  private val encodedValues: List<String> = encodedValues.toImmutableList()\n\n  /** The number of key-value pairs in this form-encoded body. */\n  @get:JvmName(\"size\")\n  val size: Int\n    get() = encodedNames.size\n\n  @JvmName(\"-deprecated_size\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"size\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun size(): Int = size\n\n  fun encodedName(index: Int): String = encodedNames[index]\n\n  fun name(index: Int): String = encodedName(index).percentDecode(plusIsSpace = true)\n\n  fun encodedValue(index: Int): String = encodedValues[index]\n\n  fun value(index: Int): String = encodedValue(index).percentDecode(plusIsSpace = true)\n\n  override fun contentType(): MediaType = CONTENT_TYPE\n\n  override fun contentLength(): Long = writeOrCountBytes(null, true)\n\n  @Throws(IOException::class)\n  override fun writeTo(sink: BufferedSink) {\n    writeOrCountBytes(sink, false)\n  }\n\n  /**\n   * Either writes this request to [sink] or measures its content length. We have one method\n   * do double-duty to make sure the counting and content are consistent, particularly when it comes\n   * to awkward operations like measuring the encoded length of header strings, or the\n   * length-in-digits of an encoded integer.\n   */\n  private fun writeOrCountBytes(\n    sink: BufferedSink?,\n    countBytes: Boolean,\n  ): Long {\n    var byteCount = 0L\n    val buffer: Buffer = if (countBytes) Buffer() else sink!!.buffer\n\n    for (i in 0 until encodedNames.size) {\n      if (i > 0) buffer.writeByte('&'.code)\n      buffer.writeUtf8(encodedNames[i])\n      buffer.writeByte('='.code)\n      buffer.writeUtf8(encodedValues[i])\n    }\n\n    if (countBytes) {\n      byteCount = buffer.size\n      buffer.clear()\n    }\n\n    return byteCount\n  }\n\n  class Builder\n    @JvmOverloads\n    constructor(\n      private val charset: Charset? = null,\n    ) {\n      private val names = mutableListOf<String>()\n      private val values = mutableListOf<String>()\n\n      fun add(\n        name: String,\n        value: String,\n      ) = apply {\n        names +=\n          name.canonicalizeWithCharset(\n            encodeSet = FORM_ENCODE_SET,\n            // Plus is encoded as `%2B`, space is encoded as plus.\n            plusIsSpace = false,\n            charset = charset,\n          )\n        values +=\n          value.canonicalizeWithCharset(\n            encodeSet = FORM_ENCODE_SET,\n            // Plus is encoded as `%2B`, space is encoded as plus.\n            plusIsSpace = false,\n            charset = charset,\n          )\n      }\n\n      fun addEncoded(\n        name: String,\n        value: String,\n      ) = apply {\n        names +=\n          name.canonicalizeWithCharset(\n            encodeSet = FORM_ENCODE_SET,\n            alreadyEncoded = true,\n            plusIsSpace = true,\n            charset = charset,\n          )\n        values +=\n          value.canonicalizeWithCharset(\n            encodeSet = FORM_ENCODE_SET,\n            alreadyEncoded = true,\n            plusIsSpace = true,\n            charset = charset,\n          )\n      }\n\n      fun build(): FormBody = FormBody(names, values)\n    }\n\n  companion object {\n    private val CONTENT_TYPE: MediaType = \"application/x-www-form-urlencoded\".toMediaType()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Gzip.kt",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okhttp3.CompressionInterceptor.DecompressionAlgorithm\nimport okio.BufferedSource\nimport okio.GzipSource\nimport okio.Source\n\nobject Gzip : DecompressionAlgorithm {\n  override val encoding: String get() = \"gzip\"\n\n  override fun decompress(compressedSource: BufferedSource): Source = GzipSource(compressedSource)\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Handshake.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.security.Principal\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSession\nimport okhttp3.internal.toImmutableList\n\n/**\n * A record of a TLS handshake. For HTTPS clients, the client is *local* and the remote server is\n * its *peer*.\n *\n * This value object describes a completed handshake. Use [ConnectionSpec] to set policy for new\n * handshakes.\n */\nclass Handshake internal constructor(\n  /**\n   * Returns the TLS version used for this connection. This value wasn't tracked prior to OkHttp\n   * 3.0. For responses cached by preceding versions this returns [TlsVersion.SSL_3_0].\n   */\n  @get:JvmName(\"tlsVersion\") val tlsVersion: TlsVersion,\n  /** Returns the cipher suite used for the connection. */\n  @get:JvmName(\"cipherSuite\") val cipherSuite: CipherSuite,\n  /** Returns a possibly-empty list of certificates that identify this peer. */\n  @get:JvmName(\"localCertificates\") val localCertificates: List<Certificate>,\n  // Delayed provider of peerCertificates, to allow lazy cleaning.\n  peerCertificatesFn: () -> List<Certificate>,\n) {\n  /** Returns a possibly-empty list of certificates that identify the remote peer. */\n  @get:JvmName(\"peerCertificates\")\n  val peerCertificates: List<Certificate> by lazy {\n    try {\n      peerCertificatesFn()\n    } catch (spue: SSLPeerUnverifiedException) {\n      listOf()\n    }\n  }\n\n  @JvmName(\"-deprecated_tlsVersion\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"tlsVersion\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun tlsVersion(): TlsVersion = tlsVersion\n\n  @JvmName(\"-deprecated_cipherSuite\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"cipherSuite\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun cipherSuite(): CipherSuite = cipherSuite\n\n  @JvmName(\"-deprecated_peerCertificates\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"peerCertificates\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun peerCertificates(): List<Certificate> = peerCertificates\n\n  /** Returns the remote peer's principle, or null if that peer is anonymous. */\n  @get:JvmName(\"peerPrincipal\")\n  val peerPrincipal: Principal?\n    get() = (peerCertificates.firstOrNull() as? X509Certificate)?.subjectX500Principal\n\n  @JvmName(\"-deprecated_peerPrincipal\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"peerPrincipal\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun peerPrincipal(): Principal? = peerPrincipal\n\n  @JvmName(\"-deprecated_localCertificates\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"localCertificates\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun localCertificates(): List<Certificate> = localCertificates\n\n  /** Returns the local principle, or null if this peer is anonymous. */\n  @get:JvmName(\"localPrincipal\")\n  val localPrincipal: Principal?\n    get() = (localCertificates.firstOrNull() as? X509Certificate)?.subjectX500Principal\n\n  @JvmName(\"-deprecated_localPrincipal\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"localPrincipal\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun localPrincipal(): Principal? = localPrincipal\n\n  override fun equals(other: Any?): Boolean =\n    other is Handshake &&\n      other.tlsVersion == tlsVersion &&\n      other.cipherSuite == cipherSuite &&\n      other.peerCertificates == peerCertificates &&\n      other.localCertificates == localCertificates\n\n  override fun hashCode(): Int {\n    var result = 17\n    result = 31 * result + tlsVersion.hashCode()\n    result = 31 * result + cipherSuite.hashCode()\n    result = 31 * result + peerCertificates.hashCode()\n    result = 31 * result + localCertificates.hashCode()\n    return result\n  }\n\n  override fun toString(): String {\n    val peerCertificatesString = peerCertificates.map { it.name }.toString()\n    return \"Handshake{\" +\n      \"tlsVersion=$tlsVersion \" +\n      \"cipherSuite=$cipherSuite \" +\n      \"peerCertificates=$peerCertificatesString \" +\n      \"localCertificates=${localCertificates.map { it.name }}}\"\n  }\n\n  private val Certificate.name: String\n    get() =\n      when (this) {\n        is X509Certificate -> subjectDN.toString()\n        else -> type\n      }\n\n  companion object {\n    @Throws(IOException::class)\n    @JvmStatic\n    @JvmName(\"get\")\n    fun SSLSession.handshake(): Handshake {\n      val cipherSuite =\n        when (val cipherSuiteString = checkNotNull(cipherSuite) { \"cipherSuite == null\" }) {\n          \"TLS_NULL_WITH_NULL_NULL\", \"SSL_NULL_WITH_NULL_NULL\" -> {\n            throw IOException(\"cipherSuite == $cipherSuiteString\")\n          }\n\n          else -> {\n            CipherSuite.forJavaName(cipherSuiteString)\n          }\n        }\n\n      val tlsVersionString = checkNotNull(protocol) { \"tlsVersion == null\" }\n      if (\"NONE\" == tlsVersionString) throw IOException(\"tlsVersion == NONE\")\n      val tlsVersion = TlsVersion.forJavaName(tlsVersionString)\n\n      val peerCertificatesCopy =\n        try {\n          peerCertificates.toImmutableList()\n        } catch (_: SSLPeerUnverifiedException) {\n          listOf()\n        }\n\n      return Handshake(\n        tlsVersion,\n        cipherSuite,\n        localCertificates.toImmutableList(),\n      ) { peerCertificatesCopy }\n    }\n\n    @Throws(IOException::class)\n    @JvmName(\"-deprecated_get\")\n    @Deprecated(\n      message = \"moved to extension function\",\n      replaceWith = ReplaceWith(expression = \"sslSession.handshake()\"),\n      level = DeprecationLevel.ERROR,\n    )\n    fun get(sslSession: SSLSession) = sslSession.handshake()\n\n    @JvmStatic\n    fun get(\n      tlsVersion: TlsVersion,\n      cipherSuite: CipherSuite,\n      peerCertificates: List<Certificate>,\n      localCertificates: List<Certificate>,\n    ): Handshake {\n      val peerCertificatesCopy = peerCertificates.toImmutableList()\n      return Handshake(tlsVersion, cipherSuite, localCertificates.toImmutableList()) {\n        peerCertificatesCopy\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Headers.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage okhttp3\n\nimport java.time.Instant\nimport java.util.Date\nimport java.util.Locale\nimport java.util.TreeMap\nimport java.util.TreeSet\nimport okhttp3.internal.commonAdd\nimport okhttp3.internal.commonAddAll\nimport okhttp3.internal.commonAddLenient\nimport okhttp3.internal.commonBuild\nimport okhttp3.internal.commonEquals\nimport okhttp3.internal.commonGet\nimport okhttp3.internal.commonHashCode\nimport okhttp3.internal.commonHeadersGet\nimport okhttp3.internal.commonHeadersOf\nimport okhttp3.internal.commonIterator\nimport okhttp3.internal.commonName\nimport okhttp3.internal.commonNewBuilder\nimport okhttp3.internal.commonRemoveAll\nimport okhttp3.internal.commonSet\nimport okhttp3.internal.commonToHeaders\nimport okhttp3.internal.commonToString\nimport okhttp3.internal.commonValue\nimport okhttp3.internal.commonValues\nimport okhttp3.internal.headersCheckName\nimport okhttp3.internal.http.toHttpDateOrNull\nimport okhttp3.internal.http.toHttpDateString\nimport okhttp3.internal.unmodifiable\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n\n/**\n * The header fields of a single HTTP message. Values are uninterpreted strings; use `Request` and\n * `Response` for interpreted headers. This class maintains the order of the header fields within\n * the HTTP message.\n *\n * This class tracks header values line-by-line. A field with multiple comma- separated values on\n * the same line will be treated as a field with a single value by this class. It is the caller's\n * responsibility to detect and split on commas if their field permits multiple values. This\n * simplifies use of single-valued fields whose values routinely contain commas, such as cookies or\n * dates.\n *\n * This class trims whitespace from values. It never returns values with leading or trailing\n * whitespace.\n *\n * Instances of this class are immutable. Use [Builder] to create instances.\n */\n@Suppress(\"NAME_SHADOWING\")\nclass Headers internal constructor(\n  internal val namesAndValues: Array<String>,\n) : Iterable<Pair<String, String>> {\n  /** Returns the last value corresponding to the specified field, or null. */\n  operator fun get(name: String): String? = commonHeadersGet(namesAndValues, name)\n\n  /**\n   * Returns the last value corresponding to the specified field parsed as an HTTP date, or null if\n   * either the field is absent or cannot be parsed as a date.\n   */\n  fun getDate(name: String): Date? = get(name)?.toHttpDateOrNull()\n\n  /**\n   * Returns the last value corresponding to the specified field parsed as an HTTP date, or null if\n   * either the field is absent or cannot be parsed as a date.\n   */\n  @Suppress(\"NewApi\")\n  @IgnoreJRERequirement // Only programs that already have Instant will use this.\n  fun getInstant(name: String): Instant? = getDate(name)?.toInstant()\n\n  /** Returns the number of field values. */\n  @get:JvmName(\"size\")\n  val size: Int\n    get() = namesAndValues.size / 2\n\n  @JvmName(\"-deprecated_size\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"size\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun size(): Int = size\n\n  /** Returns the field at `position`. */\n  fun name(index: Int): String = commonName(index)\n\n  /** Returns the value at `index`. */\n  fun value(index: Int): String = commonValue(index)\n\n  /** Returns an immutable case-insensitive set of header names. */\n  fun names(): Set<String> {\n    val result = TreeSet(String.CASE_INSENSITIVE_ORDER)\n    for (i in 0 until size) {\n      result.add(name(i))\n    }\n    return result.unmodifiable()\n  }\n\n  /** Returns an immutable list of the header values for `name`. */\n  fun values(name: String): List<String> = commonValues(name)\n\n  /**\n   * Returns the number of bytes required to encode these headers using HTTP/1.1. This is also the\n   * approximate size of HTTP/2 headers before they are compressed with HPACK. This value is\n   * intended to be used as a metric: smaller headers are more efficient to encode and transmit.\n   */\n  fun byteCount(): Long {\n    // Each header name has 2 bytes of overhead for ': ' and every header value has 2 bytes of\n    // overhead for '\\r\\n'.\n    var result = (namesAndValues.size * 2).toLong()\n\n    for (i in 0 until namesAndValues.size) {\n      result += namesAndValues[i].length.toLong()\n    }\n\n    return result\n  }\n\n  override operator fun iterator(): Iterator<Pair<String, String>> = commonIterator()\n\n  fun newBuilder(): Builder = commonNewBuilder()\n\n  /**\n   * Returns true if `other` is a `Headers` object with the same headers, with the same casing, in\n   * the same order. Note that two headers instances may be *semantically* equal but not equal\n   * according to this method. In particular, none of the following sets of headers are equal\n   * according to this method:\n   *\n   * 1. Original\n   * ```\n   * Content-Type: text/html\n   * Content-Length: 50\n   * ```\n   *\n   * 2. Different order\n   *\n   * ```\n   * Content-Length: 50\n   * Content-Type: text/html\n   * ```\n   *\n   * 3. Different case\n   *\n   * ```\n   * content-type: text/html\n   * content-length: 50\n   * ```\n   *\n   * 4. Different values\n   *\n   * ```\n   * Content-Type: text/html\n   * Content-Length: 050\n   * ```\n   *\n   * Applications that require semantically equal headers should convert them into a canonical form\n   * before comparing them for equality.\n   */\n  override fun equals(other: Any?): Boolean = commonEquals(other)\n\n  override fun hashCode(): Int = commonHashCode()\n\n  /**\n   * Returns header names and values. The names and values are separated by `: ` and each pair is\n   * followed by a newline character `\\n`.\n   *\n   * Since OkHttp 5 this redacts these sensitive headers:\n   *\n   *  * `Authorization`\n   *  * `Cookie`\n   *  * `Proxy-Authorization`\n   *  * `Set-Cookie`\n   */\n  override fun toString(): String = commonToString()\n\n  fun toMultimap(): Map<String, List<String>> {\n    val result = TreeMap<String, MutableList<String>>(String.CASE_INSENSITIVE_ORDER)\n    for (i in 0 until size) {\n      val name = name(i).lowercase(Locale.US)\n      var values: MutableList<String>? = result[name]\n      if (values == null) {\n        values = ArrayList(2)\n        result[name] = values\n      }\n      values.add(value(i))\n    }\n    return result\n  }\n\n  class Builder {\n    internal val namesAndValues: MutableList<String> = ArrayList(20)\n\n    /**\n     * Add a header line without any validation. Only appropriate for headers from the remote peer\n     * or cache.\n     */\n    internal fun addLenient(line: String) =\n      apply {\n        val index = line.indexOf(':', 1)\n        when {\n          index != -1 -> {\n            addLenient(line.substring(0, index), line.substring(index + 1))\n          }\n\n          line[0] == ':' -> {\n            // Work around empty header names and header names that start with a colon (created by old\n            // broken SPDY versions of the response cache).\n            addLenient(\"\", line.substring(1)) // Empty header name.\n          }\n\n          else -> {\n            // No header name.\n            addLenient(\"\", line)\n          }\n        }\n      }\n\n    /** Add an header line containing a field name, a literal colon, and a value. */\n    fun add(line: String) =\n      apply {\n        val index = line.indexOf(':')\n        require(index != -1) { \"Unexpected header: $line\" }\n        add(line.substring(0, index).trim(), line.substring(index + 1))\n      }\n\n    /**\n     * Add a header with the specified name and value. Does validation of header names and values.\n     */\n    fun add(\n      name: String,\n      value: String,\n    ) = commonAdd(name, value)\n\n    /**\n     * Add a header with the specified name and value. Does validation of header names, allowing\n     * non-ASCII values.\n     */\n    fun addUnsafeNonAscii(\n      name: String,\n      value: String,\n    ) = apply {\n      headersCheckName(name)\n      addLenient(name, value)\n    }\n\n    /**\n     * Adds all headers from an existing collection.\n     */\n    fun addAll(headers: Headers) = commonAddAll(headers)\n\n    /**\n     * Add a header with the specified name and formatted date. Does validation of header names and\n     * value.\n     */\n    fun add(\n      name: String,\n      value: Date,\n    ) = add(name, value.toHttpDateString())\n\n    /**\n     * Add a header with the specified name and formatted instant. Does validation of header names\n     * and value.\n     */\n    @Suppress(\"NewApi\")\n    @IgnoreJRERequirement // Only programs that already have Instant will use this.\n    fun add(\n      name: String,\n      value: Instant,\n    ) = add(name, Date.from(value))\n\n    /**\n     * Set a field with the specified date. If the field is not found, it is added. If the field is\n     * found, the existing values are replaced.\n     */\n    operator fun set(\n      name: String,\n      value: Date,\n    ) = set(name, value.toHttpDateString())\n\n    /**\n     * Set a field with the specified instant. If the field is not found, it is added. If the field\n     * is found, the existing values are replaced.\n     */\n    @Suppress(\"NewApi\")\n    @IgnoreJRERequirement // Only programs that already have Instant will use this.\n    operator fun set(name: String, value: Instant) = set(name, Date.from(value))\n\n    /**\n     * Add a field with the specified value without any validation. Only appropriate for headers\n     * from the remote peer or cache.\n     */\n    internal fun addLenient(\n      name: String,\n      value: String,\n    ) = commonAddLenient(name, value)\n\n    fun removeAll(name: String) = commonRemoveAll(name)\n\n    /**\n     * Set a field with the specified value. If the field is not found, it is added. If the field is\n     * found, the existing values are replaced.\n     */\n    operator fun set(\n      name: String,\n      value: String,\n    ) = commonSet(name, value)\n\n    /** Equivalent to `build().get(name)`, but potentially faster. */\n    operator fun get(name: String): String? = commonGet(name)\n\n    fun build(): Headers = commonBuild()\n  }\n\n  companion object {\n    /** Empty headers. */\n    @JvmField\n    val EMPTY = Headers(emptyArray())\n\n    /**\n     * Returns headers for the alternating header names and values. There must be an even number of\n     * arguments, and they must alternate between header names and values.\n     */\n    @JvmStatic\n    @JvmName(\"of\")\n    fun headersOf(vararg namesAndValues: String): Headers = commonHeadersOf(*namesAndValues)\n\n    @JvmName(\"-deprecated_of\")\n    @Deprecated(\n      message = \"function name changed\",\n      replaceWith = ReplaceWith(expression = \"headersOf(*namesAndValues)\"),\n      level = DeprecationLevel.ERROR,\n    )\n    fun of(vararg namesAndValues: String): Headers = headersOf(*namesAndValues)\n\n    /** Returns headers for the header names and values in the [Map]. */\n    @JvmStatic\n    @JvmName(\"of\")\n    fun Map<String, String>.toHeaders(): Headers = commonToHeaders()\n\n    @JvmName(\"-deprecated_of\")\n    @Deprecated(\n      message = \"function moved to extension\",\n      replaceWith = ReplaceWith(expression = \"headers.toHeaders()\"),\n      level = DeprecationLevel.ERROR,\n    )\n    fun of(headers: Map<String, String>): Headers = headers.toHeaders()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/HttpUrl.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.net.MalformedURLException\nimport java.net.URI\nimport java.net.URISyntaxException\nimport java.net.URL\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okhttp3.internal.canParseAsIpAddress\nimport okhttp3.internal.delimiterOffset\nimport okhttp3.internal.indexOfFirstNonAsciiWhitespace\nimport okhttp3.internal.indexOfLastNonAsciiWhitespace\nimport okhttp3.internal.publicsuffix.PublicSuffixDatabase\nimport okhttp3.internal.toCanonicalHost\nimport okhttp3.internal.unmodifiable\nimport okhttp3.internal.url.FRAGMENT_ENCODE_SET\nimport okhttp3.internal.url.FRAGMENT_ENCODE_SET_URI\nimport okhttp3.internal.url.PASSWORD_ENCODE_SET\nimport okhttp3.internal.url.PATH_SEGMENT_ENCODE_SET\nimport okhttp3.internal.url.PATH_SEGMENT_ENCODE_SET_URI\nimport okhttp3.internal.url.QUERY_COMPONENT_ENCODE_SET\nimport okhttp3.internal.url.QUERY_COMPONENT_ENCODE_SET_URI\nimport okhttp3.internal.url.QUERY_COMPONENT_REENCODE_SET\nimport okhttp3.internal.url.QUERY_ENCODE_SET\nimport okhttp3.internal.url.USERNAME_ENCODE_SET\nimport okhttp3.internal.url.canonicalize\nimport okhttp3.internal.url.percentDecode\n\n/**\n * A uniform resource locator (URL) with a scheme of either `http` or `https`. Use this class to\n * compose and decompose Internet addresses. For example, this code will compose and print a URL for\n * Google search:\n *\n * ```java\n * HttpUrl url = new HttpUrl.Builder()\n *     .scheme(\"https\")\n *     .host(\"www.google.com\")\n *     .addPathSegment(\"search\")\n *     .addQueryParameter(\"q\", \"polar bears\")\n *     .build();\n * System.out.println(url);\n * ```\n *\n * which prints:\n *\n * ```\n * https://www.google.com/search?q=polar%20bears\n * ```\n *\n * As another example, this code prints the human-readable query parameters of a Twitter search:\n *\n * ```java\n * HttpUrl url = HttpUrl.parse(\"https://twitter.com/search?q=cute%20%23puppies&f=images\");\n * for (int i = 0, size = url.querySize(); i < size; i++) {\n *   System.out.println(url.queryParameterName(i) + \": \" + url.queryParameterValue(i));\n * }\n * ```\n *\n * which prints:\n *\n * ```\n * q: cute #puppies\n * f: images\n * ```\n *\n * In addition to composing URLs from their component parts and decomposing URLs into their\n * component parts, this class implements relative URL resolution: what address you'd reach by\n * clicking a relative link on a specified page. For example:\n *\n * ```java\n * HttpUrl base = HttpUrl.parse(\"https://www.youtube.com/user/WatchTheDaily/videos\");\n * HttpUrl link = base.resolve(\"../../watch?v=cbP2N1BQdYc\");\n * System.out.println(link);\n * ```\n *\n * which prints:\n *\n * ```\n * https://www.youtube.com/watch?v=cbP2N1BQdYc\n * ```\n *\n * ## What's in a URL?\n *\n * A URL has several components.\n *\n * ### Scheme\n *\n * Sometimes referred to as *protocol*, A URL's scheme describes what mechanism should be used to\n * retrieve the resource. Although URLs have many schemes (`mailto`, `file`, `ftp`), this class only\n * supports `http` and `https`. Use [java.net.URI][URI] for URLs with arbitrary schemes.\n *\n * ### Username and Password\n *\n * Username and password are either present, or the empty string `\"\"` if absent. This class offers\n * no mechanism to differentiate empty from absent. Neither of these components are popular in\n * practice. Typically HTTP applications use other mechanisms for user identification and\n * authentication.\n *\n * ### Host\n *\n * The host identifies the webserver that serves the URL's resource. It is either a hostname like\n * `square.com` or `localhost`, an IPv4 address like `192.168.0.1`, or an IPv6 address like `::1`.\n *\n * Usually a webserver is reachable with multiple identifiers: its IP addresses, registered\n * domain names, and even `localhost` when connecting from the server itself. Each of a web server's\n * names is a distinct URL and they are not interchangeable. For example, even if\n * `http://square.github.io/dagger` and `http://google.github.io/dagger` are served by the same IP\n * address, the two URLs identify different resources.\n *\n * ### Port\n *\n * The port used to connect to the web server. By default this is 80 for HTTP and 443 for HTTPS.\n * This class never returns -1 for the port: if no port is explicitly specified in the URL then the\n * scheme's default is used.\n *\n * ### Path\n *\n * The path identifies a specific resource on the host. Paths have a hierarchical structure like\n * \"/square/okhttp/issues/1486\" and decompose into a list of segments like `[\"square\", \"okhttp\",\n * \"issues\", \"1486\"]`.\n *\n * This class offers methods to compose and decompose paths by segment. It composes each path\n * from a list of segments by alternating between \"/\" and the encoded segment. For example the\n * segments `[\"a\", \"b\"]` build \"/a/b\" and the segments `[\"a\", \"b\", \"\"]` build \"/a/b/\".\n *\n * If a path's last segment is the empty string then the path ends with \"/\". This class always\n * builds non-empty paths: if the path is omitted it defaults to \"/\". The default path's segment\n * list is a single empty string: `[\"\"]`.\n *\n * ### Query\n *\n * The query is optional: it can be null, empty, or non-empty. For many HTTP URLs the query string\n * is subdivided into a collection of name-value parameters. This class offers methods to set the\n * query as the single string, or as individual name-value parameters. With name-value parameters\n * the values are optional and names may be repeated.\n *\n * ### Fragment\n *\n * The fragment is optional: it can be null, empty, or non-empty. Unlike host, port, path, and\n * query the fragment is not sent to the webserver: it's private to the client.\n *\n * ## Encoding\n *\n * Each component must be encoded before it is embedded in the complete URL. As we saw above, the\n * string `cute #puppies` is encoded as `cute%20%23puppies` when used as a query parameter value.\n *\n * ### Percent encoding\n *\n * Percent encoding replaces a character (like `\\ud83c\\udf69`) with its UTF-8 hex bytes (like\n * `%F0%9F%8D%A9`). This approach works for whitespace characters, control characters, non-ASCII\n * characters, and characters that already have another meaning in a particular context.\n *\n * Percent encoding is used in every URL component except for the hostname. But the set of\n * characters that need to be encoded is different for each component. For example, the path\n * component must escape all of its `?` characters, otherwise it could be interpreted as the\n * start of the URL's query. But within the query and fragment components, the `?` character\n * doesn't delimit anything and doesn't need to be escaped.\n *\n * ```java\n * HttpUrl url = HttpUrl.parse(\"http://who-let-the-dogs.out\").newBuilder()\n *     .addPathSegment(\"_Who?_\")\n *     .query(\"_Who?_\")\n *     .fragment(\"_Who?_\")\n *     .build();\n * System.out.println(url);\n * ```\n *\n * This prints:\n *\n * ```\n * http://who-let-the-dogs.out/_Who%3F_?_Who?_#_Who?_\n * ```\n *\n * When parsing URLs that lack percent encoding where it is required, this class will percent encode\n * the offending characters.\n *\n * ### IDNA Mapping and Punycode encoding\n *\n * Hostnames have different requirements and use a different encoding scheme. It consists of IDNA\n * mapping and Punycode encoding.\n *\n * In order to avoid confusion and discourage phishing attacks, [IDNA Mapping][idna] transforms\n * names to avoid confusing characters. This includes basic case folding: transforming shouting\n * `SQUARE.COM` into cool and casual `square.com`. It also handles more exotic characters. For\n * example, the Unicode trademark sign (™) could be confused for the letters \"TM\" in\n * `http://ho™ail.com`. To mitigate this, the single character (™) maps to the string (tm). There\n * is similar policy for all of the 1.1 million Unicode code points. Note that some code points such\n * as \"\\ud83c\\udf69\" are not mapped and cannot be used in a hostname.\n *\n * [Punycode](http://ietf.org/rfc/rfc3492.txt) converts a Unicode string to an ASCII string to make\n * international domain names work everywhere. For example, \"σ\" encodes as \"xn--4xa\". The encoded\n * string is not human readable, but can be used with classes like [InetAddress] to establish\n * connections.\n *\n * ## Why another URL model?\n *\n * Java includes both [java.net.URL][URL] and [java.net.URI][URI]. We offer a new URL\n * model to address problems that the others don't.\n *\n * ### Different URLs should be different\n *\n * Although they have different content, `java.net.URL` considers the following two URLs\n * equal, and the [equals()][Object.equals] method between them returns true:\n *\n *  * https://example.net/\n *\n *  * https://example.com/\n *\n * This is because those two hosts share the same IP address. This is an old, bad design decision\n * that makes `java.net.URL` unusable for many things. It shouldn't be used as a [Map] key or in a\n * [Set]. Doing so is both inefficient because equality may require a DNS lookup, and incorrect\n * because unequal URLs may be equal because of how they are hosted.\n *\n * ### Equal URLs should be equal\n *\n * These two URLs are semantically identical, but `java.net.URI` disagrees:\n *\n *  * http://host:80/\n *\n *  * http://host\n *\n * Both the unnecessary port specification (`:80`) and the absent trailing slash (`/`) cause URI to\n * bucket the two URLs separately. This harms URI's usefulness in collections. Any application that\n * stores information-per-URL will need to either canonicalize manually, or suffer unnecessary\n * redundancy for such URLs.\n *\n * Because they don't attempt canonical form, these classes are surprisingly difficult to use\n * securely. Suppose you're building a webservice that checks that incoming paths are prefixed\n * \"/static/images/\" before serving the corresponding assets from the filesystem.\n *\n * ```java\n * String attack = \"http://example.com/static/images/../../../../../etc/passwd\";\n * System.out.println(new URL(attack).getPath());\n * System.out.println(new URI(attack).getPath());\n * System.out.println(HttpUrl.parse(attack).encodedPath());\n * ```\n *\n * By canonicalizing the input paths, they are complicit in directory traversal attacks. Code that\n * checks only the path prefix may suffer!\n *\n * ```\n * /static/images/../../../../../etc/passwd\n * /static/images/../../../../../etc/passwd\n * /etc/passwd\n * ```\n *\n * ### If it works on the web, it should work in your application\n *\n * The `java.net.URI` class is strict around what URLs it accepts. It rejects URLs like\n * `http://example.com/abc|def` because the `|` character is unsupported. This class is more\n * forgiving: it will automatically percent-encode the `|'` yielding `http://example.com/abc%7Cdef`.\n * This kind behavior is consistent with web browsers. `HttpUrl` prefers consistency with major web\n * browsers over consistency with obsolete specifications.\n *\n * ### Paths and Queries should decompose\n *\n * Neither of the built-in URL models offer direct access to path segments or query parameters.\n * Manually using `StringBuilder` to assemble these components is cumbersome: do '+' characters get\n * silently replaced with spaces? If a query parameter contains a '&amp;', does that get escaped?\n * By offering methods to read and write individual query parameters directly, application\n * developers are saved from the hassles of encoding and decoding.\n *\n * ### Plus a modern API\n *\n * The URL (JDK1.0) and URI (Java 1.4) classes predate builders and instead use telescoping\n * constructors. For example, there's no API to compose a URI with a custom port without also\n * providing a query and fragment.\n *\n * Instances of [HttpUrl] are well-formed and always have a scheme, host, and path. With\n * `java.net.URL` it's possible to create an awkward URL like `http:/` with scheme and path but no\n * hostname. Building APIs that consume such malformed values is difficult!\n *\n * This class has a modern API. It avoids punitive checked exceptions: [toHttpUrl] throws\n * [IllegalArgumentException] on invalid input or [toHttpUrlOrNull] returns null if the input is an\n * invalid URL. You can even be explicit about whether each component has been encoded already.\n *\n * [idna]: http://www.unicode.org/reports/tr46/#ToASCII\n */\nclass HttpUrl private constructor(\n  /** Either \"http\" or \"https\". */\n  @get:JvmName(\"scheme\") val scheme: String,\n  /**\n   * The decoded username, or an empty string if none is present.\n   *\n   * | URL                              | `username()` |\n   * | :------------------------------- | :----------- |\n   * | `http://host/`                   | `\"\"`         |\n   * | `http://username@host/`          | `\"username\"` |\n   * | `http://username:password@host/` | `\"username\"` |\n   * | `http://a%20b:c%20d@host/`       | `\"a b\"`      |\n   */\n  @get:JvmName(\"username\") val username: String,\n  /**\n   * Returns the decoded password, or an empty string if none is present.\n   *\n   * | URL                              | `password()` |\n   * | :------------------------------- | :----------- |\n   * | `http://host/`                   | `\"\"`         |\n   * | `http://username@host/`          | `\"\"`         |\n   * | `http://username:password@host/` | `\"password\"` |\n   * | `http://a%20b:c%20d@host/`       | `\"c d\"`      |\n   */\n  @get:JvmName(\"password\") val password: String,\n  /**\n   * The host address suitable for use with [InetAddress.getAllByName]. May be:\n   *\n   *  * A regular host name, like `android.com`.\n   *\n   *  * An IPv4 address, like `127.0.0.1`.\n   *\n   *  * An IPv6 address, like `::1`. Note that there are no square braces.\n   *\n   *  * An encoded IDN, like `xn--n3h.net`.\n   *\n   * | URL                   | `host()`        |\n   * | :-------------------- | :-------------- |\n   * | `http://android.com/` | `\"android.com\"` |\n   * | `http://127.0.0.1/`   | `\"127.0.0.1\"`   |\n   * | `http://[::1]/`       | `\"::1\"`         |\n   * | `http://xn--n3h.net/` | `\"xn--n3h.net\"` |\n   */\n  @get:JvmName(\"host\") val host: String,\n  /**\n   * The explicitly-specified port if one was provided, or the default port for this URL's scheme.\n   * For example, this returns 8443 for `https://square.com:8443/` and 443 for\n   * `https://square.com/`. The result is in `[1..65535]`.\n   *\n   * | URL                 | `port()` |\n   * | :------------------ | :------- |\n   * | `http://host/`      | `80`     |\n   * | `http://host:8000/` | `8000`   |\n   * | `https://host/`     | `443`    |\n   */\n  @get:JvmName(\"port\") val port: Int,\n  /**\n   * A list of path segments like `[\"a\", \"b\", \"c\"]` for the URL `http://host/a/b/c`. This list is\n   * never empty though it may contain a single empty string.\n   *\n   * | URL                      | `pathSegments()`    |\n   * | :----------------------- | :------------------ |\n   * | `http://host/`           | `[\"\"]`              |\n   * | `http://host/a/b/c\"`     | `[\"a\", \"b\", \"c\"]`   |\n   * | `http://host/a/b%20c/d\"` | `[\"a\", \"b c\", \"d\"]` |\n   */\n  @get:JvmName(\"pathSegments\") val pathSegments: List<String>,\n  /**\n   * Alternating, decoded query names and values, or null for no query. Names may be empty or\n   * non-empty, but never null. Values are null if the name has no corresponding '=' separator, or\n   * empty, or non-empty.\n   */\n  private val queryNamesAndValues: List<String?>?,\n  /**\n   * This URL's fragment, like `\"abc\"` for `http://host/#abc`. This is null if the URL has no\n   * fragment.\n   *\n   * | URL                    | `fragment()` |\n   * | :--------------------- | :----------- |\n   * | `http://host/`         | null         |\n   * | `http://host/#`        | `\"\"`         |\n   * | `http://host/#abc`     | `\"abc\"`      |\n   * | `http://host/#abc|def` | `\"abc|def\"`  |\n   */\n  @get:JvmName(\"fragment\") val fragment: String?,\n  /** Canonical URL. */\n  private val url: String,\n) {\n  val isHttps: Boolean\n    get() = scheme == \"https\"\n\n  /** Returns this URL as a [java.net.URL][URL]. */\n  @JvmName(\"url\")\n  fun toUrl(): URL {\n    try {\n      return URL(url)\n    } catch (e: MalformedURLException) {\n      throw RuntimeException(e) // Unexpected!\n    }\n  }\n\n  /**\n   * Returns this URL as a [java.net.URI][URI]. Because `URI` is more strict than this class, the\n   * returned URI may be semantically different from this URL:\n   *\n   *  * Characters forbidden by URI like `[` and `|` will be escaped.\n   *\n   *  * Invalid percent-encoded sequences like `%xx` will be encoded like `%25xx`.\n   *\n   *  * Whitespace and control characters in the fragment will be stripped.\n   *\n   * These differences may have a significant consequence when the URI is interpreted by a\n   * web server. For this reason the [URI class][URI] and this method should be avoided.\n   */\n  @JvmName(\"uri\")\n  fun toUri(): URI {\n    val uri = newBuilder().reencodeForUri().toString()\n    return try {\n      URI(uri)\n    } catch (e: URISyntaxException) {\n      // Unlikely edge case: the URI has a forbidden character in the fragment. Strip it & retry.\n      try {\n        val stripped = uri.replace(Regex(\"[\\\\u0000-\\\\u001F\\\\u007F-\\\\u009F\\\\p{javaWhitespace}]\"), \"\")\n        URI.create(stripped)\n      } catch (e1: Exception) {\n        throw RuntimeException(e) // Unexpected!\n      }\n    }\n  }\n\n  /**\n   * The username, or an empty string if none is set.\n   *\n   * | URL                              | `encodedUsername()` |\n   * | :------------------------------- | :------------------ |\n   * | `http://host/`                   | `\"\"`                |\n   * | `http://username@host/`          | `\"username\"`        |\n   * | `http://username:password@host/` | `\"username\"`        |\n   * | `http://a%20b:c%20d@host/`       | `\"a%20b\"`           |\n   */\n  @get:JvmName(\"encodedUsername\")\n  val encodedUsername: String\n    get() {\n      if (username.isEmpty()) return \"\"\n      val usernameStart = scheme.length + 3 // \"://\".length() == 3.\n      val usernameEnd = url.delimiterOffset(\":@\", usernameStart, url.length)\n      return url.substring(usernameStart, usernameEnd)\n    }\n\n  /**\n   * The password, or an empty string if none is set.\n   *\n   * | URL                              | `encodedPassword()` |\n   * | :--------------------------------| :------------------ |\n   * | `http://host/`                   | `\"\"`                |\n   * | `http://username@host/`          | `\"\"`                |\n   * | `http://username:password@host/` | `\"password\"`        |\n   * | `http://a%20b:c%20d@host/`       | `\"c%20d\"`           |\n   */\n  @get:JvmName(\"encodedPassword\")\n  val encodedPassword: String\n    get() {\n      if (password.isEmpty()) return \"\"\n      val passwordStart = url.indexOf(':', scheme.length + 3) + 1\n      val passwordEnd = url.indexOf('@')\n      return url.substring(passwordStart, passwordEnd)\n    }\n\n  /**\n   * The number of segments in this URL's path. This is also the number of slashes in this URL's\n   * path, like 3 in `http://host/a/b/c`. This is always at least 1.\n   *\n   * | URL                  | `pathSize()` |\n   * | :------------------- | :----------- |\n   * | `http://host/`       | `1`          |\n   * | `http://host/a/b/c`  | `3`          |\n   * | `http://host/a/b/c/` | `4`          |\n   */\n  @get:JvmName(\"pathSize\")\n  val pathSize: Int\n    get() = pathSegments.size\n\n  /**\n   * The entire path of this URL encoded for use in HTTP resource resolution. The returned path will\n   * start with `\"/\"`.\n   *\n   * | URL                     | `encodedPath()` |\n   * | :---------------------- | :-------------- |\n   * | `http://host/`          | `\"/\"`           |\n   * | `http://host/a/b/c`     | `\"/a/b/c\"`      |\n   * | `http://host/a/b%20c/d` | `\"/a/b%20c/d\"`  |\n   */\n  @get:JvmName(\"encodedPath\")\n  val encodedPath: String\n    get() {\n      val pathStart = url.indexOf('/', scheme.length + 3) // \"://\".length() == 3.\n      val pathEnd = url.delimiterOffset(\"?#\", pathStart, url.length)\n      return url.substring(pathStart, pathEnd)\n    }\n\n  /**\n   * A list of encoded path segments like `[\"a\", \"b\", \"c\"]` for the URL `http://host/a/b/c`. This\n   * list is never empty though it may contain a single empty string.\n   *\n   * | URL                     | `encodedPathSegments()` |\n   * | :---------------------- | :---------------------- |\n   * | `http://host/`          | `[\"\"]`                  |\n   * | `http://host/a/b/c`     | `[\"a\", \"b\", \"c\"]`       |\n   * | `http://host/a/b%20c/d` | `[\"a\", \"b%20c\", \"d\"]`   |\n   */\n  @get:JvmName(\"encodedPathSegments\")\n  val encodedPathSegments: List<String>\n    get() {\n      val pathStart = url.indexOf('/', scheme.length + 3)\n      val pathEnd = url.delimiterOffset(\"?#\", pathStart, url.length)\n      val result = mutableListOf<String>()\n      var i = pathStart\n      while (i < pathEnd) {\n        i++ // Skip the '/'.\n        val segmentEnd = url.delimiterOffset('/', i, pathEnd)\n        result.add(url.substring(i, segmentEnd))\n        i = segmentEnd\n      }\n      return result\n    }\n\n  /**\n   * The query of this URL, encoded for use in HTTP resource resolution. This string may be null\n   * (for URLs with no query), empty (for URLs with an empty query) or non-empty (all other URLs).\n   *\n   * | URL                               | `encodedQuery()`       |\n   * | :-------------------------------- | :--------------------- |\n   * | `http://host/`                    | null                   |\n   * | `http://host/?`                   | `\"\"`                   |\n   * | `http://host/?a=apple&k=key+lime` | `\"a=apple&k=key+lime\"` |\n   * | `http://host/?a=apple&a=apricot`  | `\"a=apple&a=apricot\"`  |\n   * | `http://host/?a=apple&b`          | `\"a=apple&b\"`          |\n   */\n  @get:JvmName(\"encodedQuery\")\n  val encodedQuery: String?\n    get() {\n      if (queryNamesAndValues == null) return null // No query.\n      val queryStart = url.indexOf('?') + 1\n      val queryEnd = url.delimiterOffset('#', queryStart, url.length)\n      return url.substring(queryStart, queryEnd)\n    }\n\n  /**\n   * This URL's query, like `\"abc\"` for `http://host/?abc`. Most callers should prefer\n   * [queryParameterName] and [queryParameterValue] because these methods offer direct access to\n   * individual query parameters.\n   *\n   * | URL                               | `query()`              |\n   * | :-------------------------------- | :--------------------- |\n   * | `http://host/`                    | null                   |\n   * | `http://host/?`                   | `\"\"`                   |\n   * | `http://host/?a=apple&k=key+lime` | `\"a=apple&k=key lime\"` |\n   * | `http://host/?a=apple&a=apricot`  | `\"a=apple&a=apricot\"`  |\n   * | `http://host/?a=apple&b`          | `\"a=apple&b\"`          |\n   */\n  @get:JvmName(\"query\")\n  val query: String?\n    get() {\n      if (queryNamesAndValues == null) return null // No query.\n      val result = StringBuilder()\n      queryNamesAndValues.toQueryString(result)\n      return result.toString()\n    }\n\n  /**\n   * The number of query parameters in this URL, like 2 for `http://host/?a=apple&b=banana`. If this\n   * URL has no query this is 0. Otherwise it is one more than the number of `\"&\"` separators in the\n   * query.\n   *\n   * | URL                               | `querySize()` |\n   * | :-------------------------------- | :------------ |\n   * | `http://host/`                    | `0`           |\n   * | `http://host/?`                   | `1`           |\n   * | `http://host/?a=apple&k=key+lime` | `2`           |\n   * | `http://host/?a=apple&a=apricot`  | `2`           |\n   * | `http://host/?a=apple&b`          | `2`           |\n   */\n  @get:JvmName(\"querySize\")\n  val querySize: Int\n    get() {\n      return if (queryNamesAndValues != null) queryNamesAndValues.size / 2 else 0\n    }\n\n  /**\n   * The first query parameter named `name` decoded using UTF-8, or null if there is no such query\n   * parameter.\n   *\n   * | URL                               | `queryParameter(\"a\")` |\n   * | :-------------------------------- | :-------------------- |\n   * | `http://host/`                    | null                  |\n   * | `http://host/?`                   | null                  |\n   * | `http://host/?a=apple&k=key+lime` | `\"apple\"`             |\n   * | `http://host/?a=apple&a=apricot`  | `\"apple\"`             |\n   * | `http://host/?a=apple&b`          | `\"apple\"`             |\n   */\n  fun queryParameter(name: String): String? {\n    if (queryNamesAndValues == null) return null\n    for (i in 0 until queryNamesAndValues.size step 2) {\n      if (name == queryNamesAndValues[i]) {\n        return queryNamesAndValues[i + 1]\n      }\n    }\n    return null\n  }\n\n  /**\n   * The distinct query parameter names in this URL, like `[\"a\", \"b\"]` for\n   * `http://host/?a=apple&b=banana`. If this URL has no query this is the empty set.\n   *\n   * | URL                               | `queryParameterNames()` |\n   * | :-------------------------------- | :---------------------- |\n   * | `http://host/`                    | `[]`                    |\n   * | `http://host/?`                   | `[\"\"]`                  |\n   * | `http://host/?a=apple&k=key+lime` | `[\"a\", \"k\"]`            |\n   * | `http://host/?a=apple&a=apricot`  | `[\"a\"]`                 |\n   * | `http://host/?a=apple&b`          | `[\"a\", \"b\"]`            |\n   */\n  @get:JvmName(\"queryParameterNames\")\n  val queryParameterNames: Set<String>\n    get() {\n      if (queryNamesAndValues == null) return emptySet()\n      val result = LinkedHashSet<String>(queryNamesAndValues.size / 2, 1.0F)\n      for (i in 0 until queryNamesAndValues.size step 2) {\n        result.add(queryNamesAndValues[i]!!)\n      }\n      return result.unmodifiable()\n    }\n\n  /**\n   * Returns all values for the query parameter `name` ordered by their appearance in this\n   * URL. For example this returns `[\"banana\"]` for `queryParameterValue(\"b\")` on\n   * `http://host/?a=apple&b=banana`.\n   *\n   * | URL                               | `queryParameterValues(\"a\")` | `queryParameterValues(\"b\")` |\n   * | :-------------------------------- | :-------------------------- | :-------------------------- |\n   * | `http://host/`                    | `[]`                        | `[]`                        |\n   * | `http://host/?`                   | `[]`                        | `[]`                        |\n   * | `http://host/?a=apple&k=key+lime` | `[\"apple\"]`                 | `[]`                        |\n   * | `http://host/?a=apple&a=apricot`  | `[\"apple\", \"apricot\"]`      | `[]`                        |\n   * | `http://host/?a=apple&b`          | `[\"apple\"]`                 | `[null]`                    |\n   */\n  fun queryParameterValues(name: String): List<String?> {\n    if (queryNamesAndValues == null) return emptyList()\n    val result = ArrayList<String?>(4)\n    for (i in 0 until queryNamesAndValues.size step 2) {\n      if (name == queryNamesAndValues[i]) {\n        result.add(queryNamesAndValues[i + 1])\n      }\n    }\n    return result.unmodifiable()\n  }\n\n  /**\n   * Returns the name of the query parameter at `index`. For example this returns `\"a\"`\n   * for `queryParameterName(0)` on `http://host/?a=apple&b=banana`. This throws if\n   * `index` is not less than the [query size][querySize].\n   *\n   * | URL                               | `queryParameterName(0)` | `queryParameterName(1)` |\n   * | :-------------------------------- | :---------------------- | :---------------------- |\n   * | `http://host/`                    | exception               | exception               |\n   * | `http://host/?`                   | `\"\"`                    | exception               |\n   * | `http://host/?a=apple&k=key+lime` | `\"a\"`                   | `\"k\"`                   |\n   * | `http://host/?a=apple&a=apricot`  | `\"a\"`                   | `\"a\"`                   |\n   * | `http://host/?a=apple&b`          | `\"a\"`                   | `\"b\"`                   |\n   */\n  fun queryParameterName(index: Int): String {\n    if (queryNamesAndValues == null) throw IndexOutOfBoundsException()\n    return queryNamesAndValues[index * 2]!!\n  }\n\n  /**\n   * Returns the value of the query parameter at `index`. For example this returns `\"apple\"` for\n   * `queryParameterName(0)` on `http://host/?a=apple&b=banana`. This throws if `index` is not less\n   * than the [query size][querySize].\n   *\n   * | URL                               | `queryParameterValue(0)` | `queryParameterValue(1)` |\n   * | :-------------------------------- | :----------------------- | :----------------------- |\n   * | `http://host/`                    | exception                | exception                |\n   * | `http://host/?`                   | null                     | exception                |\n   * | `http://host/?a=apple&k=key+lime` | `\"apple\"`                | `\"key lime\"`             |\n   * | `http://host/?a=apple&a=apricot`  | `\"apple\"`                | `\"apricot\"`              |\n   * | `http://host/?a=apple&b`          | `\"apple\"`                | null                     |\n   */\n  fun queryParameterValue(index: Int): String? {\n    if (queryNamesAndValues == null) throw IndexOutOfBoundsException()\n    return queryNamesAndValues[index * 2 + 1]\n  }\n\n  /**\n   * This URL's encoded fragment, like `\"abc\"` for `http://host/#abc`. This is null if the URL has\n   * no fragment.\n   *\n   * | URL                    | `encodedFragment()` |\n   * | :--------------------- | :------------------ |\n   * | `http://host/`         | null                |\n   * | `http://host/#`        | `\"\"`                |\n   * | `http://host/#abc`     | `\"abc\"`             |\n   * | `http://host/#abc|def` | `\"abc|def\"`         |\n   */\n  @get:JvmName(\"encodedFragment\")\n  val encodedFragment: String?\n    get() {\n      if (fragment == null) return null\n      val fragmentStart = url.indexOf('#') + 1\n      return url.substring(fragmentStart)\n    }\n\n  /**\n   * Returns a string with containing this URL with its username, password, query, and fragment\n   * stripped, and its path replaced with `/...`. For example, redacting\n   * `http://username:password@example.com/path` returns `http://example.com/...`.\n   */\n  fun redact(): String =\n    newBuilder(\"/...\")!!\n      .username(\"\")\n      .password(\"\")\n      .build()\n      .toString()\n\n  /**\n   * Returns the URL that would be retrieved by following `link` from this URL, or null if the\n   * resulting URL is not well-formed.\n   */\n  fun resolve(link: String): HttpUrl? = newBuilder(link)?.build()\n\n  /**\n   * Returns a builder based on this URL.\n   */\n  fun newBuilder(): Builder {\n    val result = Builder()\n    result.scheme = scheme\n    result.encodedUsername = encodedUsername\n    result.encodedPassword = encodedPassword\n    result.host = host\n    // If we're set to a default port, unset it in case of a scheme change.\n    result.port = if (port != defaultPort(scheme)) port else -1\n    result.encodedPathSegments.clear()\n    result.encodedPathSegments.addAll(encodedPathSegments)\n    result.encodedQuery(encodedQuery)\n    result.encodedFragment = encodedFragment\n    return result\n  }\n\n  /**\n   * Returns a builder for the URL that would be retrieved by following `link` from this URL,\n   * or null if the resulting URL is not well-formed.\n   */\n  fun newBuilder(link: String): Builder? =\n    try {\n      Builder().parse(this, link)\n    } catch (_: IllegalArgumentException) {\n      null\n    }\n\n  override fun equals(other: Any?): Boolean = other is HttpUrl && other.url == url\n\n  override fun hashCode(): Int = url.hashCode()\n\n  override fun toString(): String = url\n\n  /**\n   * Returns the domain name of this URL's [host] that is one level beneath the public suffix by\n   * consulting the [public suffix list](https://publicsuffix.org). Returns null if this URL's\n   * [host] is an IP address or is considered a public suffix by the public suffix list.\n   *\n   * In general this method **should not** be used to test whether a domain is valid or routable.\n   * Instead, DNS is the recommended source for that information.\n   *\n   * | URL                           | `topPrivateDomain()` |\n   * | :---------------------------- | :------------------- |\n   * | `http://google.com`           | `\"google.com\"`       |\n   * | `http://adwords.google.co.uk` | `\"google.co.uk\"`     |\n   * | `http://square`               | null                 |\n   * | `http://co.uk`                | null                 |\n   * | `http://localhost`            | null                 |\n   * | `http://127.0.0.1`            | null                 |\n   */\n  fun topPrivateDomain(): String? =\n    if (host.canParseAsIpAddress()) {\n      null\n    } else {\n      PublicSuffixDatabase.get().getEffectiveTldPlusOne(host)\n    }\n\n  @JvmName(\"-deprecated_url\")\n  @Deprecated(\n    message = \"moved to toUrl()\",\n    replaceWith = ReplaceWith(expression = \"toUrl()\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun url(): URL = toUrl()\n\n  @JvmName(\"-deprecated_uri\")\n  @Deprecated(\n    message = \"moved to toUri()\",\n    replaceWith = ReplaceWith(expression = \"toUri()\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun uri(): URI = toUri()\n\n  @JvmName(\"-deprecated_scheme\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"scheme\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun scheme(): String = scheme\n\n  @JvmName(\"-deprecated_encodedUsername\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"encodedUsername\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun encodedUsername(): String = encodedUsername\n\n  @JvmName(\"-deprecated_username\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"username\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun username(): String = username\n\n  @JvmName(\"-deprecated_encodedPassword\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"encodedPassword\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun encodedPassword(): String = encodedPassword\n\n  @JvmName(\"-deprecated_password\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"password\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun password(): String = password\n\n  @JvmName(\"-deprecated_host\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"host\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun host(): String = host\n\n  @JvmName(\"-deprecated_port\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"port\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun port(): Int = port\n\n  @JvmName(\"-deprecated_pathSize\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"pathSize\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun pathSize(): Int = pathSize\n\n  @JvmName(\"-deprecated_encodedPath\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"encodedPath\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun encodedPath(): String = encodedPath\n\n  @JvmName(\"-deprecated_encodedPathSegments\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"encodedPathSegments\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun encodedPathSegments(): List<String> = encodedPathSegments\n\n  @JvmName(\"-deprecated_pathSegments\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"pathSegments\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun pathSegments(): List<String> = pathSegments\n\n  @JvmName(\"-deprecated_encodedQuery\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"encodedQuery\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun encodedQuery(): String? = encodedQuery\n\n  @JvmName(\"-deprecated_query\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"query\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun query(): String? = query\n\n  @JvmName(\"-deprecated_querySize\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"querySize\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun querySize(): Int = querySize\n\n  @JvmName(\"-deprecated_queryParameterNames\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"queryParameterNames\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun queryParameterNames(): Set<String> = queryParameterNames\n\n  @JvmName(\"-deprecated_encodedFragment\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"encodedFragment\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun encodedFragment(): String? = encodedFragment\n\n  @JvmName(\"-deprecated_fragment\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"fragment\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun fragment(): String? = fragment\n\n  class Builder {\n    internal var scheme: String? = null\n    internal var encodedUsername = \"\"\n    internal var encodedPassword = \"\"\n    internal var host: String? = null\n    internal var port = -1\n    internal val encodedPathSegments = mutableListOf<String>(\"\")\n    internal var encodedQueryNamesAndValues: MutableList<String?>? = null\n    internal var encodedFragment: String? = null\n\n    /**\n     * @param scheme either \"http\" or \"https\".\n     */\n    fun scheme(scheme: String) =\n      apply {\n        when {\n          scheme.equals(\"http\", ignoreCase = true) -> this.scheme = \"http\"\n          scheme.equals(\"https\", ignoreCase = true) -> this.scheme = \"https\"\n          else -> throw IllegalArgumentException(\"unexpected scheme: $scheme\")\n        }\n      }\n\n    fun username(username: String) =\n      apply {\n        this.encodedUsername = username.canonicalize(encodeSet = USERNAME_ENCODE_SET)\n      }\n\n    fun encodedUsername(encodedUsername: String) =\n      apply {\n        this.encodedUsername =\n          encodedUsername.canonicalize(\n            encodeSet = USERNAME_ENCODE_SET,\n            alreadyEncoded = true,\n          )\n      }\n\n    fun password(password: String) =\n      apply {\n        this.encodedPassword = password.canonicalize(encodeSet = PASSWORD_ENCODE_SET)\n      }\n\n    fun encodedPassword(encodedPassword: String) =\n      apply {\n        this.encodedPassword =\n          encodedPassword.canonicalize(\n            encodeSet = PASSWORD_ENCODE_SET,\n            alreadyEncoded = true,\n          )\n      }\n\n    /**\n     * @param host either a regular hostname, International Domain Name, IPv4 address, or IPv6\n     * address.\n     */\n    fun host(host: String) =\n      apply {\n        val encoded =\n          host.percentDecode().toCanonicalHost()\n            ?: throw IllegalArgumentException(\"unexpected host: $host\")\n        this.host = encoded\n      }\n\n    fun port(port: Int) =\n      apply {\n        require(port in 1..65535) { \"unexpected port: $port\" }\n        this.port = port\n      }\n\n    fun addPathSegment(pathSegment: String) =\n      apply {\n        push(pathSegment, 0, pathSegment.length, addTrailingSlash = false, alreadyEncoded = false)\n      }\n\n    /**\n     * Adds a set of path segments separated by a slash (either `\\` or `/`). If `pathSegments`\n     * starts with a slash, the resulting URL will have empty path segment.\n     */\n    fun addPathSegments(pathSegments: String): Builder = addPathSegments(pathSegments, false)\n\n    fun addEncodedPathSegment(encodedPathSegment: String) =\n      apply {\n        push(\n          encodedPathSegment,\n          0,\n          encodedPathSegment.length,\n          addTrailingSlash = false,\n          alreadyEncoded = true,\n        )\n      }\n\n    /**\n     * Adds a set of encoded path segments separated by a slash (either `\\` or `/`). If\n     * `encodedPathSegments` starts with a slash, the resulting URL will have empty path segment.\n     */\n    fun addEncodedPathSegments(encodedPathSegments: String): Builder = addPathSegments(encodedPathSegments, true)\n\n    private fun addPathSegments(\n      pathSegments: String,\n      alreadyEncoded: Boolean,\n    ) = apply {\n      var offset = 0\n      do {\n        val segmentEnd = pathSegments.delimiterOffset(\"/\\\\\", offset, pathSegments.length)\n        val addTrailingSlash = segmentEnd < pathSegments.length\n        push(pathSegments, offset, segmentEnd, addTrailingSlash, alreadyEncoded)\n        offset = segmentEnd + 1\n      } while (offset <= pathSegments.length)\n    }\n\n    fun setPathSegment(\n      index: Int,\n      pathSegment: String,\n    ) = apply {\n      val canonicalPathSegment = pathSegment.canonicalize(encodeSet = PATH_SEGMENT_ENCODE_SET)\n      require(!isDot(canonicalPathSegment) && !isDotDot(canonicalPathSegment)) {\n        \"unexpected path segment: $pathSegment\"\n      }\n      encodedPathSegments[index] = canonicalPathSegment\n    }\n\n    fun setEncodedPathSegment(\n      index: Int,\n      encodedPathSegment: String,\n    ) = apply {\n      val canonicalPathSegment =\n        encodedPathSegment.canonicalize(\n          encodeSet = PATH_SEGMENT_ENCODE_SET,\n          alreadyEncoded = true,\n        )\n      encodedPathSegments[index] = canonicalPathSegment\n      require(!isDot(canonicalPathSegment) && !isDotDot(canonicalPathSegment)) {\n        \"unexpected path segment: $encodedPathSegment\"\n      }\n    }\n\n    fun removePathSegment(index: Int) =\n      apply {\n        encodedPathSegments.removeAt(index)\n        if (encodedPathSegments.isEmpty()) {\n          encodedPathSegments.add(\"\") // Always leave at least one '/'.\n        }\n      }\n\n    fun encodedPath(encodedPath: String) =\n      apply {\n        require(encodedPath.startsWith(\"/\")) { \"unexpected encodedPath: $encodedPath\" }\n        resolvePath(encodedPath, 0, encodedPath.length)\n      }\n\n    fun query(query: String?) =\n      apply {\n        this.encodedQueryNamesAndValues =\n          query\n            ?.canonicalize(\n              encodeSet = QUERY_ENCODE_SET,\n              plusIsSpace = true,\n            )?.toQueryNamesAndValues()\n      }\n\n    fun encodedQuery(encodedQuery: String?) =\n      apply {\n        this.encodedQueryNamesAndValues =\n          encodedQuery\n            ?.canonicalize(\n              encodeSet = QUERY_ENCODE_SET,\n              alreadyEncoded = true,\n              plusIsSpace = true,\n            )?.toQueryNamesAndValues()\n      }\n\n    /** Encodes the query parameter using UTF-8 and adds it to this URL's query string. */\n    fun addQueryParameter(\n      name: String,\n      value: String?,\n    ) = apply {\n      if (encodedQueryNamesAndValues == null) encodedQueryNamesAndValues = mutableListOf()\n      encodedQueryNamesAndValues!!.add(\n        name.canonicalize(\n          encodeSet = QUERY_COMPONENT_ENCODE_SET,\n          plusIsSpace = true,\n        ),\n      )\n      encodedQueryNamesAndValues!!.add(\n        value?.canonicalize(\n          encodeSet = QUERY_COMPONENT_ENCODE_SET,\n          plusIsSpace = true,\n        ),\n      )\n    }\n\n    /** Adds the pre-encoded query parameter to this URL's query string. */\n    fun addEncodedQueryParameter(\n      encodedName: String,\n      encodedValue: String?,\n    ) = apply {\n      if (encodedQueryNamesAndValues == null) encodedQueryNamesAndValues = mutableListOf()\n      encodedQueryNamesAndValues!!.add(\n        encodedName.canonicalize(\n          encodeSet = QUERY_COMPONENT_REENCODE_SET,\n          alreadyEncoded = true,\n          plusIsSpace = true,\n        ),\n      )\n      encodedQueryNamesAndValues!!.add(\n        encodedValue?.canonicalize(\n          encodeSet = QUERY_COMPONENT_REENCODE_SET,\n          alreadyEncoded = true,\n          plusIsSpace = true,\n        ),\n      )\n    }\n\n    fun setQueryParameter(\n      name: String,\n      value: String?,\n    ) = apply {\n      removeAllQueryParameters(name)\n      addQueryParameter(name, value)\n    }\n\n    fun setEncodedQueryParameter(\n      encodedName: String,\n      encodedValue: String?,\n    ) = apply {\n      removeAllEncodedQueryParameters(encodedName)\n      addEncodedQueryParameter(encodedName, encodedValue)\n    }\n\n    fun removeAllQueryParameters(name: String) =\n      apply {\n        if (encodedQueryNamesAndValues == null) return this\n        val nameToRemove =\n          name.canonicalize(\n            encodeSet = QUERY_COMPONENT_ENCODE_SET,\n            plusIsSpace = true,\n          )\n        removeAllCanonicalQueryParameters(nameToRemove)\n      }\n\n    fun removeAllEncodedQueryParameters(encodedName: String) =\n      apply {\n        if (encodedQueryNamesAndValues == null) return this\n        removeAllCanonicalQueryParameters(\n          encodedName.canonicalize(\n            encodeSet = QUERY_COMPONENT_REENCODE_SET,\n            alreadyEncoded = true,\n            plusIsSpace = true,\n          ),\n        )\n      }\n\n    private fun removeAllCanonicalQueryParameters(canonicalName: String) {\n      for (i in encodedQueryNamesAndValues!!.size - 2 downTo 0 step 2) {\n        if (canonicalName == encodedQueryNamesAndValues!![i]) {\n          encodedQueryNamesAndValues!!.removeAt(i + 1)\n          encodedQueryNamesAndValues!!.removeAt(i)\n          if (encodedQueryNamesAndValues!!.isEmpty()) {\n            encodedQueryNamesAndValues = null\n            return\n          }\n        }\n      }\n    }\n\n    fun fragment(fragment: String?) =\n      apply {\n        this.encodedFragment =\n          fragment?.canonicalize(\n            encodeSet = FRAGMENT_ENCODE_SET,\n            unicodeAllowed = true,\n          )\n      }\n\n    fun encodedFragment(encodedFragment: String?) =\n      apply {\n        this.encodedFragment =\n          encodedFragment?.canonicalize(\n            encodeSet = FRAGMENT_ENCODE_SET,\n            alreadyEncoded = true,\n            unicodeAllowed = true,\n          )\n      }\n\n    /**\n     * Re-encodes the components of this URL so that it satisfies (obsolete) RFC 2396, which is\n     * particularly strict for certain components.\n     */\n    internal fun reencodeForUri() =\n      apply {\n        host = host?.replace(Regex(\"[\\\"<>^`{|}]\"), \"\")\n\n        for (i in 0 until encodedPathSegments.size) {\n          encodedPathSegments[i] =\n            encodedPathSegments[i].canonicalize(\n              encodeSet = PATH_SEGMENT_ENCODE_SET_URI,\n              alreadyEncoded = true,\n              strict = true,\n            )\n        }\n\n        val encodedQueryNamesAndValues = this.encodedQueryNamesAndValues\n        if (encodedQueryNamesAndValues != null) {\n          for (i in 0 until encodedQueryNamesAndValues.size) {\n            encodedQueryNamesAndValues[i] =\n              encodedQueryNamesAndValues[i]?.canonicalize(\n                encodeSet = QUERY_COMPONENT_ENCODE_SET_URI,\n                alreadyEncoded = true,\n                strict = true,\n                plusIsSpace = true,\n              )\n          }\n        }\n\n        encodedFragment =\n          encodedFragment?.canonicalize(\n            encodeSet = FRAGMENT_ENCODE_SET_URI,\n            alreadyEncoded = true,\n            strict = true,\n            unicodeAllowed = true,\n          )\n      }\n\n    fun build(): HttpUrl {\n      @Suppress(\"UNCHECKED_CAST\") // percentDecode returns either List<String?> or List<String>.\n      return HttpUrl(\n        scheme = scheme ?: throw IllegalStateException(\"scheme == null\"),\n        username = encodedUsername.percentDecode(),\n        password = encodedPassword.percentDecode(),\n        host = host ?: throw IllegalStateException(\"host == null\"),\n        port = effectivePort(),\n        pathSegments = encodedPathSegments.map { it.percentDecode() },\n        queryNamesAndValues = encodedQueryNamesAndValues?.map { it?.percentDecode(plusIsSpace = true) },\n        fragment = encodedFragment?.percentDecode(),\n        url = toString(),\n      )\n    }\n\n    private fun effectivePort(): Int = if (port != -1) port else defaultPort(scheme!!)\n\n    override fun toString(): String =\n      buildString {\n        if (scheme != null) {\n          append(scheme)\n          append(\"://\")\n        } else {\n          append(\"//\")\n        }\n\n        if (encodedUsername.isNotEmpty() || encodedPassword.isNotEmpty()) {\n          append(encodedUsername)\n          if (encodedPassword.isNotEmpty()) {\n            append(':')\n            append(encodedPassword)\n          }\n          append('@')\n        }\n\n        if (host != null) {\n          if (':' in host!!) {\n            // Host is an IPv6 address.\n            append('[')\n            append(host)\n            append(']')\n          } else {\n            append(host)\n          }\n        }\n\n        if (port != -1 || scheme != null) {\n          val effectivePort = effectivePort()\n          if (scheme == null || effectivePort != defaultPort(scheme!!)) {\n            append(':')\n            append(effectivePort)\n          }\n        }\n\n        encodedPathSegments.toPathString(this)\n\n        if (encodedQueryNamesAndValues != null) {\n          append('?')\n          encodedQueryNamesAndValues!!.toQueryString(this)\n        }\n\n        if (encodedFragment != null) {\n          append('#')\n          append(encodedFragment)\n        }\n      }\n\n    /** Returns a path string for this list of path segments. */\n    private fun List<String>.toPathString(out: StringBuilder) {\n      for (i in 0 until size) {\n        out.append('/')\n        out.append(this[i])\n      }\n    }\n\n    internal fun parse(\n      base: HttpUrl?,\n      input: String,\n    ): Builder {\n      var pos = input.indexOfFirstNonAsciiWhitespace()\n      val limit = input.indexOfLastNonAsciiWhitespace(pos)\n\n      // Scheme.\n      val schemeDelimiterOffset = schemeDelimiterOffset(input, pos, limit)\n      if (schemeDelimiterOffset != -1) {\n        when {\n          input.startsWith(\"https:\", ignoreCase = true, startIndex = pos) -> {\n            this.scheme = \"https\"\n            pos += \"https:\".length\n          }\n\n          input.startsWith(\"http:\", ignoreCase = true, startIndex = pos) -> {\n            this.scheme = \"http\"\n            pos += \"http:\".length\n          }\n\n          else -> {\n            throw IllegalArgumentException(\n              \"Expected URL scheme 'http' or 'https' but was '\" +\n                input.substring(0, schemeDelimiterOffset) + \"'\",\n            )\n          }\n        }\n      } else if (base != null) {\n        this.scheme = base.scheme\n      } else {\n        val truncated = if (input.length > 6) input.take(6) + \"...\" else input\n        throw IllegalArgumentException(\n          \"Expected URL scheme 'http' or 'https' but no scheme was found for $truncated\",\n        )\n      }\n\n      // Authority.\n      var hasUsername = false\n      var hasPassword = false\n      val slashCount = input.slashCount(pos, limit)\n      if (slashCount >= 2 || base == null || base.scheme != this.scheme) {\n        // Read an authority if either:\n        //  * The input starts with 2 or more slashes. These follow the scheme if it exists.\n        //  * The input scheme exists and is different from the base URL's scheme.\n        //\n        // The structure of an authority is:\n        //   username:password@host:port\n        //\n        // Username, password and port are optional.\n        //   [username[:password]@]host[:port]\n        pos += slashCount\n        authority@ while (true) {\n          val componentDelimiterOffset = input.delimiterOffset(\"@/\\\\?#\", pos, limit)\n          val c =\n            if (componentDelimiterOffset != limit) {\n              input[componentDelimiterOffset].code\n            } else {\n              -1\n            }\n          when (c) {\n            '@'.code -> {\n              // User info precedes.\n              if (!hasPassword) {\n                val passwordColonOffset = input.delimiterOffset(':', pos, componentDelimiterOffset)\n                val canonicalUsername =\n                  input.canonicalize(\n                    pos = pos,\n                    limit = passwordColonOffset,\n                    encodeSet = USERNAME_ENCODE_SET,\n                    alreadyEncoded = true,\n                  )\n                this.encodedUsername =\n                  if (hasUsername) {\n                    this.encodedUsername + \"%40\" + canonicalUsername\n                  } else {\n                    canonicalUsername\n                  }\n                if (passwordColonOffset != componentDelimiterOffset) {\n                  hasPassword = true\n                  this.encodedPassword =\n                    input.canonicalize(\n                      pos = passwordColonOffset + 1,\n                      limit = componentDelimiterOffset,\n                      encodeSet = PASSWORD_ENCODE_SET,\n                      alreadyEncoded = true,\n                    )\n                }\n                hasUsername = true\n              } else {\n                this.encodedPassword = this.encodedPassword + \"%40\" +\n                  input.canonicalize(\n                    pos = pos,\n                    limit = componentDelimiterOffset,\n                    encodeSet = PASSWORD_ENCODE_SET,\n                    alreadyEncoded = true,\n                  )\n              }\n              pos = componentDelimiterOffset + 1\n            }\n\n            -1, '/'.code, '\\\\'.code, '?'.code, '#'.code -> {\n              // Host info precedes.\n              val portColonOffset = portColonOffset(input, pos, componentDelimiterOffset)\n              if (portColonOffset + 1 < componentDelimiterOffset) {\n                host = input.percentDecode(pos = pos, limit = portColonOffset).toCanonicalHost()\n                port = parsePort(input, portColonOffset + 1, componentDelimiterOffset)\n                require(port != -1) {\n                  \"Invalid URL port: \\\"${input.substring(\n                    portColonOffset + 1,\n                    componentDelimiterOffset,\n                  )}\\\"\"\n                }\n              } else {\n                host = input.percentDecode(pos = pos, limit = portColonOffset).toCanonicalHost()\n                port = defaultPort(scheme!!)\n              }\n              require(host != null) {\n                \"Invalid URL host: \\\"${input.substring(pos, portColonOffset)}\\\"\"\n              }\n              pos = componentDelimiterOffset\n              break@authority\n            }\n          }\n        }\n      } else {\n        // This is a relative link. Copy over all authority components. Also maybe the path & query.\n        this.encodedUsername = base.encodedUsername\n        this.encodedPassword = base.encodedPassword\n        this.host = base.host\n        this.port = base.port\n        this.encodedPathSegments.clear()\n        this.encodedPathSegments.addAll(base.encodedPathSegments)\n        if (pos == limit || input[pos] == '#') {\n          encodedQuery(base.encodedQuery)\n        }\n      }\n\n      // Resolve the relative path.\n      val pathDelimiterOffset = input.delimiterOffset(\"?#\", pos, limit)\n      resolvePath(input, pos, pathDelimiterOffset)\n      pos = pathDelimiterOffset\n\n      // Query.\n      if (pos < limit && input[pos] == '?') {\n        val queryDelimiterOffset = input.delimiterOffset('#', pos, limit)\n        this.encodedQueryNamesAndValues =\n          input\n            .canonicalize(\n              pos = pos + 1,\n              limit = queryDelimiterOffset,\n              encodeSet = QUERY_ENCODE_SET,\n              alreadyEncoded = true,\n              plusIsSpace = true,\n            ).toQueryNamesAndValues()\n        pos = queryDelimiterOffset\n      }\n\n      // Fragment.\n      if (pos < limit && input[pos] == '#') {\n        this.encodedFragment =\n          input.canonicalize(\n            pos = pos + 1,\n            limit = limit,\n            encodeSet = FRAGMENT_ENCODE_SET,\n            alreadyEncoded = true,\n            unicodeAllowed = true,\n          )\n      }\n\n      return this\n    }\n\n    private fun resolvePath(\n      input: String,\n      startPos: Int,\n      limit: Int,\n    ) {\n      var pos = startPos\n      // Read a delimiter.\n      if (pos == limit) {\n        // Empty path: keep the base path as-is.\n        return\n      }\n      val c = input[pos]\n      if (c == '/' || c == '\\\\') {\n        // Absolute path: reset to the default \"/\".\n        encodedPathSegments.clear()\n        encodedPathSegments.add(\"\")\n        pos++\n      } else {\n        // Relative path: clear everything after the last '/'.\n        encodedPathSegments[encodedPathSegments.size - 1] = \"\"\n      }\n\n      // Read path segments.\n      var i = pos\n      while (i < limit) {\n        val pathSegmentDelimiterOffset = input.delimiterOffset(\"/\\\\\", i, limit)\n        val segmentHasTrailingSlash = pathSegmentDelimiterOffset < limit\n        push(input, i, pathSegmentDelimiterOffset, segmentHasTrailingSlash, true)\n        i = pathSegmentDelimiterOffset\n        if (segmentHasTrailingSlash) i++\n      }\n    }\n\n    /** Adds a path segment. If the input is \"..\" or equivalent, this pops a path segment. */\n    private fun push(\n      input: String,\n      pos: Int,\n      limit: Int,\n      addTrailingSlash: Boolean,\n      alreadyEncoded: Boolean,\n    ) {\n      val segment =\n        input.canonicalize(\n          pos = pos,\n          limit = limit,\n          encodeSet = PATH_SEGMENT_ENCODE_SET,\n          alreadyEncoded = alreadyEncoded,\n        )\n      if (isDot(segment)) {\n        return // Skip '.' path segments.\n      }\n      if (isDotDot(segment)) {\n        pop()\n        return\n      }\n      if (encodedPathSegments[encodedPathSegments.size - 1].isEmpty()) {\n        encodedPathSegments[encodedPathSegments.size - 1] = segment\n      } else {\n        encodedPathSegments.add(segment)\n      }\n      if (addTrailingSlash) {\n        encodedPathSegments.add(\"\")\n      }\n    }\n\n    /**\n     * Removes a path segment. When this method returns the last segment is always \"\", which means\n     * the encoded path will have a trailing '/'.\n     *\n     * Popping \"/a/b/c/\" yields \"/a/b/\". In this case the list of path segments goes from [\"a\",\n     * \"b\", \"c\", \"\"] to [\"a\", \"b\", \"\"].\n     *\n     * Popping \"/a/b/c\" also yields \"/a/b/\". The list of path segments goes from [\"a\", \"b\", \"c\"]\n     * to [\"a\", \"b\", \"\"].\n     */\n    private fun pop() {\n      val removed = encodedPathSegments.removeAt(encodedPathSegments.size - 1)\n\n      // Make sure the path ends with a '/' by either adding an empty string or clearing a segment.\n      if (removed.isEmpty() && encodedPathSegments.isNotEmpty()) {\n        encodedPathSegments[encodedPathSegments.size - 1] = \"\"\n      } else {\n        encodedPathSegments.add(\"\")\n      }\n    }\n\n    private fun isDot(input: String): Boolean = input == \".\" || input.equals(\"%2e\", ignoreCase = true)\n\n    private fun isDotDot(input: String): Boolean =\n      input == \"..\" ||\n        input.equals(\"%2e.\", ignoreCase = true) ||\n        input.equals(\".%2e\", ignoreCase = true) ||\n        input.equals(\"%2e%2e\", ignoreCase = true)\n\n    /**\n     * Cuts this string up into alternating parameter names and values. This divides a query string\n     * like `subject=math&easy&problem=5-2=3` into the list `[\"subject\", \"math\", \"easy\", null,\n     * \"problem\", \"5-2=3\"]`. Note that values may be null and may contain '=' characters.\n     */\n    private fun String.toQueryNamesAndValues(): MutableList<String?> {\n      val result = mutableListOf<String?>()\n      var pos = 0\n      while (pos <= length) {\n        var ampersandOffset = indexOf('&', pos)\n        if (ampersandOffset == -1) ampersandOffset = length\n\n        val equalsOffset = indexOf('=', pos)\n        if (equalsOffset == -1 || equalsOffset > ampersandOffset) {\n          result.add(substring(pos, ampersandOffset))\n          result.add(null) // No value for this name.\n        } else {\n          result.add(substring(pos, equalsOffset))\n          result.add(substring(equalsOffset + 1, ampersandOffset))\n        }\n        pos = ampersandOffset + 1\n      }\n      return result\n    }\n\n    /**\n     * Returns the index of the ':' in `input` that is after scheme characters. Returns -1 if\n     * `input` does not have a scheme that starts at `pos`.\n     */\n    private fun schemeDelimiterOffset(\n      input: String,\n      pos: Int,\n      limit: Int,\n    ): Int {\n      if (limit - pos < 2) return -1\n\n      val c0 = input[pos]\n      if ((c0 < 'a' || c0 > 'z') && (c0 < 'A' || c0 > 'Z')) return -1 // Not a scheme start char.\n\n      characters@ for (i in pos + 1 until limit) {\n        return when (input[i]) {\n          // Scheme character. Keep going.\n          in 'a'..'z', in 'A'..'Z', in '0'..'9', '+', '-', '.' -> continue@characters\n\n          // Scheme prefix!\n          ':' -> i\n\n          // Non-scheme character before the first ':'.\n          else -> -1\n        }\n      }\n\n      return -1 // No ':'; doesn't start with a scheme.\n    }\n\n    /** Returns the number of '/' and '\\' slashes in this, starting at `pos`. */\n    private fun String.slashCount(\n      pos: Int,\n      limit: Int,\n    ): Int {\n      var slashCount = 0\n      for (i in pos until limit) {\n        val c = this[i]\n        if (c == '\\\\' || c == '/') {\n          slashCount++\n        } else {\n          break\n        }\n      }\n      return slashCount\n    }\n\n    /** Finds the first ':' in `input`, skipping characters between square braces \"[...]\". */\n    private fun portColonOffset(\n      input: String,\n      pos: Int,\n      limit: Int,\n    ): Int {\n      var i = pos\n      while (i < limit) {\n        when (input[i]) {\n          '[' -> {\n            while (++i < limit) {\n              if (input[i] == ']') break\n            }\n          }\n\n          ':' -> {\n            return i\n          }\n        }\n        i++\n      }\n      return limit // No colon.\n    }\n\n    private fun parsePort(\n      input: String,\n      pos: Int,\n      limit: Int,\n    ): Int =\n      try {\n        // Canonicalize the port string to skip '\\n' etc.\n        val portString = input.canonicalize(pos = pos, limit = limit, encodeSet = \"\")\n        val i = portString.toInt()\n        if (i in 1..65535) i else -1\n      } catch (_: NumberFormatException) {\n        -1 // Invalid port.\n      }\n  }\n\n  companion object {\n    /** Returns 80 if `scheme.equals(\"http\")`, 443 if `scheme.equals(\"https\")` and -1 otherwise. */\n    @JvmStatic\n    fun defaultPort(scheme: String): Int =\n      when (scheme) {\n        \"http\" -> 80\n        \"https\" -> 443\n        else -> -1\n      }\n\n    /** Returns a string for this list of query names and values. */\n    private fun List<String?>.toQueryString(out: StringBuilder) {\n      for (i in 0 until size step 2) {\n        val name = this[i]\n        val value = this[i + 1]\n        if (i > 0) out.append('&')\n        out.append(name)\n        if (value != null) {\n          out.append('=')\n          out.append(value)\n        }\n      }\n    }\n\n    /**\n     * Returns a new [HttpUrl] representing this.\n     *\n     * @throws IllegalArgumentException If this is not a well-formed HTTP or HTTPS URL.\n     */\n    @JvmStatic\n    @JvmName(\"get\")\n    fun String.toHttpUrl(): HttpUrl = Builder().parse(null, this).build()\n\n    /**\n     * Returns a new `HttpUrl` representing `url` if it is a well-formed HTTP or HTTPS URL, or null\n     * if it isn't.\n     */\n    @JvmStatic\n    @JvmName(\"parse\")\n    fun String.toHttpUrlOrNull(): HttpUrl? =\n      try {\n        toHttpUrl()\n      } catch (_: IllegalArgumentException) {\n        null\n      }\n\n    /**\n     * Returns an [HttpUrl] for this if its protocol is `http` or `https`, or null if it has any\n     * other protocol.\n     */\n    @JvmStatic\n    @JvmName(\"get\")\n    fun URL.toHttpUrlOrNull(): HttpUrl? = toString().toHttpUrlOrNull()\n\n    @JvmStatic\n    @JvmName(\"get\")\n    fun URI.toHttpUrlOrNull(): HttpUrl? = toString().toHttpUrlOrNull()\n\n    @JvmName(\"-deprecated_get\")\n    @Deprecated(\n      message = \"moved to extension function\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"url.toHttpUrl()\",\n          imports = [\"okhttp3.HttpUrl.Companion.toHttpUrl\"],\n        ),\n      level = DeprecationLevel.ERROR,\n    )\n    fun get(url: String): HttpUrl = url.toHttpUrl()\n\n    @JvmName(\"-deprecated_parse\")\n    @Deprecated(\n      message = \"moved to extension function\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"url.toHttpUrlOrNull()\",\n          imports = [\"okhttp3.HttpUrl.Companion.toHttpUrlOrNull\"],\n        ),\n      level = DeprecationLevel.ERROR,\n    )\n    fun parse(url: String): HttpUrl? = url.toHttpUrlOrNull()\n\n    @JvmName(\"-deprecated_get\")\n    @Deprecated(\n      message = \"moved to extension function\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"url.toHttpUrlOrNull()\",\n          imports = [\"okhttp3.HttpUrl.Companion.toHttpUrlOrNull\"],\n        ),\n      level = DeprecationLevel.ERROR,\n    )\n    fun get(url: URL): HttpUrl? = url.toHttpUrlOrNull()\n\n    @JvmName(\"-deprecated_get\")\n    @Deprecated(\n      message = \"moved to extension function\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"uri.toHttpUrlOrNull()\",\n          imports = [\"okhttp3.HttpUrl.Companion.toHttpUrlOrNull\"],\n        ),\n      level = DeprecationLevel.ERROR,\n    )\n    fun get(uri: URI): HttpUrl? = uri.toHttpUrlOrNull()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Interceptor.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.util.concurrent.TimeUnit\nimport javax.net.SocketFactory\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.internal.tls.CertificateChainCleaner\n\n/**\n * Observes, modifies, and potentially short-circuits requests going out and the corresponding\n * responses coming back in. Typically interceptors add, remove, or transform headers on the request\n * or response.\n *\n * Implementations of this interface throw [IOException] to signal connectivity failures. This\n * includes both natural exceptions such as unreachable servers, as well as synthetic exceptions\n * when responses are of an unexpected type or cannot be decoded.\n *\n * Other exception types cancel the current call:\n *\n *  * For synchronous calls made with [Call.execute], the exception is propagated to the caller.\n *\n *  * For asynchronous calls made with [Call.enqueue], an [IOException] is propagated to the caller\n *    indicating that the call was canceled. The interceptor's exception is delivered to the current\n *    thread's [uncaught exception handler][Thread.UncaughtExceptionHandler]. By default this\n *    crashes the application on Android and prints a stacktrace on the JVM. (Crash reporting\n *    libraries may customize this behavior.)\n *\n * A good way to signal a failure is with a synthetic HTTP response:\n *\n * ```kotlin\n *   @Throws(IOException::class)\n *   override fun intercept(chain: Interceptor.Chain): Response {\n *     if (myConfig.isInvalid()) {\n *       return Response.Builder()\n *           .request(chain.request())\n *           .protocol(Protocol.HTTP_1_1)\n *           .code(400)\n *           .message(\"client config invalid\")\n *           .body(\"client config invalid\".toResponseBody(null))\n *           .build()\n *     }\n *\n *     return chain.proceed(chain.request())\n *   }\n * ```\n */\nfun interface Interceptor {\n  @Throws(IOException::class)\n  fun intercept(chain: Chain): Response\n\n  companion object {\n    /**\n     * Constructs an interceptor for a lambda. This compact syntax is most useful for inline\n     * interceptors.\n     *\n     * ```kotlin\n     * val interceptor = Interceptor { chain: Interceptor.Chain ->\n     *     chain.proceed(chain.request())\n     * }\n     * ```\n     */\n    inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor = Interceptor { block(it) }\n  }\n\n  interface Chain {\n    fun request(): Request\n\n    @Throws(IOException::class)\n    fun proceed(request: Request): Response\n\n    /**\n     * Returns the connection the request will be executed on. This is only available in the chains\n     * of network interceptors. For application interceptors this is always null.\n     */\n    fun connection(): Connection?\n\n    /**\n     * Returns the `Call` to which this chain belongs.\n     */\n    fun call(): Call\n\n    /**\n     * Returns the connect timeout in milliseconds.\n     */\n    fun connectTimeoutMillis(): Int\n\n    /**\n     * Returns a new chain with the specified connect timeout.\n     */\n    fun withConnectTimeout(\n      timeout: Int,\n      unit: TimeUnit,\n    ): Chain\n\n    /**\n     * Returns the read timeout in milliseconds.\n     */\n    fun readTimeoutMillis(): Int\n\n    /**\n     * Returns a new chain with the specified read timeout.\n     */\n    fun withReadTimeout(\n      timeout: Int,\n      unit: TimeUnit,\n    ): Chain\n\n    /**\n     * Returns the write timeout in milliseconds.\n     */\n    fun writeTimeoutMillis(): Int\n\n    /**\n     * Returns a new chain with the specified write timeout.\n     */\n    fun withWriteTimeout(\n      timeout: Int,\n      unit: TimeUnit,\n    ): Chain\n\n    val followSslRedirects: Boolean\n\n    val followRedirects: Boolean\n\n    /**\n     * Get the [DNS] instance for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val dns: Dns\n\n    /**\n     * Override the [DNS] for the Call.Chain.\n     *\n     * @throws IllegalStateException if this is a Network Interceptor, since the override is too late.\n     */\n    fun withDns(dns: Dns): Chain\n\n    /**\n     * Returns the [SocketFactory] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val socketFactory: SocketFactory\n\n    /**\n     * Override the [SocketFactory] for the Call.Chain.\n     *\n     * @throws IllegalStateException if this is a Network Interceptor, since the override is too late.\n     */\n    fun withSocketFactory(socketFactory: SocketFactory): Chain\n\n    /**\n     * Returns true if the call should retry on connection failures.\n     */\n    val retryOnConnectionFailure: Boolean\n\n    /**\n     * Returns a new chain with the specified retry on connection failure setting.\n     */\n    fun withRetryOnConnectionFailure(retryOnConnectionFailure: Boolean): Chain\n\n    /**\n     * Returns the [Authenticator] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val authenticator: Authenticator\n\n    /**\n     * Override the [Authenticator] for the Call.Chain.\n     *\n     * @throws IllegalStateException if this is a Network Interceptor, since the override is too late.\n     */\n    fun withAuthenticator(authenticator: Authenticator): Chain\n\n    /**\n     * Returns the [CookieJar] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val cookieJar: CookieJar\n\n    /**\n     * Returns a new chain with the specified [CookieJar].\n     */\n    fun withCookieJar(cookieJar: CookieJar): Chain\n\n    /**\n     * Returns the [Cache] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val cache: Cache?\n\n    /**\n     * Override the [Cache] for the Call.Chain.\n     *\n     * @throws IllegalStateException if this is a Network Interceptor, since the override is too late.\n     */\n    fun withCache(cache: Cache?): Chain\n\n    /**\n     * Returns the [Proxy] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val proxy: Proxy?\n\n    /**\n     * Returns a new chain with the specified [Proxy].\n     */\n    fun withProxy(proxy: Proxy?): Chain\n\n    /**\n     * Returns the [ProxySelector] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val proxySelector: ProxySelector\n\n    /**\n     * Override the [ProxySelector] for the Call.Chain.\n     *\n     * @throws IllegalStateException if this is a Network Interceptor, since the override is too late.\n     */\n    fun withProxySelector(proxySelector: ProxySelector): Chain\n\n    /**\n     * Returns the proxy [Authenticator] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val proxyAuthenticator: Authenticator\n\n    /**\n     * Returns a new chain with the specified proxy [Authenticator].\n     */\n    fun withProxyAuthenticator(proxyAuthenticator: Authenticator): Chain\n\n    /**\n     * Returns the [SSLSocketFactory] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val sslSocketFactoryOrNull: SSLSocketFactory?\n\n    /**\n     * Returns a new chain with the specified [SSLSocketFactory].\n     *\n     * @throws IllegalStateException if this is a Network Interceptor, since the override is too late.\n     */\n    fun withSslSocketFactory(\n      sslSocketFactory: SSLSocketFactory?,\n      x509TrustManager: X509TrustManager?,\n    ): Chain\n\n    /**\n     * Returns the [X509TrustManager] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val x509TrustManagerOrNull: X509TrustManager?\n\n    /**\n     * Returns the [HostnameVerifier] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val hostnameVerifier: HostnameVerifier\n\n    /**\n     * Override the [HostnameVerifier] for the Call.Chain.\n     *\n     * @throws IllegalStateException if this is a Network Interceptor, since the override is too late.\n     */\n    fun withHostnameVerifier(hostnameVerifier: HostnameVerifier): Chain\n\n    /**\n     * Returns the [CertificatePinner] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val certificatePinner: CertificatePinner\n\n    /**\n     * Returns a new chain with the specified [CertificatePinner].\n     */\n    fun withCertificatePinner(certificatePinner: CertificatePinner): Chain\n\n    /**\n     * Returns the [ConnectionPool] for the OkHttpClient, or an override from the Call.Chain.\n     */\n    val connectionPool: ConnectionPool\n\n    /**\n     * Returns a new chain with the specified [ConnectionPool].\n     */\n    fun withConnectionPool(connectionPool: ConnectionPool): Chain\n\n    val eventListener: EventListener\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/MediaType.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.nio.charset.Charset\n\n/**\n * An [RFC 2045][rfc_2045] Media Type, appropriate to describe the content type of an HTTP request\n * or response body.\n *\n * [rfc_2045]: http://tools.ietf.org/html/rfc2045\n */\nclass MediaType internal constructor(\n  internal val mediaType: String,\n  /**\n   * Returns the high-level media type, such as \"text\", \"image\", \"audio\", \"video\", or \"application\".\n   */\n  @get:JvmName(\"type\") val type: String,\n  /**\n   * Returns a specific media subtype, such as \"plain\" or \"png\", \"mpeg\", \"mp4\" or \"xml\".\n   */\n  @get:JvmName(\"subtype\") val subtype: String,\n  /** Alternating parameter names with their values, like `[\"charset\", \"utf-8\"]`. */\n  private val parameterNamesAndValues: Array<String>,\n) {\n  /**\n   * Returns the charset of this media type, or [defaultValue] if either this media type doesn't\n   * specify a charset, or if its charset is unsupported by the current runtime.\n   */\n  @JvmOverloads\n  fun charset(defaultValue: Charset? = null): Charset? {\n    val charset = parameter(\"charset\") ?: return defaultValue\n    return try {\n      Charset.forName(charset)\n    } catch (_: IllegalArgumentException) {\n      defaultValue // This charset is invalid or unsupported. Give up.\n    }\n  }\n\n  /**\n   * Returns the parameter [name] of this media type, or null if this media type does not define\n   * such a parameter.\n   */\n  fun parameter(name: String): String? {\n    for (i in parameterNamesAndValues.indices step 2) {\n      if (parameterNamesAndValues[i].equals(name, ignoreCase = true)) {\n        return parameterNamesAndValues[i + 1]\n      }\n    }\n    return null\n  }\n\n  @JvmName(\"-deprecated_type\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"type\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun type(): String = type\n\n  @JvmName(\"-deprecated_subtype\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"subtype\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun subtype(): String = subtype\n\n  /**\n   * Returns the encoded media type, like \"text/plain; charset=utf-8\", appropriate for use in a\n   * Content-Type header.\n   */\n  override fun toString(): String = mediaType\n\n  override fun equals(other: Any?): Boolean = other is MediaType && other.mediaType == mediaType\n\n  override fun hashCode(): Int = mediaType.hashCode()\n\n  companion object {\n    private const val TOKEN = \"([a-zA-Z0-9-!#$%&'*+.^_`{|}~]+)\"\n    private const val QUOTED = \"\\\"([^\\\"]*)\\\"\"\n    private val TYPE_SUBTYPE = Regex(\"$TOKEN/$TOKEN\")\n    private val PARAMETER = Regex(\";\\\\s*(?:$TOKEN=(?:$TOKEN|$QUOTED))?\")\n\n    /**\n     * Returns a media type for this string.\n     *\n     * @throws IllegalArgumentException if this is not a well-formed media type.\n     */\n    @JvmStatic\n    @JvmName(\"get\")\n    fun String.toMediaType(): MediaType {\n      val typeSubtype =\n        TYPE_SUBTYPE.matchAt(this, 0)\n          ?: throw IllegalArgumentException(\"No subtype found for: \\\"$this\\\"\")\n      val type = typeSubtype.groupValues[1].lowercase()\n      val subtype = typeSubtype.groupValues[2].lowercase()\n\n      val parameterNamesAndValues = mutableListOf<String>()\n      var s = typeSubtype.range.last + 1\n      while (s < length) {\n        val parameter = PARAMETER.matchAt(this, s)\n        require(parameter != null) {\n          \"Parameter is not formatted correctly: \\\"${substring(s)}\\\" for: \\\"$this\\\"\"\n        }\n\n        val name = parameter.groups[1]?.value\n        if (name == null) {\n          s = parameter.range.last + 1\n          continue\n        }\n\n        val token = parameter.groups[2]?.value\n        val value =\n          when {\n            token == null -> {\n              // Value is \"double-quoted\". That's valid and our regex group already strips the quotes.\n              parameter.groups[3]!!.value\n            }\n\n            token.startsWith('\\'') && token.endsWith('\\'') && token.length > 2 -> {\n              // If the token is 'single-quoted' it's invalid! But we're lenient and strip the quotes.\n              token.substring(1, token.length - 1)\n            }\n\n            else -> {\n              token\n            }\n          }\n\n        parameterNamesAndValues += name\n        parameterNamesAndValues += value\n        s = parameter.range.last + 1\n      }\n\n      return MediaType(this, type, subtype, parameterNamesAndValues.toTypedArray())\n    }\n\n    /** Returns a media type for this, or null if this is not a well-formed media type. */\n    @JvmStatic\n    @JvmName(\"parse\")\n    fun String.toMediaTypeOrNull(): MediaType? =\n      try {\n        toMediaType()\n      } catch (_: IllegalArgumentException) {\n        null\n      }\n\n    @JvmName(\"-deprecated_get\")\n    @Deprecated(\n      message = \"moved to extension function\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"mediaType.toMediaType()\",\n          imports = [\"okhttp3.MediaType.Companion.toMediaType\"],\n        ),\n      level = DeprecationLevel.ERROR,\n    )\n    fun get(mediaType: String): MediaType = mediaType.toMediaType()\n\n    @JvmName(\"-deprecated_parse\")\n    @Deprecated(\n      message = \"moved to extension function\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"mediaType.toMediaTypeOrNull()\",\n          imports = [\"okhttp3.MediaType.Companion.toMediaTypeOrNull\"],\n        ),\n      level = DeprecationLevel.ERROR,\n    )\n    fun parse(mediaType: String): MediaType? = mediaType.toMediaTypeOrNull()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/MultipartBody.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.util.UUID\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.internal.toImmutableList\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\n\n/**\n * An [RFC 2387][rfc_2387]-compliant request body.\n *\n * [rfc_2387]: http://www.ietf.org/rfc/rfc2387.txt\n */\n@Suppress(\"NAME_SHADOWING\")\nclass MultipartBody internal constructor(\n  private val boundaryByteString: ByteString,\n  @get:JvmName(\"type\") val type: MediaType,\n  @get:JvmName(\"parts\") val parts: List<Part>,\n) : RequestBody() {\n  private val contentType: MediaType = \"$type; boundary=$boundary\".toMediaType()\n  private var contentLength = -1L\n\n  @get:JvmName(\"boundary\")\n  val boundary: String\n    get() = boundaryByteString.utf8()\n\n  /** The number of parts in this multipart body. */\n  @get:JvmName(\"size\")\n  val size: Int\n    get() = parts.size\n\n  fun part(index: Int): Part = parts[index]\n\n  override fun isOneShot(): Boolean = parts.any { it.body.isOneShot() }\n\n  /** A combination of [type] and [boundaryByteString]. */\n  override fun contentType(): MediaType = contentType\n\n  @JvmName(\"-deprecated_type\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"type\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun type(): MediaType = type\n\n  @JvmName(\"-deprecated_boundary\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"boundary\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun boundary(): String = boundary\n\n  @JvmName(\"-deprecated_size\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"size\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun size(): Int = size\n\n  @JvmName(\"-deprecated_parts\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"parts\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun parts(): List<Part> = parts\n\n  @Throws(IOException::class)\n  override fun contentLength(): Long {\n    var result = contentLength\n    if (result == -1L) {\n      result = writeOrCountBytes(null, true)\n      contentLength = result\n    }\n    return result\n  }\n\n  @Throws(IOException::class)\n  override fun writeTo(sink: BufferedSink) {\n    writeOrCountBytes(sink, false)\n  }\n\n  /**\n   * Either writes this request to [sink] or measures its content length. We have one method do\n   * double-duty to make sure the counting and content are consistent, particularly when it comes\n   * to awkward operations like measuring the encoded length of header strings, or the\n   * length-in-digits of an encoded integer.\n   */\n  @Throws(IOException::class)\n  private fun writeOrCountBytes(\n    sink: BufferedSink?,\n    countBytes: Boolean,\n  ): Long {\n    var sink = sink\n    var byteCount = 0L\n\n    var byteCountBuffer: Buffer? = null\n    if (countBytes) {\n      byteCountBuffer = Buffer()\n      sink = byteCountBuffer\n    }\n\n    for (p in 0 until parts.size) {\n      val part = parts[p]\n      val headers = part.headers\n      val body = part.body\n\n      sink!!.write(DASHDASH)\n      sink.write(boundaryByteString)\n      sink.write(CRLF)\n\n      if (headers != null) {\n        for (h in 0 until headers.size) {\n          sink\n            .writeUtf8(headers.name(h))\n            .write(COLONSPACE)\n            .writeUtf8(headers.value(h))\n            .write(CRLF)\n        }\n      }\n\n      val contentType = body.contentType()\n      if (contentType != null) {\n        sink\n          .writeUtf8(\"Content-Type: \")\n          .writeUtf8(contentType.toString())\n          .write(CRLF)\n      }\n\n      // We can't measure the body's size without the sizes of its components.\n      val contentLength = body.contentLength()\n      if (contentLength == -1L && countBytes) {\n        byteCountBuffer!!.clear()\n        return -1L\n      }\n\n      sink.write(CRLF)\n\n      if (countBytes) {\n        byteCount += contentLength\n      } else {\n        body.writeTo(sink)\n      }\n\n      sink.write(CRLF)\n    }\n\n    sink!!.write(DASHDASH)\n    sink.write(boundaryByteString)\n    sink.write(DASHDASH)\n    sink.write(CRLF)\n\n    if (countBytes) {\n      byteCount += byteCountBuffer!!.size\n      byteCountBuffer.clear()\n    }\n\n    return byteCount\n  }\n\n  class Part private constructor(\n    @get:JvmName(\"headers\") val headers: Headers?,\n    @get:JvmName(\"body\") val body: RequestBody,\n  ) {\n    @JvmName(\"-deprecated_headers\")\n    @Deprecated(\n      message = \"moved to val\",\n      replaceWith = ReplaceWith(expression = \"headers\"),\n      level = DeprecationLevel.ERROR,\n    )\n    fun headers(): Headers? = headers\n\n    @JvmName(\"-deprecated_body\")\n    @Deprecated(\n      message = \"moved to val\",\n      replaceWith = ReplaceWith(expression = \"body\"),\n      level = DeprecationLevel.ERROR,\n    )\n    fun body(): RequestBody = body\n\n    companion object {\n      @JvmStatic\n      fun create(body: RequestBody): Part = create(null, body)\n\n      @JvmStatic\n      fun create(\n        headers: Headers?,\n        body: RequestBody,\n      ): Part {\n        require(headers?.get(\"Content-Type\") == null) { \"Unexpected header: Content-Type\" }\n        require(headers?.get(\"Content-Length\") == null) { \"Unexpected header: Content-Length\" }\n        return Part(headers, body)\n      }\n\n      @JvmStatic\n      fun createFormData(\n        name: String,\n        value: String,\n      ): Part = createFormData(name, null, value.toRequestBody())\n\n      @JvmStatic\n      fun createFormData(\n        name: String,\n        filename: String?,\n        body: RequestBody,\n      ): Part {\n        val disposition =\n          buildString {\n            append(\"form-data; name=\")\n            appendQuotedString(name)\n\n            if (filename != null) {\n              append(\"; filename=\")\n              appendQuotedString(filename)\n            }\n          }\n\n        val headers =\n          Headers\n            .Builder()\n            .addUnsafeNonAscii(\"Content-Disposition\", disposition)\n            .build()\n\n        return create(headers, body)\n      }\n    }\n  }\n\n  class Builder\n    @JvmOverloads\n    constructor(\n      boundary: String = UUID.randomUUID().toString(),\n    ) {\n      private val boundary: ByteString = boundary.encodeUtf8()\n      private var type = MIXED\n      private val parts = mutableListOf<Part>()\n\n      /**\n       * Set the MIME type. Expected values for `type` are [MIXED] (the default), [ALTERNATIVE],\n       * [DIGEST], [PARALLEL] and [FORM].\n       */\n      fun setType(type: MediaType) =\n        apply {\n          require(type.type == \"multipart\") { \"multipart != $type\" }\n          this.type = type\n        }\n\n      /** Add a part to the body. */\n      fun addPart(body: RequestBody) =\n        apply {\n          addPart(Part.create(body))\n        }\n\n      /** Add a part to the body. */\n      fun addPart(\n        headers: Headers?,\n        body: RequestBody,\n      ) = apply {\n        addPart(Part.create(headers, body))\n      }\n\n      /** Add a form data part to the body. */\n      fun addFormDataPart(\n        name: String,\n        value: String,\n      ) = apply {\n        addPart(Part.createFormData(name, value))\n      }\n\n      /** Add a form data part to the body. */\n      fun addFormDataPart(\n        name: String,\n        filename: String?,\n        body: RequestBody,\n      ) = apply {\n        addPart(Part.createFormData(name, filename, body))\n      }\n\n      /** Add a part to the body. */\n      fun addPart(part: Part) =\n        apply {\n          parts += part\n        }\n\n      /** Assemble the specified parts into a request body. */\n      fun build(): MultipartBody {\n        check(parts.isNotEmpty()) { \"Multipart body must have at least one part.\" }\n        return MultipartBody(boundary, type, parts.toImmutableList())\n      }\n    }\n\n  companion object {\n    /**\n     * The \"mixed\" subtype of \"multipart\" is intended for use when the body parts are independent\n     * and need to be bundled in a particular order. Any \"multipart\" subtypes that an implementation\n     * does not recognize must be treated as being of subtype \"mixed\".\n     */\n    @JvmField\n    val MIXED = \"multipart/mixed\".toMediaType()\n\n    /**\n     * The \"multipart/alternative\" type is syntactically identical to \"multipart/mixed\", but the\n     * semantics are different. In particular, each of the body parts is an \"alternative\" version of\n     * the same information.\n     */\n    @JvmField\n    val ALTERNATIVE = \"multipart/alternative\".toMediaType()\n\n    /**\n     * This type is syntactically identical to \"multipart/mixed\", but the semantics are different.\n     * In particular, in a digest, the default `Content-Type` value for a body part is changed from\n     * \"text/plain\" to \"message/rfc822\".\n     */\n    @JvmField\n    val DIGEST = \"multipart/digest\".toMediaType()\n\n    /**\n     * This type is syntactically identical to \"multipart/mixed\", but the semantics are different.\n     * In particular, in a parallel entity, the order of body parts is not significant.\n     */\n    @JvmField\n    val PARALLEL = \"multipart/parallel\".toMediaType()\n\n    /**\n     * The media-type multipart/form-data follows the rules of all multipart MIME data streams as\n     * outlined in RFC 2046. In forms, there are a series of fields to be supplied by the user who\n     * fills out the form. Each field has a name. Within a given form, the names are unique.\n     */\n    @JvmField\n    val FORM = \"multipart/form-data\".toMediaType()\n\n    private val COLONSPACE = byteArrayOf(':'.code.toByte(), ' '.code.toByte())\n    private val CRLF = byteArrayOf('\\r'.code.toByte(), '\\n'.code.toByte())\n    private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte())\n\n    /**\n     * Appends a quoted-string to a StringBuilder.\n     *\n     * RFC 2388 is rather vague about how one should escape special characters in form-data\n     * parameters, and as it turns out Firefox and Chrome actually do rather different things, and\n     * both say in their comments that they're not really sure what the right approach is. We go\n     * with Chrome's behavior (which also experimentally seems to match what IE does), but if you\n     * actually want to have a good chance of things working, please avoid double-quotes, newlines,\n     * percent signs, and the like in your field names.\n     */\n    internal fun StringBuilder.appendQuotedString(key: String) {\n      append('\"')\n      for (i in 0 until key.length) {\n        when (val ch = key[i]) {\n          '\\n' -> append(\"%0A\")\n          '\\r' -> append(\"%0D\")\n          '\"' -> append(\"%22\")\n          else -> append(ch)\n        }\n      }\n      append('\"')\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/MultipartReader.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.Closeable\nimport java.io.EOFException\nimport java.io.IOException\nimport java.net.ProtocolException\nimport okhttp3.internal.http1.HeadersReader\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.Options\nimport okio.Source\nimport okio.Timeout\nimport okio.buffer\n\n/**\n * Reads a stream of [RFC 2046][rfc_2046] multipart body parts. Callers read parts one-at-a-time\n * until [nextPart] returns null. After calling [nextPart] any preceding parts should not be read.\n *\n * Typical use loops over the parts in sequence:\n *\n * ```kotlin\n * val response: Response = call.execute()\n * val multipartReader = MultipartReader(response.body!!)\n *\n * multipartReader.use {\n *   while (true) {\n *     val part = multipartReader.nextPart() ?: break\n *     process(part.headers, part.body)\n *   }\n * }\n * ```\n *\n * Note that [nextPart] will skip any unprocessed data from the preceding part. If the preceding\n * part is particularly large or if the underlying source is particularly slow, the [nextPart] call\n * may be slow!\n *\n * Closing a part **does not** close this multipart reader; callers must explicitly close this with\n * [close].\n *\n * [rfc_2046]: http://www.ietf.org/rfc/rfc2046.txt\n */\nclass MultipartReader\n  @Throws(IOException::class)\n  constructor(\n    private val source: BufferedSource,\n    @get:JvmName(\"boundary\") val boundary: String,\n  ) : Closeable {\n    /** This delimiter typically precedes the first part. */\n    private val dashDashBoundary =\n      Buffer()\n        .writeUtf8(\"--\")\n        .writeUtf8(boundary)\n        .readByteString()\n\n    /**\n     * This delimiter typically precedes all subsequent parts. It may also precede the first part\n     * if the body contains a preamble.\n     */\n    private val crlfDashDashBoundary =\n      Buffer()\n        .writeUtf8(\"\\r\\n--\")\n        .writeUtf8(boundary)\n        .readByteString()\n\n    private var partCount = 0\n    private var closed = false\n    private var noMoreParts = false\n\n    /** This is only part that's allowed to read from the underlying source. */\n    private var currentPart: PartSource? = null\n\n    @Throws(IOException::class)\n    constructor(response: ResponseBody) : this(\n      source = response.source(),\n      boundary =\n        response.contentType()?.parameter(\"boundary\")\n          ?: throw ProtocolException(\"expected the Content-Type to have a boundary parameter\"),\n    )\n\n    @Throws(IOException::class)\n    fun nextPart(): Part? {\n      check(!closed) { \"closed\" }\n\n      if (noMoreParts) return null\n\n      // Read a boundary, skipping the remainder of the preceding part as necessary.\n      if (partCount == 0 && source.rangeEquals(0L, dashDashBoundary)) {\n        // This is the first part. Consume \"--\" followed by the boundary.\n        source.skip(dashDashBoundary.size.toLong())\n      } else {\n        // This is a subsequent part or a preamble. Skip until \"\\r\\n--\" followed by the boundary.\n        while (true) {\n          val toSkip = currentPartBytesRemaining(maxByteCount = 8192)\n          if (toSkip == 0L) break\n          source.skip(toSkip)\n        }\n        source.skip(crlfDashDashBoundary.size.toLong())\n      }\n\n      // Read either \\r\\n or --\\r\\n to determine if there is another part.\n      var whitespace = false\n      afterBoundaryLoop@while (true) {\n        when (source.select(afterBoundaryOptions)) {\n          0 -> {\n            // \"\\r\\n\": We've found a new part.\n            partCount++\n            break@afterBoundaryLoop\n          }\n\n          1 -> {\n            // \"--\": No more parts.\n            if (whitespace) throw ProtocolException(\"unexpected characters after boundary\")\n            if (partCount == 0) throw ProtocolException(\"expected at least 1 part\")\n            noMoreParts = true\n            return null\n          }\n\n          2, 3 -> {\n            // \" \" or \"\\t\" Ignore whitespace and keep looking.\n            whitespace = true\n            continue@afterBoundaryLoop\n          }\n\n          -1 -> {\n            throw ProtocolException(\"unexpected characters after boundary\")\n          }\n        }\n      }\n\n      // There's another part. Parse its headers and return it.\n      val headers = HeadersReader(source).readHeaders()\n      val partSource = PartSource()\n      currentPart = partSource\n      return Part(headers, partSource.buffer())\n    }\n\n    /** A single part in the stream. It is an error to read this after calling [nextPart]. */\n    private inner class PartSource : Source {\n      private val timeout = Timeout()\n\n      override fun close() {\n        if (currentPart == this) {\n          currentPart = null\n        }\n      }\n\n      override fun read(\n        sink: Buffer,\n        byteCount: Long,\n      ): Long {\n        require(byteCount >= 0L) { \"byteCount < 0: $byteCount\" }\n        check(currentPart == this) { \"closed\" }\n\n        return source.timeout().intersectWith(timeout) {\n          when (val limit = currentPartBytesRemaining(maxByteCount = byteCount)) {\n            0L -> -1L\n\n            // No more bytes in this part.\n            else -> source.read(sink, limit)\n          }\n        }\n      }\n\n      override fun timeout(): Timeout = timeout\n    }\n\n    /**\n     * Returns a value in [0..maxByteCount] with the number of bytes that can be read from [source]\n     * in the current part. If this returns 0 the current part is exhausted; otherwise it has at\n     * least one byte left to read.\n     */\n    private fun currentPartBytesRemaining(maxByteCount: Long): Long {\n      val toIndex = minOf(source.buffer.size, maxByteCount) + 1L\n      val boundaryIndex =\n        source.indexOf(\n          bytes = crlfDashDashBoundary,\n          fromIndex = 0L,\n          toIndex = toIndex,\n        )\n      return when {\n        boundaryIndex != -1L -> boundaryIndex\n\n        // We found the boundary.\n        source.buffer.size >= toIndex -> minOf(toIndex, maxByteCount)\n\n        // No boundary before toIndex.\n        else -> throw EOFException() // We ran out of data before we found the required boundary.\n      }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n      if (closed) return\n      closed = true\n      currentPart = null\n      source.close()\n    }\n\n    /** A single part in a multipart body. */\n    class Part(\n      @get:JvmName(\"headers\") val headers: Headers,\n      @get:JvmName(\"body\") val body: BufferedSource,\n    ) : Closeable by body\n\n    internal companion object {\n      /** These options follow the boundary. */\n      val afterBoundaryOptions =\n        Options.of(\n          // 0.  \"\\r\\n\"  More parts.\n          \"\\r\\n\".encodeUtf8(),\n          // 1.  \"--\"    No more parts.\n          \"--\".encodeUtf8(),\n          // 2.  \" \"     Optional whitespace. Only used if there are more parts.\n          \" \".encodeUtf8(),\n          // 3.  \"\\t\"    Optional whitespace. Only used if there are more parts.\n          \"\\t\".encodeUtf8(),\n        )\n    }\n  }\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/OkHttp.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nexpect object OkHttp {\n  /**\n   * This is a string like \"5.0.0\", \"5.0.0-alpha.762\", or \"5.3.0-SNAPSHOT\" indicating the version of\n   * OkHttp in the current runtime. Use this to include the OkHttp version in custom `User-Agent`\n   * headers.\n   *\n   * Official OkHttp releases follow [semantic versioning][semver]. Versions with the `-SNAPSHOT`\n   * qualifier are not unique and should only be used in development environments. If you create\n   * custom builds of OkHttp please include a qualifier your version name, like \"4.7.0-mycompany.3\".\n   * The version string is configured in the root project's `build.gradle`.\n   *\n   * Note that OkHttp's runtime version may be different from the version specified in your\n   * project's build file due to the dependency resolution features of your build tool.\n   *\n   * [semver]: https://semver.org\n   */\n  val VERSION: String\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/OkHttpClient.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.Socket\nimport java.time.Duration\nimport java.util.Random\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.TimeUnit.MILLISECONDS\nimport javax.net.SocketFactory\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport kotlin.time.Duration as KotlinDuration\nimport okhttp3.Protocol.HTTP_1_1\nimport okhttp3.Protocol.HTTP_2\nimport okhttp3.internal.asFactory\nimport okhttp3.internal.checkDuration\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.connection.RouteDatabase\nimport okhttp3.internal.immutableListOf\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.proxy.NullProxySelector\nimport okhttp3.internal.tls.CertificateChainCleaner\nimport okhttp3.internal.tls.OkHostnameVerifier\nimport okhttp3.internal.toImmutableList\nimport okhttp3.internal.unmodifiable\nimport okhttp3.internal.ws.RealWebSocket\nimport okio.Sink\nimport okio.Source\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n\n/**\n * Factory for [calls][Call], which can be used to send HTTP requests and read their responses.\n *\n * ## OkHttpClients Should Be Shared\n *\n * OkHttp performs best when you create a single `OkHttpClient` instance and reuse it for all of\n * your HTTP calls. This is because each client holds its own connection pool and thread pools.\n * Reusing connections and threads reduces latency and saves memory. Conversely, creating a client\n * for each request wastes resources on idle pools.\n *\n * Use `new OkHttpClient()` to create a shared instance with the default settings:\n *\n * ```java\n * // The singleton HTTP client.\n * public final OkHttpClient client = new OkHttpClient();\n * ```\n *\n * Or use `new OkHttpClient.Builder()` to create a shared instance with custom settings:\n *\n * ```java\n * // The singleton HTTP client.\n * public final OkHttpClient client = new OkHttpClient.Builder()\n *     .addInterceptor(new HttpLoggingInterceptor())\n *     .cache(new Cache(cacheDir, cacheSize))\n *     .build();\n * ```\n *\n * ## Customize Your Client With newBuilder()\n *\n * You can customize a shared OkHttpClient instance with [newBuilder]. This builds a client that\n * shares the same connection pool, thread pools, and configuration. Use the builder methods to\n * add configuration to the derived client for a specific purpose.\n *\n * This example shows the single instance with default configurations.\n *\n * ```java\n * public final OkHttpClient client = new OkHttpClient.Builder()\n *     .readTimeout(1000, TimeUnit.MILLISECONDS)\n *     .writeTimeout(1000, TimeUnit.MILLISECONDS)\n *     .build();\n * ```\n *\n * This example shows a call with a short 500 millisecond read timeout and a 1000 millisecond\n * write timeout. Original configuration is kept, but can be overriden.\n *\n * ```java\n * OkHttpClient eagerClient = client.newBuilder()\n *     .readTimeout(500, TimeUnit.MILLISECONDS)\n *     .build();\n * Response response = eagerClient.newCall(request).execute();\n * ```\n *\n * ## Shutdown Isn't Necessary\n *\n * The threads and connections that are held will be released automatically if they remain idle. But\n * if you are writing a application that needs to aggressively release unused resources you may do\n * so.\n *\n * Shutdown the dispatcher's executor service with [shutdown()][ExecutorService.shutdown]. This will\n * also cause future calls to the client to be rejected.\n *\n * ```java\n * client.dispatcher().executorService().shutdown();\n * ```\n *\n * Clear the connection pool with [evictAll()][ConnectionPool.evictAll]. Note that the connection\n * pool's daemon thread may not exit immediately.\n *\n * ```java\n * client.connectionPool().evictAll();\n * ```\n *\n * If your client has a cache, call [close()][Cache.close]. Note that it is an error to create calls\n * against a cache that is closed, and doing so will cause the call to crash.\n *\n * ```java\n * client.cache().close();\n * ```\n *\n * OkHttp also uses daemon threads for HTTP/2 connections. These will exit automatically if they\n * remain idle.\n */\nopen class OkHttpClient internal constructor(\n  builder: Builder,\n) : Call.Factory,\n  WebSocket.Factory {\n  @get:JvmName(\"dispatcher\")\n  val dispatcher: Dispatcher = builder.dispatcher\n\n  /**\n   * Returns an immutable list of interceptors that observe the full span of each call: from before\n   * the connection is established (if any) until after the response source is selected (either the\n   * origin server, cache, or both).\n   */\n  @get:JvmName(\"interceptors\")\n  val interceptors: List<Interceptor> =\n    builder.interceptors.toImmutableList()\n\n  /**\n   * Returns an immutable list of interceptors that observe a single network request and response.\n   * These interceptors must call [Interceptor.Chain.proceed] exactly once: it is an error for\n   * a network interceptor to short-circuit or repeat a network request.\n   */\n  @get:JvmName(\"networkInterceptors\")\n  val networkInterceptors: List<Interceptor> =\n    builder.networkInterceptors.toImmutableList()\n\n  @get:JvmName(\"eventListenerFactory\")\n  val eventListenerFactory: EventListener.Factory =\n    builder.eventListenerFactory\n\n  @get:JvmName(\"retryOnConnectionFailure\")\n  val retryOnConnectionFailure: Boolean =\n    builder.retryOnConnectionFailure\n\n  @get:JvmName(\"fastFallback\")\n  val fastFallback: Boolean = builder.fastFallback\n\n  @get:JvmName(\"authenticator\")\n  val authenticator: Authenticator = builder.authenticator\n\n  @get:JvmName(\"followRedirects\")\n  val followRedirects: Boolean = builder.followRedirects\n\n  @get:JvmName(\"followSslRedirects\")\n  val followSslRedirects: Boolean = builder.followSslRedirects\n\n  @get:JvmName(\"cookieJar\")\n  val cookieJar: CookieJar = builder.cookieJar\n\n  @get:JvmName(\"cache\")\n  val cache: Cache? = builder.cache\n\n  @get:JvmName(\"dns\")\n  val dns: Dns = builder.dns\n\n  @get:JvmName(\"proxy\")\n  val proxy: Proxy? = builder.proxy\n\n  @get:JvmName(\"proxySelector\")\n  val proxySelector: ProxySelector =\n    when {\n      // Defer calls to ProxySelector.getDefault() because it can throw a SecurityException.\n      builder.proxy != null -> NullProxySelector\n\n      else -> builder.proxySelector ?: ProxySelector.getDefault() ?: NullProxySelector\n    }\n\n  @get:JvmName(\"proxyAuthenticator\")\n  val proxyAuthenticator: Authenticator =\n    builder.proxyAuthenticator\n\n  @get:JvmName(\"socketFactory\")\n  val socketFactory: SocketFactory = builder.socketFactory\n\n  internal val sslSocketFactoryOrNull: SSLSocketFactory?\n\n  @get:JvmName(\"sslSocketFactory\")\n  val sslSocketFactory: SSLSocketFactory\n    get() = sslSocketFactoryOrNull ?: throw IllegalStateException(\"CLEARTEXT-only client\")\n\n  @get:JvmName(\"x509TrustManager\")\n  val x509TrustManager: X509TrustManager?\n\n  @get:JvmName(\"connectionSpecs\")\n  val connectionSpecs: List<ConnectionSpec> =\n    builder.connectionSpecs\n\n  @get:JvmName(\"protocols\")\n  val protocols: List<Protocol> = builder.protocols\n\n  @get:JvmName(\"hostnameVerifier\")\n  val hostnameVerifier: HostnameVerifier = builder.hostnameVerifier\n\n  @get:JvmName(\"certificatePinner\")\n  val certificatePinner: CertificatePinner\n\n  @get:JvmName(\"certificateChainCleaner\")\n  val certificateChainCleaner: CertificateChainCleaner?\n\n  /**\n   * Default call timeout (in milliseconds). By default there is no timeout for complete calls, but\n   * there is for the connect, write, and read actions within a call.\n   *\n   * For WebSockets and duplex calls the timeout only applies to the initial setup.\n   */\n  @get:JvmName(\"callTimeoutMillis\")\n  val callTimeoutMillis: Int = builder.callTimeout\n\n  /** Default connect timeout (in milliseconds). The default is 10 seconds. */\n  @get:JvmName(\"connectTimeoutMillis\")\n  val connectTimeoutMillis: Int = builder.connectTimeout\n\n  /** Default read timeout (in milliseconds). The default is 10 seconds. */\n  @get:JvmName(\"readTimeoutMillis\")\n  val readTimeoutMillis: Int = builder.readTimeout\n\n  /** Default write timeout (in milliseconds). The default is 10 seconds. */\n  @get:JvmName(\"writeTimeoutMillis\")\n  val writeTimeoutMillis: Int = builder.writeTimeout\n\n  /** Web socket and HTTP/2 ping interval (in milliseconds). By default pings are not sent. */\n  @get:JvmName(\"pingIntervalMillis\")\n  val pingIntervalMillis: Int = builder.pingInterval\n\n  /** Web socket close timeout (in milliseconds). */\n  @get:JvmName(\"webSocketCloseTimeout\")\n  val webSocketCloseTimeout: Int = builder.webSocketCloseTimeout\n\n  /**\n   * Minimum outbound web socket message size (in bytes) that will be compressed.\n   * The default is 1024 bytes.\n   */\n  @get:JvmName(\"minWebSocketMessageToCompress\")\n  val minWebSocketMessageToCompress: Long = builder.minWebSocketMessageToCompress\n\n  internal val routeDatabase: RouteDatabase = builder.routeDatabase ?: RouteDatabase()\n  internal val taskRunner: TaskRunner = builder.taskRunner ?: TaskRunner.INSTANCE\n\n  @get:JvmName(\"connectionPool\")\n  val connectionPool: ConnectionPool =\n    builder.connectionPool ?: ConnectionPool().also {\n      // Cache the pool in the builder so that it will be shared with other clients\n      builder.connectionPool = it\n    }\n\n  constructor() : this(Builder())\n\n  init {\n    if (connectionSpecs.none { it.isTls }) {\n      this.sslSocketFactoryOrNull = null\n      this.certificateChainCleaner = null\n      this.x509TrustManager = null\n      this.certificatePinner = CertificatePinner.DEFAULT\n    } else if (builder.sslSocketFactoryOrNull != null) {\n      this.sslSocketFactoryOrNull = builder.sslSocketFactoryOrNull\n      this.certificateChainCleaner = builder.certificateChainCleaner!!\n      this.x509TrustManager = builder.x509TrustManagerOrNull!!\n      this.certificatePinner =\n        builder.certificatePinner\n          .withCertificateChainCleaner(certificateChainCleaner)\n    } else {\n      this.x509TrustManager = Platform.get().platformTrustManager()\n      this.sslSocketFactoryOrNull = Platform.get().newSslSocketFactory(x509TrustManager)\n      this.certificateChainCleaner = CertificateChainCleaner.get(x509TrustManager)\n      this.certificatePinner =\n        builder.certificatePinner\n          .withCertificateChainCleaner(certificateChainCleaner)\n    }\n\n    verifyClientState()\n  }\n\n  /**\n   * Creates an [Address] of out of the provided [HttpUrl]\n   * that uses this client’s DNS, TLS, and proxy configuration.\n   */\n  fun address(url: HttpUrl): Address {\n    var useSslSocketFactory: SSLSocketFactory? = null\n    var useHostnameVerifier: HostnameVerifier? = null\n    var useCertificatePinner: CertificatePinner? = null\n    if (url.isHttps) {\n      useSslSocketFactory = sslSocketFactory\n      useHostnameVerifier = hostnameVerifier\n      useCertificatePinner = certificatePinner\n    }\n\n    return Address(\n      uriHost = url.host,\n      uriPort = url.port,\n      dns = dns,\n      socketFactory = socketFactory,\n      sslSocketFactory = useSslSocketFactory,\n      hostnameVerifier = useHostnameVerifier,\n      certificatePinner = useCertificatePinner,\n      proxyAuthenticator = proxyAuthenticator,\n      proxy = proxy,\n      protocols = protocols,\n      connectionSpecs = connectionSpecs,\n      proxySelector = proxySelector,\n    )\n  }\n\n  private fun verifyClientState() {\n    check(null !in (interceptors as List<Interceptor?>)) {\n      \"Null interceptor: $interceptors\"\n    }\n    check(null !in (networkInterceptors as List<Interceptor?>)) {\n      \"Null network interceptor: $networkInterceptors\"\n    }\n\n    if (connectionSpecs.none { it.isTls }) {\n      check(sslSocketFactoryOrNull == null)\n      check(certificateChainCleaner == null)\n      check(x509TrustManager == null)\n      check(certificatePinner == CertificatePinner.DEFAULT)\n    } else {\n      checkNotNull(sslSocketFactoryOrNull) { \"sslSocketFactory == null\" }\n      checkNotNull(certificateChainCleaner) { \"certificateChainCleaner == null\" }\n      checkNotNull(x509TrustManager) { \"x509TrustManager == null\" }\n    }\n  }\n\n  /** Prepares the [request] to be executed at some point in the future. */\n  override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)\n\n  /** Uses [request] to connect a new web socket. */\n  override fun newWebSocket(\n    request: Request,\n    listener: WebSocketListener,\n  ): WebSocket {\n    val webSocket =\n      RealWebSocket(\n        taskRunner = taskRunner,\n        originalRequest = request,\n        listener = listener,\n        random = Random(),\n        pingIntervalMillis = pingIntervalMillis.toLong(),\n        // extensions is always null for clients:\n        extensions = null,\n        minimumDeflateSize = minWebSocketMessageToCompress,\n        webSocketCloseTimeout = webSocketCloseTimeout.toLong(),\n      )\n    webSocket.connect(this)\n    return webSocket\n  }\n\n  open fun newBuilder(): Builder = Builder(this)\n\n  @JvmName(\"-deprecated_dispatcher\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"dispatcher\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun dispatcher(): Dispatcher = dispatcher\n\n  @JvmName(\"-deprecated_connectionPool\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"connectionPool\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun connectionPool(): ConnectionPool = connectionPool\n\n  @JvmName(\"-deprecated_interceptors\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"interceptors\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun interceptors(): List<Interceptor> = interceptors\n\n  @JvmName(\"-deprecated_networkInterceptors\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"networkInterceptors\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun networkInterceptors(): List<Interceptor> = networkInterceptors\n\n  @JvmName(\"-deprecated_eventListenerFactory\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"eventListenerFactory\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun eventListenerFactory(): EventListener.Factory = eventListenerFactory\n\n  @JvmName(\"-deprecated_retryOnConnectionFailure\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"retryOnConnectionFailure\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun retryOnConnectionFailure(): Boolean = retryOnConnectionFailure\n\n  @JvmName(\"-deprecated_authenticator\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"authenticator\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun authenticator(): Authenticator = authenticator\n\n  @JvmName(\"-deprecated_followRedirects\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"followRedirects\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun followRedirects(): Boolean = followRedirects\n\n  @JvmName(\"-deprecated_followSslRedirects\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"followSslRedirects\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun followSslRedirects(): Boolean = followSslRedirects\n\n  @JvmName(\"-deprecated_cookieJar\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"cookieJar\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun cookieJar(): CookieJar = cookieJar\n\n  @JvmName(\"-deprecated_cache\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"cache\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun cache(): Cache? = cache\n\n  @JvmName(\"-deprecated_dns\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"dns\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun dns(): Dns = dns\n\n  @JvmName(\"-deprecated_proxy\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"proxy\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun proxy(): Proxy? = proxy\n\n  @JvmName(\"-deprecated_proxySelector\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"proxySelector\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun proxySelector(): ProxySelector = proxySelector\n\n  @JvmName(\"-deprecated_proxyAuthenticator\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"proxyAuthenticator\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun proxyAuthenticator(): Authenticator = proxyAuthenticator\n\n  @JvmName(\"-deprecated_socketFactory\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"socketFactory\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun socketFactory(): SocketFactory = socketFactory\n\n  @JvmName(\"-deprecated_sslSocketFactory\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"sslSocketFactory\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun sslSocketFactory(): SSLSocketFactory = sslSocketFactory\n\n  @JvmName(\"-deprecated_connectionSpecs\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"connectionSpecs\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun connectionSpecs(): List<ConnectionSpec> = connectionSpecs\n\n  @JvmName(\"-deprecated_protocols\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"protocols\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun protocols(): List<Protocol> = protocols\n\n  @JvmName(\"-deprecated_hostnameVerifier\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"hostnameVerifier\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun hostnameVerifier(): HostnameVerifier = hostnameVerifier\n\n  @JvmName(\"-deprecated_certificatePinner\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"certificatePinner\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun certificatePinner(): CertificatePinner = certificatePinner\n\n  @JvmName(\"-deprecated_callTimeoutMillis\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"callTimeoutMillis\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun callTimeoutMillis(): Int = callTimeoutMillis\n\n  @JvmName(\"-deprecated_connectTimeoutMillis\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"connectTimeoutMillis\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun connectTimeoutMillis(): Int = connectTimeoutMillis\n\n  @JvmName(\"-deprecated_readTimeoutMillis\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"readTimeoutMillis\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun readTimeoutMillis(): Int = readTimeoutMillis\n\n  @JvmName(\"-deprecated_writeTimeoutMillis\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"writeTimeoutMillis\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun writeTimeoutMillis(): Int = writeTimeoutMillis\n\n  @JvmName(\"-deprecated_pingIntervalMillis\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"pingIntervalMillis\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun pingIntervalMillis(): Int = pingIntervalMillis\n\n  class Builder() {\n    internal var dispatcher: Dispatcher = Dispatcher()\n    internal var connectionPool: ConnectionPool? = null\n    internal val interceptors: MutableList<Interceptor> = mutableListOf()\n    internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()\n    internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()\n    internal var retryOnConnectionFailure = true\n    internal var fastFallback = true\n    internal var authenticator: Authenticator = Authenticator.NONE\n    internal var followRedirects = true\n    internal var followSslRedirects = true\n    internal var cookieJar: CookieJar = CookieJar.NO_COOKIES\n    internal var cache: Cache? = null\n    internal var dns: Dns = Dns.SYSTEM\n    internal var proxy: Proxy? = null\n    internal var proxySelector: ProxySelector? = null\n    internal var proxyAuthenticator: Authenticator = Authenticator.NONE\n    internal var socketFactory: SocketFactory = SocketFactory.getDefault()\n    internal var sslSocketFactoryOrNull: SSLSocketFactory? = null\n    internal var x509TrustManagerOrNull: X509TrustManager? = null\n    internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS\n    internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS\n    internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier\n    internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT\n    internal var certificateChainCleaner: CertificateChainCleaner? = null\n    internal var callTimeout = 0\n    internal var connectTimeout = 10_000\n    internal var readTimeout = 10_000\n    internal var writeTimeout = 10_000\n    internal var pingInterval = 0\n    internal var webSocketCloseTimeout = RealWebSocket.CANCEL_AFTER_CLOSE_MILLIS.toInt()\n    internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE\n    internal var routeDatabase: RouteDatabase? = null\n    internal var taskRunner: TaskRunner? = null\n\n    internal constructor(okHttpClient: OkHttpClient) : this() {\n      this.dispatcher = okHttpClient.dispatcher\n      this.connectionPool = okHttpClient.connectionPool\n      this.interceptors += okHttpClient.interceptors\n      this.networkInterceptors += okHttpClient.networkInterceptors\n      this.eventListenerFactory = okHttpClient.eventListenerFactory\n      this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure\n      this.fastFallback = okHttpClient.fastFallback\n      this.authenticator = okHttpClient.authenticator\n      this.followRedirects = okHttpClient.followRedirects\n      this.followSslRedirects = okHttpClient.followSslRedirects\n      this.cookieJar = okHttpClient.cookieJar\n      this.cache = okHttpClient.cache\n      this.dns = okHttpClient.dns\n      this.proxy = okHttpClient.proxy\n      this.proxySelector = okHttpClient.proxySelector\n      this.proxyAuthenticator = okHttpClient.proxyAuthenticator\n      this.socketFactory = okHttpClient.socketFactory\n      this.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNull\n      this.x509TrustManagerOrNull = okHttpClient.x509TrustManager\n      this.connectionSpecs = okHttpClient.connectionSpecs\n      this.protocols = okHttpClient.protocols\n      this.hostnameVerifier = okHttpClient.hostnameVerifier\n      this.certificatePinner = okHttpClient.certificatePinner\n      this.certificateChainCleaner = okHttpClient.certificateChainCleaner\n      this.callTimeout = okHttpClient.callTimeoutMillis\n      this.connectTimeout = okHttpClient.connectTimeoutMillis\n      this.readTimeout = okHttpClient.readTimeoutMillis\n      this.writeTimeout = okHttpClient.writeTimeoutMillis\n      this.pingInterval = okHttpClient.pingIntervalMillis\n      this.webSocketCloseTimeout = okHttpClient.webSocketCloseTimeout\n      this.minWebSocketMessageToCompress = okHttpClient.minWebSocketMessageToCompress\n      this.routeDatabase = okHttpClient.routeDatabase\n      this.taskRunner = okHttpClient.taskRunner\n    }\n\n    /**\n     * Sets the dispatcher used to set policy and execute asynchronous requests. Must not be null.\n     */\n    fun dispatcher(dispatcher: Dispatcher) =\n      apply {\n        this.dispatcher = dispatcher\n      }\n\n    /**\n     * Sets the connection pool used to recycle HTTP and HTTPS connections.\n     *\n     * If unset, a new connection pool will be used.\n     */\n    fun connectionPool(connectionPool: ConnectionPool) =\n      apply {\n        this.connectionPool = connectionPool\n      }\n\n    /**\n     * Returns a modifiable list of interceptors that observe the full span of each call: from\n     * before the connection is established (if any) until after the response source is selected\n     * (either the origin server, cache, or both).\n     */\n    fun interceptors(): MutableList<Interceptor> = interceptors\n\n    fun addInterceptor(interceptor: Interceptor) =\n      apply {\n        interceptors += interceptor\n      }\n\n    @JvmName(\"-addInterceptor\") // Prefix with '-' to prevent ambiguous overloads from Java.\n    inline fun addInterceptor(crossinline block: (chain: Interceptor.Chain) -> Response) =\n      addInterceptor(Interceptor { chain -> block(chain) })\n\n    /**\n     * Returns a modifiable list of interceptors that observe a single network request and response.\n     * These interceptors must call [Interceptor.Chain.proceed] exactly once: it is an error for a\n     * network interceptor to short-circuit or repeat a network request.\n     */\n    fun networkInterceptors(): MutableList<Interceptor> = networkInterceptors\n\n    fun addNetworkInterceptor(interceptor: Interceptor) =\n      apply {\n        networkInterceptors += interceptor\n      }\n\n    @JvmName(\"-addNetworkInterceptor\") // Prefix with '-' to prevent ambiguous overloads from Java.\n    inline fun addNetworkInterceptor(crossinline block: (chain: Interceptor.Chain) -> Response) =\n      addNetworkInterceptor(Interceptor { chain -> block(chain) })\n\n    /**\n     * Configure a single client scoped listener that will receive all analytic events for this\n     * client.\n     *\n     * @see EventListener for semantics and restrictions on listener implementations.\n     */\n    fun eventListener(eventListener: EventListener) =\n      apply {\n        this.eventListenerFactory = eventListener.asFactory()\n      }\n\n    /**\n     * Configure a factory to provide per-call scoped listeners that will receive analytic events\n     * for this client.\n     *\n     * @see EventListener for semantics and restrictions on listener implementations.\n     */\n    fun eventListenerFactory(eventListenerFactory: EventListener.Factory) =\n      apply {\n        this.eventListenerFactory = eventListenerFactory\n      }\n\n    /**\n     * Configure this client to retry or not when a connectivity problem is encountered. By default,\n     * this client silently recovers from the following problems:\n     *\n     * * **Unreachable IP addresses.** If the URL's host has multiple IP addresses,\n     *   failure to reach any individual IP address doesn't fail the overall request. This can\n     *   increase availability of multi-homed services.\n     *\n     * * **Stale pooled connections.** The [ConnectionPool] reuses sockets\n     *   to decrease request latency, but these connections will occasionally time out.\n     *\n     * * **Unreachable proxy servers.** A [ProxySelector] can be used to\n     *   attempt multiple proxy servers in sequence, eventually falling back to a direct\n     *   connection.\n     *\n     * Set this to false to avoid retrying requests when doing so is destructive. In this case the\n     * calling application should do its own recovery of connectivity failures.\n     */\n    fun retryOnConnectionFailure(retryOnConnectionFailure: Boolean) =\n      apply {\n        this.retryOnConnectionFailure = retryOnConnectionFailure\n      }\n\n    /**\n     * Configure this client to perform fast fallbacks by attempting multiple connections\n     * concurrently, returning once any connection connects successfully.\n     *\n     * This implements Happy Eyeballs ([RFC 6555][rfc_6555]), balancing connect latency vs.\n     * wasted resources.\n     *\n     * Defaults to enabled, call with [fastFallback] = false to revert to 4.x behaviour.\n     *\n     * [rfc_6555]: https://datatracker.ietf.org/doc/html/rfc6555\n     */\n    fun fastFallback(fastFallback: Boolean) =\n      apply {\n        this.fastFallback = fastFallback\n      }\n\n    /**\n     * Sets the authenticator used to respond to challenges from origin servers. Use\n     * [proxyAuthenticator] to set the authenticator for proxy servers.\n     *\n     * If unset, the [no authentication will be attempted][Authenticator.NONE].\n     */\n    fun authenticator(authenticator: Authenticator) =\n      apply {\n        this.authenticator = authenticator\n      }\n\n    /** Configure this client to follow redirects. If unset, redirects will be followed. */\n    fun followRedirects(followRedirects: Boolean) =\n      apply {\n        this.followRedirects = followRedirects\n      }\n\n    /**\n     * Configure this client to allow protocol redirects from HTTPS to HTTP and from HTTP to HTTPS.\n     * Redirects are still first restricted by [followRedirects].  Defaults to true.\n     *\n     * @param followProtocolRedirects whether to follow redirects between HTTPS and HTTP.\n     */\n    fun followSslRedirects(followProtocolRedirects: Boolean) =\n      apply {\n        this.followSslRedirects = followProtocolRedirects\n      }\n\n    /**\n     * Sets the handler that can accept cookies from incoming HTTP responses and provides cookies to\n     * outgoing HTTP requests.\n     *\n     * If unset, [no cookies][CookieJar.NO_COOKIES] will be accepted nor provided.\n     */\n    fun cookieJar(cookieJar: CookieJar) =\n      apply {\n        this.cookieJar = cookieJar\n      }\n\n    /** Sets the response cache to be used to read and write cached responses. */\n    fun cache(cache: Cache?) =\n      apply {\n        this.cache = cache\n      }\n\n    internal fun taskRunner(taskRunner: TaskRunner) =\n      apply {\n        this.taskRunner = taskRunner\n      }\n\n    /**\n     * Sets the DNS service used to lookup IP addresses for hostnames.\n     *\n     * If unset, the [system-wide default][Dns.SYSTEM] DNS will be used.\n     */\n    fun dns(dns: Dns) =\n      apply {\n        if (dns != this.dns) {\n          this.routeDatabase = null\n        }\n        this.dns = dns\n      }\n\n    /**\n     * Sets the HTTP proxy that will be used by connections created by this client. This takes\n     * precedence over [proxySelector], which is only honored when this proxy is null (which it is\n     * by default). To disable proxy use completely, call `proxy(Proxy.NO_PROXY)`.\n     */\n    fun proxy(proxy: Proxy?) =\n      apply {\n        if (proxy != this.proxy) {\n          this.routeDatabase = null\n        }\n        this.proxy = proxy\n      }\n\n    /**\n     * Sets the proxy selection policy to be used if no [proxy][proxy] is specified explicitly. The\n     * proxy selector may return multiple proxies; in that case they will be tried in sequence until\n     * a successful connection is established.\n     *\n     * If unset, the [system-wide default][ProxySelector.getDefault] proxy selector will be used.\n     */\n    fun proxySelector(proxySelector: ProxySelector) =\n      apply {\n        if (proxySelector != this.proxySelector) {\n          this.routeDatabase = null\n        }\n\n        this.proxySelector = proxySelector\n      }\n\n    /**\n     * Sets the authenticator used to respond to challenges from proxy servers. Use [authenticator]\n     * to set the authenticator for origin servers.\n     *\n     * If unset, the [no authentication will be attempted][Authenticator.NONE].\n     */\n    fun proxyAuthenticator(proxyAuthenticator: Authenticator) =\n      apply {\n        if (proxyAuthenticator != this.proxyAuthenticator) {\n          this.routeDatabase = null\n        }\n\n        this.proxyAuthenticator = proxyAuthenticator\n      }\n\n    /**\n     * Sets the socket factory used to create connections. OkHttp only uses the parameterless\n     * [SocketFactory.createSocket] method to create unconnected sockets. Overriding this method,\n     * e. g., allows the socket to be bound to a specific local address.\n     *\n     * If unset, the [system-wide default][SocketFactory.getDefault] socket factory will be used.\n     */\n    fun socketFactory(socketFactory: SocketFactory) =\n      apply {\n        require(socketFactory !is SSLSocketFactory) { \"socketFactory instanceof SSLSocketFactory\" }\n\n        if (socketFactory != this.socketFactory) {\n          this.routeDatabase = null\n        }\n\n        this.socketFactory = socketFactory\n      }\n\n    /**\n     * Sets the socket factory used to secure HTTPS connections. If unset, the system default will\n     * be used.\n     *\n     * @deprecated [SSLSocketFactory] does not expose its [X509TrustManager], which is a field that\n     *     OkHttp needs to build a clean certificate chain. This method instead must use reflection\n     *     to extract the trust manager. Applications should prefer to call\n     *     `sslSocketFactory(SSLSocketFactory, X509TrustManager)`, which avoids such reflection.\n     */\n    @Deprecated(\n      message = \"Use the sslSocketFactory overload that accepts a X509TrustManager.\",\n      level = DeprecationLevel.ERROR,\n    )\n    fun sslSocketFactory(sslSocketFactory: SSLSocketFactory) =\n      apply {\n        if (sslSocketFactory != this.sslSocketFactoryOrNull) {\n          this.routeDatabase = null\n        }\n\n        this.sslSocketFactoryOrNull = sslSocketFactory\n        this.x509TrustManagerOrNull =\n          Platform.get().trustManager(sslSocketFactory) ?: throw IllegalStateException(\n            \"Unable to extract the trust manager on ${Platform.get()}, \" +\n              \"sslSocketFactory is ${sslSocketFactory.javaClass}\",\n          )\n        this.certificateChainCleaner =\n          Platform.get().buildCertificateChainCleaner(x509TrustManagerOrNull!!)\n      }\n\n    /**\n     * Sets the socket factory and trust manager used to secure HTTPS connections. If unset, the\n     * system defaults will be used.\n     *\n     * Most applications should not call this method, and instead use the system defaults. Those\n     * classes include special optimizations that can be lost if the implementations are decorated.\n     *\n     * If necessary, you can create and configure the defaults yourself with the following code:\n     *\n     * ```java\n     * TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(\n     * TrustManagerFactory.getDefaultAlgorithm());\n     * trustManagerFactory.init((KeyStore) null);\n     * TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();\n     * if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {\n     *     throw new IllegalStateException(\"Unexpected default trust managers:\"\n     *         + Arrays.toString(trustManagers));\n     * }\n     * X509TrustManager trustManager = (X509TrustManager) trustManagers[0];\n     *\n     * SSLContext sslContext = SSLContext.getInstance(\"TLS\");\n     * sslContext.init(null, new TrustManager[] { trustManager }, null);\n     * SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();\n     *\n     * OkHttpClient client = new OkHttpClient.Builder()\n     *     .sslSocketFactory(sslSocketFactory, trustManager)\n     *     .build();\n     * ```\n     *\n     * ## TrustManagers on Android are Weird!\n     *\n     * Trust managers targeting Android must also define a method that has this signature:\n     *\n     * ```java\n     *    @SuppressWarnings(\"unused\")\n     *    public List<X509Certificate> checkServerTrusted(\n     *        X509Certificate[] chain, String authType, String host) throws CertificateException {\n     *    }\n     * ```\n     *\n     * This method works like [X509TrustManager.checkServerTrusted] but it receives the hostname of\n     * the server as an extra parameter. Regardless of what checks this method performs, OkHttp will\n     * always check that the server's certificates match its hostname using the [HostnameVerifier].\n     * See [android.net.http.X509TrustManagerExtensions] for more information.\n     */\n    fun sslSocketFactory(\n      sslSocketFactory: SSLSocketFactory,\n      trustManager: X509TrustManager,\n    ) = apply {\n      if (sslSocketFactory != this.sslSocketFactoryOrNull || trustManager != this.x509TrustManagerOrNull) {\n        this.routeDatabase = null\n      }\n\n      this.sslSocketFactoryOrNull = sslSocketFactory\n      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager)\n      this.x509TrustManagerOrNull = trustManager\n    }\n\n    fun connectionSpecs(connectionSpecs: List<ConnectionSpec>) =\n      apply {\n        if (connectionSpecs != this.connectionSpecs) {\n          this.routeDatabase = null\n        }\n\n        this.connectionSpecs = connectionSpecs.toImmutableList()\n      }\n\n    /**\n     * Configure the protocols used by this client to communicate with remote servers. By default\n     * this client will prefer the most efficient transport available, falling back to more\n     * ubiquitous protocols. Applications should only call this method to avoid specific\n     * compatibility problems, such as web servers that behave incorrectly when HTTP/2 is enabled.\n     *\n     * The following protocols are currently supported:\n     *\n     * * [http/1.1][rfc_2616]\n     * * [h2][rfc_7540]\n     * * [h2 with prior knowledge(cleartext only)][rfc_7540_34]\n     *\n     * **This is an evolving set.** Future releases include support for transitional\n     * protocols. The http/1.1 transport will never be dropped.\n     *\n     * If multiple protocols are specified, [ALPN][alpn] will be used to negotiate a transport.\n     * Protocol negotiation is only attempted for HTTPS URLs.\n     *\n     * [Protocol.HTTP_1_0] is not supported in this set. Requests are initiated with `HTTP/1.1`. If\n     * the server responds with `HTTP/1.0`, that will be exposed by [Response.protocol].\n     *\n     * [alpn]: http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg\n     * [rfc_2616]: http://www.w3.org/Protocols/rfc2616/rfc2616.html\n     * [rfc_7540]: https://tools.ietf.org/html/rfc7540\n     * [rfc_7540_34]: https://tools.ietf.org/html/rfc7540#section-3.4\n     *\n     * @param protocols the protocols to use, in order of preference. If the list contains\n     *     [Protocol.H2_PRIOR_KNOWLEDGE] then that must be the only protocol and HTTPS URLs will not\n     *     be supported. Otherwise the list must contain [Protocol.HTTP_1_1]. The list must\n     *     not contain null or [Protocol.HTTP_1_0].\n     */\n    fun protocols(protocols: List<Protocol>) =\n      apply {\n        // Create a private copy of the list.\n        val protocolsCopy = protocols.toMutableList()\n\n        // Validate that the list has everything we require and nothing we forbid.\n        require(Protocol.H2_PRIOR_KNOWLEDGE in protocolsCopy || HTTP_1_1 in protocolsCopy) {\n          \"protocols must contain h2_prior_knowledge or http/1.1: $protocolsCopy\"\n        }\n        require(Protocol.H2_PRIOR_KNOWLEDGE !in protocolsCopy || protocolsCopy.size <= 1) {\n          \"protocols containing h2_prior_knowledge cannot use other protocols: $protocolsCopy\"\n        }\n        require(Protocol.HTTP_1_0 !in protocolsCopy) {\n          \"protocols must not contain http/1.0: $protocolsCopy\"\n        }\n        require(null !in (protocolsCopy as List<Protocol?>)) {\n          \"protocols must not contain null\"\n        }\n\n        // Remove protocols that we no longer support.\n        @Suppress(\"DEPRECATION\")\n        protocolsCopy.remove(Protocol.SPDY_3)\n\n        if (protocolsCopy != this.protocols) {\n          this.routeDatabase = null\n        }\n\n        // Assign as an unmodifiable list. This is effectively immutable.\n        this.protocols = protocolsCopy.unmodifiable()\n      }\n\n    /**\n     * Sets the verifier used to confirm that response certificates apply to requested hostnames for\n     * HTTPS connections.\n     *\n     * If unset, a default hostname verifier will be used.\n     */\n    fun hostnameVerifier(hostnameVerifier: HostnameVerifier) =\n      apply {\n        if (hostnameVerifier != this.hostnameVerifier) {\n          this.routeDatabase = null\n        }\n\n        this.hostnameVerifier = hostnameVerifier\n      }\n\n    /**\n     * Sets the certificate pinner that constrains which certificates are trusted. By default HTTPS\n     * connections rely on only the [SSL socket factory][sslSocketFactory] to establish trust.\n     * Pinning certificates avoids the need to trust certificate authorities.\n     */\n    fun certificatePinner(certificatePinner: CertificatePinner) =\n      apply {\n        if (certificatePinner != this.certificatePinner) {\n          this.routeDatabase = null\n        }\n\n        this.certificatePinner = certificatePinner\n      }\n\n    /**\n     * Sets the default timeout for complete calls. A value of 0 means no timeout, otherwise values\n     * must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The call timeout spans the entire call: resolving DNS, connecting, writing the request body,\n     * server processing, and reading the response body. If the call requires redirects or retries\n     * all must complete within one timeout period.\n     *\n     * The default value is 0 which imposes no timeout.\n     */\n    fun callTimeout(\n      timeout: Long,\n      unit: TimeUnit,\n    ) = apply {\n      callTimeout = checkDuration(\"timeout\", timeout, unit)\n    }\n\n    /**\n     * Sets the default timeout for complete calls. A value of 0 means no timeout, otherwise values\n     * must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The call timeout spans the entire call: resolving DNS, connecting, writing the request body,\n     * server processing, and reading the response body. If the call requires redirects or retries\n     * all must complete within one timeout period.\n     *\n     * The default value is 0 which imposes no timeout.\n     */\n    @Suppress(\"NewApi\")\n    @IgnoreJRERequirement\n    fun callTimeout(duration: Duration) =\n      apply {\n        callTimeout(duration.toMillis(), MILLISECONDS)\n      }\n\n    /**\n     * Sets the default timeout for complete calls. A value of 0 means no timeout, otherwise values\n     * must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The call timeout spans the entire call: resolving DNS, connecting, writing the request body,\n     * server processing, and reading the response body. If the call requires redirects or retries\n     * all must complete within one timeout period.\n     *\n     * The default value is 0 which imposes no timeout.\n     */\n    fun callTimeout(duration: KotlinDuration) =\n      apply {\n        callTimeout = checkDuration(\"duration\", duration)\n      }\n\n    /**\n     * Sets the default connect timeout for new connections. A value of 0 means no timeout,\n     * otherwise values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The connect timeout is applied when connecting a TCP socket to the target host. The default\n     * value is 10 seconds.\n     */\n    fun connectTimeout(\n      timeout: Long,\n      unit: TimeUnit,\n    ) = apply {\n      connectTimeout = checkDuration(\"timeout\", timeout, unit)\n    }\n\n    /**\n     * Sets the default connect timeout for new connections. A value of 0 means no timeout,\n     * otherwise values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The connect timeout is applied when connecting a TCP socket to the target host. The default\n     * value is 10 seconds.\n     */\n    @Suppress(\"NewApi\")\n    @IgnoreJRERequirement\n    fun connectTimeout(duration: Duration) =\n      apply {\n        connectTimeout(duration.toMillis(), MILLISECONDS)\n      }\n\n    /**\n     * Sets the default connect timeout for new connections. A value of 0 means no timeout,\n     * otherwise values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The connect timeout is applied when connecting a TCP socket to the target host. The default\n     * value is 10 seconds.\n     */\n    fun connectTimeout(duration: KotlinDuration) =\n      apply {\n        connectTimeout = checkDuration(\"duration\", duration)\n      }\n\n    /**\n     * Sets the default read timeout for new connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The read timeout is applied to both the TCP socket and for individual read IO operations\n     * including on [Source] of the [Response]. The default value is 10 seconds.\n     *\n     * @see Socket.setSoTimeout\n     * @see Source.timeout\n     */\n    fun readTimeout(\n      timeout: Long,\n      unit: TimeUnit,\n    ) = apply {\n      readTimeout = checkDuration(\"timeout\", timeout, unit)\n    }\n\n    /**\n     * Sets the default read timeout for new connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The read timeout is applied to both the TCP socket and for individual read IO operations\n     * including on [Source] of the [Response]. The default value is 10 seconds.\n     *\n     * @see Socket.setSoTimeout\n     * @see Source.timeout\n     */\n    @Suppress(\"NewApi\")\n    @IgnoreJRERequirement\n    fun readTimeout(duration: Duration) =\n      apply {\n        readTimeout(duration.toMillis(), MILLISECONDS)\n      }\n\n    /**\n     * Sets the default read timeout for new connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The read timeout is applied to both the TCP socket and for individual read IO operations\n     * including on [Source] of the [Response]. The default value is 10 seconds.\n     *\n     * @see Socket.setSoTimeout\n     * @see Source.timeout\n     */\n    fun readTimeout(duration: KotlinDuration) =\n      apply {\n        readTimeout = checkDuration(\"duration\", duration)\n      }\n\n    /**\n     * Sets the default write timeout for new connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The write timeout is applied for individual write IO operations. The default value is 10\n     * seconds.\n     *\n     * @see Sink.timeout\n     */\n    fun writeTimeout(\n      timeout: Long,\n      unit: TimeUnit,\n    ) = apply {\n      writeTimeout = checkDuration(\"timeout\", timeout, unit)\n    }\n\n    /**\n     * Sets the default write timeout for new connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The write timeout is applied for individual write IO operations. The default value is 10\n     * seconds.\n     *\n     * @see Sink.timeout\n     */\n    @Suppress(\"NewApi\")\n    @IgnoreJRERequirement\n    fun writeTimeout(duration: Duration) =\n      apply {\n        writeTimeout(duration.toMillis(), MILLISECONDS)\n      }\n\n    /**\n     * Sets the default write timeout for new connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The write timeout is applied for individual write IO operations. The default value is 10\n     * seconds.\n     *\n     * @see Sink.timeout\n     */\n    fun writeTimeout(duration: KotlinDuration) =\n      apply {\n        writeTimeout = checkDuration(\"duration\", duration)\n      }\n\n    /**\n     * Sets the interval between HTTP/2 and web socket pings initiated by this client. Use this to\n     * automatically send ping frames until either the connection fails or it is closed. This keeps\n     * the connection alive and may detect connectivity failures.\n     *\n     * If the server does not respond to each ping with a pong within `interval`, this client will\n     * assume that connectivity has been lost. When this happens on a web socket the connection is\n     * canceled and its listener is [notified][WebSocketListener.onFailure]. When it happens on an\n     * HTTP/2 connection the connection is closed and any calls it is carrying\n     * [will fail with an IOException][java.io.IOException].\n     *\n     * The default value of 0 disables client-initiated pings.\n     */\n    fun pingInterval(\n      interval: Long,\n      unit: TimeUnit,\n    ) = apply {\n      pingInterval = checkDuration(\"interval\", interval, unit)\n    }\n\n    /**\n     * Sets the interval between HTTP/2 and web socket pings initiated by this client. Use this to\n     * automatically send ping frames until either the connection fails or it is closed. This keeps\n     * the connection alive and may detect connectivity failures.\n     *\n     * If the server does not respond to each ping with a pong within `interval`, this client will\n     * assume that connectivity has been lost. When this happens on a web socket the connection is\n     * canceled and its listener is [notified][WebSocketListener.onFailure]. When it happens on an\n     * HTTP/2 connection the connection is closed and any calls it is carrying\n     * [will fail with an IOException][java.io.IOException].\n     *\n     * The default value of 0 disables client-initiated pings.\n     */\n    @Suppress(\"NewApi\")\n    @IgnoreJRERequirement\n    fun pingInterval(duration: Duration) =\n      apply {\n        pingInterval(duration.toMillis(), MILLISECONDS)\n      }\n\n    /**\n     * Sets the interval between HTTP/2 and web socket pings initiated by this client. Use this to\n     * automatically send ping frames until either the connection fails or it is closed. This keeps\n     * the connection alive and may detect connectivity failures.\n     *\n     * If the server does not respond to each ping with a pong within `interval`, this client will\n     * assume that connectivity has been lost. When this happens on a web socket the connection is\n     * canceled and its listener is [notified][WebSocketListener.onFailure]. When it happens on an\n     * HTTP/2 connection the connection is closed and any calls it is carrying\n     * [will fail with an IOException][java.io.IOException].\n     *\n     * The default value of 0 disables client-initiated pings.\n     */\n    fun pingInterval(duration: KotlinDuration) =\n      apply {\n        pingInterval = checkDuration(\"duration\", duration)\n      }\n\n    /**\n     * Sets the close timeout for web socket connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The close timeout is the maximum amount of time after the client calls [WebSocket.close] to\n     * wait for a graceful shutdown. If the server doesn't respond the web socket will be canceled.\n     * The default value is 60 seconds.\n     */\n    fun webSocketCloseTimeout(\n      timeout: Long,\n      unit: TimeUnit,\n    ) = apply {\n      webSocketCloseTimeout = checkDuration(\"webSocketCloseTimeout\", timeout, unit)\n    }\n\n    /**\n     * Sets the close timeout for web socket connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The close timeout is the maximum amount of time after the client calls [WebSocket.close] to\n     * wait for a graceful shutdown. If the server doesn't respond the web socket will be canceled.\n     * The default value is 60 seconds.\n     */\n    @Suppress(\"NewApi\")\n    @IgnoreJRERequirement\n    fun webSocketCloseTimeout(duration: Duration) =\n      apply {\n        webSocketCloseTimeout(duration.toMillis(), MILLISECONDS)\n      }\n\n    /**\n     * Sets the close timeout for web socket connections. A value of 0 means no timeout, otherwise\n     * values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.\n     *\n     * The close timeout is the maximum amount of time after the client calls [WebSocket.close] to\n     * wait for a graceful shutdown. If the server doesn't respond the web socket will be canceled.\n     * The default value is 60 seconds.\n     */\n    fun webSocketCloseTimeout(duration: KotlinDuration) =\n      apply {\n        webSocketCloseTimeout = checkDuration(\"duration\", duration)\n      }\n\n    /**\n     * Sets minimum outbound web socket message size (in bytes) that will be compressed.\n     *\n     * Set to 0 to enable compression for all outbound messages.\n     *\n     * 1024 by default.\n     */\n    fun minWebSocketMessageToCompress(bytes: Long) =\n      apply {\n        require(bytes >= 0) {\n          \"minWebSocketMessageToCompress must be positive: $bytes\"\n        }\n\n        this.minWebSocketMessageToCompress = bytes\n      }\n\n    fun build(): OkHttpClient = OkHttpClient(this)\n  }\n\n  companion object {\n    internal val DEFAULT_PROTOCOLS = immutableListOf(HTTP_2, HTTP_1_1)\n\n    internal val DEFAULT_CONNECTION_SPECS =\n      immutableListOf(\n        ConnectionSpec.MODERN_TLS,\n        ConnectionSpec.CLEARTEXT,\n      )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Protocol.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okio.IOException\n\n/**\n * Protocols that OkHttp implements for [ALPN][ietf_alpn] selection.\n *\n * ## Protocol vs Scheme\n *\n * Despite its name, [java.net.URL.getProtocol] returns the [scheme][java.net.URI.getScheme] (http,\n * https, etc.) of the URL, not the protocol (http/1.1, spdy/3.1, etc.). OkHttp uses the word\n * *protocol* to identify how HTTP messages are framed.\n *\n * [ietf_alpn]: http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg\n */\nenum class Protocol(\n  private val protocol: String,\n) {\n  /**\n   * An obsolete plaintext framing that does not use persistent sockets by default.\n   */\n  HTTP_1_0(\"http/1.0\"),\n\n  /**\n   * A plaintext framing that includes persistent connections.\n   *\n   * This version of OkHttp implements [RFC 7230][rfc_7230], and tracks revisions to that spec.\n   *\n   * [rfc_7230]: https://tools.ietf.org/html/rfc7230\n   */\n  HTTP_1_1(\"http/1.1\"),\n\n  /**\n   * Chromium's binary-framed protocol that includes header compression, multiplexing multiple\n   * requests on the same socket, and server-push. HTTP/1.1 semantics are layered on SPDY/3.\n   *\n   * Current versions of OkHttp do not support this protocol.\n   */\n  @Deprecated(\"OkHttp has dropped support for SPDY. Prefer {@link #HTTP_2}.\")\n  SPDY_3(\"spdy/3.1\"),\n\n  /**\n   * The IETF's binary-framed protocol that includes header compression, multiplexing multiple\n   * requests on the same socket, and server-push. HTTP/1.1 semantics are layered on HTTP/2.\n   *\n   * HTTP/2 requires deployments of HTTP/2 that use TLS 1.2 support\n   * [CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256], present in Java 8+ and Android 5+.\n   * Servers that enforce this may send an exception message including the string\n   * `INADEQUATE_SECURITY`.\n   */\n  HTTP_2(\"h2\"),\n\n  /**\n   * Cleartext HTTP/2 with no \"upgrade\" round trip. This option requires the client to have prior\n   * knowledge that the server supports cleartext HTTP/2.\n   *\n   * See also [Starting HTTP/2 with Prior Knowledge][rfc_7540_34].\n   *\n   * [rfc_7540_34]: https://datatracker.ietf.org/doc/html/rfc7540#autoid-10\n   */\n  H2_PRIOR_KNOWLEDGE(\"h2_prior_knowledge\"),\n\n  /**\n   * QUIC (Quick UDP Internet Connection) is a new multiplexed and secure transport atop UDP,\n   * designed from the ground up and optimized for HTTP/2 semantics. HTTP/1.1 semantics are layered\n   * on HTTP/2.\n   *\n   * QUIC is not natively supported by OkHttp, but provided to allow a theoretical interceptor that\n   * provides support.\n   */\n  QUIC(\"quic\"),\n\n  /**\n   * HTTP/3 is the third and upcoming major version of the Hypertext Transfer Protocol used to\n   * exchange information. HTTP/3 runs over QUIC, which is published as RFC 9000.\n   *\n   * HTTP/3 is not natively supported by OkHttp, but provided to allow a theoretical interceptor\n   * that provides support.\n   */\n  HTTP_3(\"h3\"),\n  ;\n\n  /**\n   * Returns the string used to identify this protocol for ALPN, like \"http/1.1\", \"spdy/3.1\" or\n   * \"h2\".\n   *\n   * See also [IANA tls-extensiontype-values][iana].\n   *\n   * [iana]: https://www.iana.org/assignments/tls-extensiontype-values\n   */\n  override fun toString(): String = protocol\n\n  companion object {\n    /**\n     * Returns the protocol identified by `protocol`.\n     *\n     * @throws IOException if `protocol` is unknown.\n     */\n    @JvmStatic\n    @Throws(IOException::class)\n    fun get(protocol: String): Protocol {\n      // Unroll the loop over values() to save an allocation.\n      @Suppress(\"DEPRECATION\")\n      return when (protocol) {\n        HTTP_1_0.protocol -> {\n          HTTP_1_0\n        }\n\n        HTTP_1_1.protocol -> {\n          HTTP_1_1\n        }\n\n        H2_PRIOR_KNOWLEDGE.protocol -> {\n          H2_PRIOR_KNOWLEDGE\n        }\n\n        HTTP_2.protocol -> {\n          HTTP_2\n        }\n\n        SPDY_3.protocol -> {\n          SPDY_3\n        }\n\n        QUIC.protocol -> {\n          QUIC\n        }\n\n        else -> {\n          // Support HTTP3 draft like h3-29\n          if (protocol.startsWith(HTTP_3.protocol)) HTTP_3 else throw IOException(\"Unexpected protocol: $protocol\")\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.net.URL\nimport kotlin.reflect.KClass\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.internal.EmptyTags\nimport okhttp3.internal.Tags\nimport okhttp3.internal.http.GzipRequestBody\nimport okhttp3.internal.http.HttpMethod\nimport okhttp3.internal.isProbablyUtf8\nimport okhttp3.internal.isSensitiveHeader\nimport okio.Buffer\n\n/**\n * An HTTP request. Instances of this class are immutable if their [body] is null or itself\n * immutable.\n */\nclass Request internal constructor(\n  builder: Builder,\n) {\n  @get:JvmName(\"url\")\n  val url: HttpUrl = checkNotNull(builder.url) { \"url == null\" }\n\n  @get:JvmName(\"method\")\n  val method: String = builder.method\n\n  @get:JvmName(\"headers\")\n  val headers: Headers = builder.headers.build()\n\n  @get:JvmName(\"body\")\n  val body: RequestBody? = builder.body\n\n  @get:JvmName(\"cacheUrlOverride\")\n  val cacheUrlOverride: HttpUrl? = builder.cacheUrlOverride\n\n  internal val tags = builder.tags\n\n  private var lazyCacheControl: CacheControl? = null\n\n  val isHttps: Boolean\n    get() = url.isHttps\n\n  /**\n   * Constructs a new request.\n   *\n   * Use [Builder] for more fluent construction, including helper methods for various HTTP methods.\n   *\n   * @param method defaults to \"GET\" if [body] is null, and \"POST\" otherwise.\n   */\n  constructor(\n    url: HttpUrl,\n    headers: Headers = headersOf(),\n    // '\\u0000' is a sentinel value that'll choose based on what the body is:\n    method: String = \"\\u0000\",\n    body: RequestBody? = null,\n  ) : this(\n    Builder()\n      .url(url)\n      .headers(headers)\n      .method(\n        when {\n          method != \"\\u0000\" -> method\n          body != null -> \"POST\"\n          else -> \"GET\"\n        },\n        body,\n      ),\n  )\n\n  fun header(name: String): String? = headers[name]\n\n  fun headers(name: String): List<String> = headers.values(name)\n\n  /** Returns the tag attached with [T] as a key, or null if no tag is attached with that key. */\n  @JvmName(\"reifiedTag\")\n  inline fun <reified T : Any> tag(): T? = tag(T::class)\n\n  /** Returns the tag attached with [type] as a key, or null if no tag is attached with that key. */\n  fun <T : Any> tag(type: KClass<T>): T? = type.java.cast(tags[type])\n\n  /**\n   * Returns the tag attached with `Object.class` as a key, or null if no tag is attached with\n   * that key.\n   *\n   * Prior to OkHttp 3.11, this method never returned null if no tag was attached. Instead it\n   * returned either this request, or the request upon which this request was derived with\n   * [newBuilder].\n   *\n   * @suppress this method breaks Dokka! https://github.com/Kotlin/dokka/issues/2473\n   */\n  fun tag(): Any? = tag<Any>()\n\n  /**\n   * Returns the tag attached with [type] as a key, or null if no tag is attached with that\n   * key.\n   */\n  fun <T> tag(type: Class<out T>): T? = tag(type.kotlin)\n\n  fun newBuilder(): Builder = Builder(this)\n\n  /**\n   * Returns the cache control directives for this response. This is never null, even if this\n   * response contains no `Cache-Control` header.\n   */\n  @get:JvmName(\"cacheControl\")\n  val cacheControl: CacheControl\n    get() {\n      var result = lazyCacheControl\n      if (result == null) {\n        result = CacheControl.parse(headers)\n        lazyCacheControl = result\n      }\n      return result\n    }\n\n  @JvmName(\"-deprecated_url\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"url\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun url(): HttpUrl = url\n\n  @JvmName(\"-deprecated_method\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"method\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun method(): String = method\n\n  @JvmName(\"-deprecated_headers\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"headers\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun headers(): Headers = headers\n\n  @JvmName(\"-deprecated_body\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"body\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun body(): RequestBody? = body\n\n  @JvmName(\"-deprecated_cacheControl\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"cacheControl\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun cacheControl(): CacheControl = cacheControl\n\n  override fun toString(): String =\n    buildString(32) {\n      append(\"Request{method=\")\n      append(method)\n      append(\", url=\")\n      append(url)\n      if (headers.size != 0) {\n        append(\", headers=[\")\n        headers.forEachIndexed { index, (name, value) ->\n          if (index > 0) {\n            append(\", \")\n          }\n          append(name)\n          append(':')\n          append(if (isSensitiveHeader(name)) \"██\" else value)\n        }\n        append(']')\n      }\n      if (tags != EmptyTags) {\n        append(\", tags=\")\n        append(tags)\n      }\n      append('}')\n    }\n\n  open class Builder {\n    internal var url: HttpUrl? = null\n    internal var method: String\n    internal var headers: Headers.Builder\n    internal var body: RequestBody? = null\n    internal var cacheUrlOverride: HttpUrl? = null\n    internal var tags: Tags = EmptyTags\n\n    constructor() {\n      this.method = \"GET\"\n      this.headers = Headers.Builder()\n    }\n\n    internal constructor(request: Request) {\n      this.url = request.url\n      this.method = request.method\n      this.body = request.body\n      this.tags = request.tags\n      this.headers = request.headers.newBuilder()\n      this.cacheUrlOverride = request.cacheUrlOverride\n    }\n\n    open fun url(url: HttpUrl): Builder =\n      apply {\n        this.url = url\n      }\n\n    /**\n     * Sets the URL target of this request.\n     *\n     * @throws IllegalArgumentException if [url] is not a valid HTTP or HTTPS URL. Avoid this\n     *     exception by calling [HttpUrl.parse]; it returns null for invalid URLs.\n     */\n    open fun url(url: String): Builder = url(canonicalUrl(url).toHttpUrl())\n\n    // Silently replace web socket URLs with HTTP URLs.\n    private fun canonicalUrl(url: String) =\n      when {\n        url.startsWith(\"ws:\", ignoreCase = true) -> \"http:${url.substring(3)}\"\n        url.startsWith(\"wss:\", ignoreCase = true) -> \"https:${url.substring(4)}\"\n        else -> url\n      }\n\n    /**\n     * Sets the URL target of this request.\n     *\n     * @throws IllegalArgumentException if the scheme of [url] is not `http` or `https`.\n     */\n    open fun url(url: URL) = url(url.toString().toHttpUrl())\n\n    /**\n     * Sets the header named [name] to [value]. If this request already has any headers\n     * with that name, they are all replaced.\n     */\n    open fun header(\n      name: String,\n      value: String,\n    ) = apply {\n      headers[name] = value\n    }\n\n    /**\n     * Adds a header with [name] and [value]. Prefer this method for multiply-valued\n     * headers like \"Cookie\".\n     *\n     * Note that for some headers including `Content-Length` and `Content-Encoding`,\n     * OkHttp may replace [value] with a header derived from the request body.\n     */\n    open fun addHeader(\n      name: String,\n      value: String,\n    ) = apply {\n      headers.add(name, value)\n    }\n\n    /** Removes all headers named [name] on this builder. */\n    open fun removeHeader(name: String) =\n      apply {\n        headers.removeAll(name)\n      }\n\n    /** Removes all headers on this builder and adds [headers]. */\n    open fun headers(headers: Headers) =\n      apply {\n        this.headers = headers.newBuilder()\n      }\n\n    /**\n     * Sets this request's `Cache-Control` header, replacing any cache control headers already\n     * present. If [cacheControl] doesn't define any directives, this clears this request's\n     * cache-control headers.\n     */\n    open fun cacheControl(cacheControl: CacheControl): Builder {\n      val value = cacheControl.toString()\n      return when {\n        value.isEmpty() -> removeHeader(\"Cache-Control\")\n        else -> header(\"Cache-Control\", value)\n      }\n    }\n\n    open fun get(): Builder = method(\"GET\", null)\n\n    open fun head(): Builder = method(\"HEAD\", null)\n\n    open fun post(body: RequestBody): Builder = method(\"POST\", body)\n\n    @JvmOverloads\n    open fun delete(body: RequestBody? = RequestBody.EMPTY): Builder = method(\"DELETE\", body)\n\n    open fun put(body: RequestBody): Builder = method(\"PUT\", body)\n\n    open fun patch(body: RequestBody): Builder = method(\"PATCH\", body)\n\n    /**\n     * Sets this request's method to `QUERY`.\n     *\n     * By default, `QUERY` requests are not cached. You can use [cacheUrlOverride] to specify\n     * how to cache them.\n     *\n     * A typical use case is to hash the request body:\n     *\n     * ```kotlin\n     *     val hash = body.sha256().hex()\n     *     val query = Request\n     *         .Builder()\n     *         .query(body)\n     *         .url(\"https://example.com/query\")\n     *         .cacheUrlOverride(\"https://example.com/query/$hash\".toHttpUrl())\n     *         .build()\n     * ```\n     *\n     * @see cacheUrlOverride\n     */\n    open fun query(body: RequestBody): Builder = method(\"QUERY\", body)\n\n    open fun method(\n      method: String,\n      body: RequestBody?,\n    ): Builder =\n      apply {\n        require(method.isNotEmpty()) {\n          \"method.isEmpty() == true\"\n        }\n        if (body == null) {\n          require(!HttpMethod.requiresRequestBody(method)) {\n            \"method $method must have a request body.\"\n          }\n        } else {\n          require(HttpMethod.permitsRequestBody(method)) {\n            \"method $method must not have a request body.\"\n          }\n        }\n        this.method = method\n        this.body = body\n      }\n\n    /**\n     * Attaches [tag] to the request using [T] as a key. Tags can be read from a request using\n     * [Request.tag]. Use null to remove any existing tag assigned for [T].\n     *\n     * Use this API to attach timing, debugging, or other application data to a request so that\n     * you may read it in interceptors, event listeners, or callbacks.\n     */\n    @JvmName(\"reifiedTag\")\n    inline fun <reified T : Any> tag(tag: T?): Builder = tag(T::class, tag)\n\n    /**\n     * Attaches [tag] to the request using [type] as a key. Tags can be read from a request using\n     * [Request.tag]. Use null to remove any existing tag assigned for [type].\n     *\n     * Use this API to attach timing, debugging, or other application data to a request so that\n     * you may read it in interceptors, event listeners, or callbacks.\n     */\n    fun <T : Any> tag(\n      type: KClass<T>,\n      tag: T?,\n    ): Builder =\n      apply {\n        tags = tags.plus(type, tag)\n      }\n\n    /** Attaches [tag] to the request using `Object.class` as a key. */\n    open fun tag(tag: Any?): Builder = tag(Any::class, tag)\n\n    /**\n     * Attaches [tag] to the request using [type] as a key. Tags can be read from a\n     * request using [Request.tag]. Use null to remove any existing tag assigned for [type].\n     *\n     * Use this API to attach timing, debugging, or other application data to a request so that\n     * you may read it in interceptors, event listeners, or callbacks.\n     */\n    open fun <T> tag(\n      type: Class<in T>,\n      tag: T?,\n    ) = tag(type.kotlin, tag)\n\n    /**\n     * Override the [Request.url] for caching, if it is either polluted with\n     * transient query params, or has a canonical URL possibly for a CDN.\n     *\n     * Note that POST requests will not be sent to the server if this URL is set\n     * and matches a cached response.\n     */\n    fun cacheUrlOverride(cacheUrlOverride: HttpUrl?) =\n      apply {\n        this.cacheUrlOverride = cacheUrlOverride\n      }\n\n    /**\n     * Configures this request's body to be compressed when it is transmitted. This also adds the\n     * 'Content-Encoding: gzip' header.\n     *\n     * Only use this method if you have prior knowledge that the receiving server supports\n     * gzip-compressed requests.\n     *\n     * It is an error to call this multiple times on the same instance.\n     *\n     * @throws IllegalStateException if this request doesn't have a request body, or if it already\n     *     has a 'Content-Encoding' header.\n     */\n    fun gzip() =\n      apply {\n        val identityBody =\n          body\n            ?: throw IllegalStateException(\"cannot gzip a request that has no body\")\n\n        val contentEncoding = headers[\"Content-Encoding\"]\n        check(contentEncoding == null) {\n          \"Content-Encoding already set: $contentEncoding\"\n        }\n\n        headers.add(\"Content-Encoding\", \"gzip\")\n        body = GzipRequestBody(identityBody)\n      }\n\n    open fun build(): Request = Request(this)\n  }\n\n  /**\n   * Returns a cURL command equivalent to this request, useful for debugging and reproducing\n   * requests.\n   *\n   * This includes the HTTP method, headers, request body (if present), and URL.\n   *\n   * Example:\n   *\n   * ```\n   * curl 'https://example.com/api' \\\n   *   -X PUT \\\n   *   -H 'Authorization: Bearer token' \\\n   *   --data '{\\\"key\\\":\\\"value\\\"}'\n   * ```\n   *\n   * **Note:** This will consume the request body. This may have side effects if the [RequestBody]\n   * is streaming or can be consumed only once.\n   */\n  @JvmOverloads\n  fun toCurl(includeBody: Boolean = true): String =\n    buildString {\n      append(\"curl ${url.toString().shellEscape()}\")\n\n      val contentType = body?.contentType()?.toString()\n\n      // Add method if not the default.\n      val defaultMethod =\n        when {\n          includeBody && body != null -> \"POST\"\n          else -> \"GET\"\n        }\n      if (method != defaultMethod) {\n        append(\" \\\\\\n  -X ${method.shellEscape()}\")\n      }\n\n      // Append headers.\n      for ((name, value) in headers) {\n        if (contentType != null && name.equals(\"Content-Type\", ignoreCase = true)) continue\n        append(\" \\\\\\n  -H ${\"$name: $value\".shellEscape()}\")\n      }\n\n      if (contentType != null) {\n        append(\" \\\\\\n  -H ${\"Content-Type: $contentType\".shellEscape()}\")\n      }\n\n      // Append body if present.\n      if (includeBody && body != null) {\n        val bodyBuffer = Buffer()\n        body.writeTo(bodyBuffer)\n\n        if (bodyBuffer.isProbablyUtf8()) {\n          append(\" \\\\\\n  --data ${bodyBuffer.readUtf8().shellEscape()}\")\n        } else {\n          append(\" \\\\\\n  --data-binary ${bodyBuffer.readByteString().hex().shellEscape()}\")\n        }\n      }\n    }\n\n  private fun String.shellEscape(): String = \"'${replace(\"'\", \"'\\\\''\")}'\"\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/RequestBody.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.File\nimport java.io.FileDescriptor\nimport java.io.FileInputStream\nimport java.io.IOException\nimport okhttp3.internal.checkOffsetAndCount\nimport okhttp3.internal.chooseCharset\nimport okio.BufferedSink\nimport okio.ByteString\nimport okio.FileSystem\nimport okio.HashingSink\nimport okio.Path\nimport okio.blackholeSink\nimport okio.buffer\nimport okio.source\n\nabstract class RequestBody {\n  /** Returns the Content-Type header for this body. */\n  abstract fun contentType(): MediaType?\n\n  /**\n   * Returns the number of bytes that will be written to sink in a call to [writeTo],\n   * or -1 if that count is unknown.\n   */\n  @Throws(IOException::class)\n  open fun contentLength(): Long = -1L\n\n  /** Writes the content of this request to [sink]. */\n  @Throws(IOException::class)\n  abstract fun writeTo(sink: BufferedSink)\n\n  /**\n   * A duplex request body is special in how it is **transmitted** on the network and\n   * in the **API contract** between OkHttp and the application.\n   *\n   * This method returns false unless it is overridden by a subclass.\n   *\n   * ### Duplex Transmission\n   *\n   * With regular HTTP calls the request always completes sending before the response may begin\n   * receiving. With duplex the request and response may be interleaved! That is, request body bytes\n   * may be sent after response headers or body bytes have been received.\n   *\n   * Though any call may be initiated as a duplex call, only web servers that are specially\n   * designed for this nonstandard interaction will use it. As of 2019-01, the only widely-used\n   * implementation of this pattern is [gRPC][grpc].\n   *\n   * Because the encoding of interleaved data is not well-defined for HTTP/1, duplex request\n   * bodies may only be used with HTTP/2. Calls to HTTP/1 servers will fail before the HTTP request\n   * is transmitted. If you cannot ensure that your client and server both support HTTP/2, do not\n   * use this feature.\n   *\n   * ### Duplex APIs\n   *\n   * With regular request bodies it is not legal to write bytes to the sink passed to\n   * [RequestBody.writeTo] after that method returns. For duplex requests bodies that condition is\n   * lifted. Such writes occur on an application-provided thread and may occur concurrently with\n   * reads of the [ResponseBody]. For duplex request bodies, [writeTo] should return\n   * quickly, possibly by handing off the provided request body to another thread to perform\n   * writing.\n   *\n   * [grpc]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md\n   */\n  open fun isDuplex(): Boolean = false\n\n  /**\n   * Returns true if this body expects at most one call to [writeTo] and can be transmitted\n   * at most once. This is typically used when writing the request body is destructive and it is not\n   * possible to recreate the request body after it has been sent.\n   *\n   * This method returns false unless it is overridden by a subclass.\n   *\n   * By default OkHttp will attempt to retransmit request bodies when the original request fails\n   * due to any of:\n   *\n   *  * A stale connection. The request was made on a reused connection and that reused connection\n   *    has since been closed by the server.\n   *  * A client timeout (HTTP 408).\n   *  * A authorization challenge (HTTP 401 and 407) that is satisfied by the [Authenticator].\n   *  * A retryable server failure (HTTP 503 with a `Retry-After: 0` response header).\n   *  * A misdirected request (HTTP 421) on a coalesced connection.\n   */\n  open fun isOneShot(): Boolean = false\n\n  /**\n   * Returns the SHA-256 hash of this [RequestBody]\n   */\n  @Throws(IOException::class)\n  fun sha256(): ByteString {\n    val hashingSink = HashingSink.sha256(blackholeSink())\n    hashingSink.buffer().use {\n      this.writeTo(it)\n    }\n    return hashingSink.hash\n  }\n\n  companion object {\n    /** Empty request body with no content-type. */\n    @JvmField\n    val EMPTY: RequestBody = ByteString.EMPTY.toRequestBody()\n\n    /**\n     * Returns a new request body that transmits this string. If [contentType] is non-null and lacks\n     * a charset, this will use UTF-8.\n     */\n    @JvmStatic\n    @JvmName(\"create\")\n    fun String.toRequestBody(contentType: MediaType? = null): RequestBody {\n      val (charset, finalContentType) = contentType.chooseCharset()\n      val bytes = toByteArray(charset)\n      return bytes.toRequestBody(finalContentType, 0, bytes.size)\n    }\n\n    @JvmStatic\n    @JvmName(\"create\")\n    fun ByteString.toRequestBody(contentType: MediaType? = null): RequestBody =\n      object : RequestBody() {\n        override fun contentType() = contentType\n\n        override fun contentLength() = size.toLong()\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.write(this@toRequestBody)\n        }\n      }\n\n    /** Returns a new request body that transmits this. */\n    @JvmStatic\n    @JvmName(\"create\")\n    fun FileDescriptor.toRequestBody(contentType: MediaType? = null): RequestBody =\n      object : RequestBody() {\n        override fun contentType() = contentType\n\n        override fun isOneShot(): Boolean = true\n\n        override fun writeTo(sink: BufferedSink) {\n          FileInputStream(this@toRequestBody).use {\n            sink.buffer.writeAll(it.source())\n          }\n        }\n      }\n\n    /** Returns a new request body that transmits this. */\n    @JvmOverloads\n    @JvmStatic\n    @JvmName(\"create\")\n    fun ByteArray.toRequestBody(\n      contentType: MediaType? = null,\n      offset: Int = 0,\n      byteCount: Int = size,\n    ): RequestBody {\n      checkOffsetAndCount(size.toLong(), offset.toLong(), byteCount.toLong())\n      return object : RequestBody() {\n        override fun contentType() = contentType\n\n        override fun contentLength() = byteCount.toLong()\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.write(this@toRequestBody, offset, byteCount)\n        }\n      }\n    }\n\n    /** Returns a new request body that transmits the content of this. */\n    @JvmStatic\n    @JvmName(\"create\")\n    fun File.asRequestBody(contentType: MediaType? = null): RequestBody =\n      object : RequestBody() {\n        override fun contentType() = contentType\n\n        override fun contentLength() = length()\n\n        override fun writeTo(sink: BufferedSink) {\n          source().use { source -> sink.writeAll(source) }\n        }\n      }\n\n    /** Returns a new request body that transmits the content of this. */\n    @JvmStatic\n    @JvmName(\"create\")\n    fun Path.asRequestBody(\n      fileSystem: FileSystem,\n      contentType: MediaType? = null,\n    ): RequestBody =\n      object : RequestBody() {\n        override fun contentType() = contentType\n\n        override fun contentLength() = fileSystem.metadata(this@asRequestBody).size ?: -1\n\n        override fun writeTo(sink: BufferedSink) {\n          fileSystem.source(this@asRequestBody).use { source -> sink.writeAll(source) }\n        }\n      }\n\n    @JvmStatic\n    @Deprecated(\n      message = \"Moved to extension function. Put the 'content' argument first to fix Java\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"content.toRequestBody(contentType)\",\n          imports = [\"okhttp3.RequestBody.Companion.toRequestBody\"],\n        ),\n      level = DeprecationLevel.WARNING,\n    )\n    fun create(\n      contentType: MediaType?,\n      content: String,\n    ): RequestBody = content.toRequestBody(contentType)\n\n    @JvmStatic\n    @Deprecated(\n      message = \"Moved to extension function. Put the 'content' argument first to fix Java\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"content.toRequestBody(contentType)\",\n          imports = [\"okhttp3.RequestBody.Companion.toRequestBody\"],\n        ),\n      level = DeprecationLevel.WARNING,\n    )\n    fun create(\n      contentType: MediaType?,\n      content: ByteString,\n    ): RequestBody = content.toRequestBody(contentType)\n\n    @JvmOverloads\n    @JvmStatic\n    @Deprecated(\n      message = \"Moved to extension function. Put the 'content' argument first to fix Java\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"content.toRequestBody(contentType, offset, byteCount)\",\n          imports = [\"okhttp3.RequestBody.Companion.toRequestBody\"],\n        ),\n      level = DeprecationLevel.WARNING,\n    )\n    fun create(\n      contentType: MediaType?,\n      content: ByteArray,\n      offset: Int = 0,\n      byteCount: Int = content.size,\n    ): RequestBody = content.toRequestBody(contentType, offset, byteCount)\n\n    @JvmStatic\n    @Deprecated(\n      message = \"Moved to extension function. Put the 'file' argument first to fix Java\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"file.asRequestBody(contentType)\",\n          imports = [\"okhttp3.RequestBody.Companion.asRequestBody\"],\n        ),\n      level = DeprecationLevel.WARNING,\n    )\n    fun create(\n      contentType: MediaType?,\n      file: File,\n    ): RequestBody = file.asRequestBody(contentType)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Response.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.net.HttpURLConnection.HTTP_MOVED_PERM\nimport java.net.HttpURLConnection.HTTP_MOVED_TEMP\nimport java.net.HttpURLConnection.HTTP_MULT_CHOICE\nimport java.net.HttpURLConnection.HTTP_PROXY_AUTH\nimport java.net.HttpURLConnection.HTTP_SEE_OTHER\nimport java.net.HttpURLConnection.HTTP_UNAUTHORIZED\nimport okhttp3.ResponseBody.Companion.asResponseBody\nimport okhttp3.internal.connection.Exchange\nimport okhttp3.internal.http.HTTP_PERM_REDIRECT\nimport okhttp3.internal.http.HTTP_TEMP_REDIRECT\nimport okhttp3.internal.http.parseChallenges\nimport okio.Buffer\nimport okio.Socket\n\n/**\n * An HTTP response. Instances of this class are not immutable: the response body is a one-shot\n * value that may be consumed only once and then closed. All other properties are immutable.\n *\n * This class implements [Closeable]. Closing it simply closes its response body. See\n * [ResponseBody] for an explanation and examples.\n */\nclass Response internal constructor(\n  /**\n   * The request that initiated this HTTP response. This is not necessarily the same request issued\n   * by the application:\n   *\n   * * It may be transformed by the user's interceptors. For example, an application interceptor\n   *   may add headers like `User-Agent`.\n   * * It may be the request generated in response to an HTTP redirect or authentication\n   *   challenge. In this case the request URL may be different than the initial request URL.\n   *\n   * Use the `request` of the [networkResponse] field to get the wire-level request that was\n   * transmitted. In the case of follow-ups and redirects, also look at the `request` of the\n   * [priorResponse] objects, which have its own [priorResponse].\n   */\n  @get:JvmName(\"request\") val request: Request,\n  /** Returns the HTTP protocol, such as [Protocol.HTTP_1_1] or [Protocol.HTTP_1_0]. */\n  @get:JvmName(\"protocol\") val protocol: Protocol,\n  /** Returns the HTTP status message. */\n  @get:JvmName(\"message\") val message: String,\n  /** Returns the HTTP status code. */\n  @get:JvmName(\"code\") val code: Int,\n  /**\n   * Returns the TLS handshake of the connection that carried this response, or null if the\n   * response was received without TLS.\n   */\n  @get:JvmName(\"handshake\") val handshake: Handshake?,\n  /** Returns the HTTP headers. */\n  @get:JvmName(\"headers\") val headers: Headers,\n  /**\n   * Returns a non-null stream with the server's response. The returned value must be\n   * [closed][ResponseBody] and may be consumed only once.\n   *\n   * If this is a [cacheResponse], [networkResponse], or [priorResponse], the server's response body\n   * is not available, and it is always an error to attempt read its streamed content. Reading from\n   * [ResponseBody.source] always throws on such instances.\n   *\n   * It is safe and supported to call [ResponseBody.contentType] and [ResponseBody.contentLength] on\n   * all instances of [ResponseBody].\n   */\n  @get:JvmName(\"body\") val body: ResponseBody,\n  /**\n   * Non-null if this response is a successful upgrade ...\n   */\n  @get:JvmName(\"socket\") val socket: Socket?,\n  /**\n   * Returns the raw response received from the network. Will be null if this response didn't use\n   * the network, such as when the response is fully cached. The body of the returned response\n   * should not be read.\n   */\n  @get:JvmName(\"networkResponse\") val networkResponse: Response?,\n  /**\n   * Returns the raw response received from the cache. Will be null if this response didn't use\n   * the cache. For conditional get requests the cache response and network response may both be\n   * non-null. The body of the returned response should not be read.\n   */\n  @get:JvmName(\"cacheResponse\") val cacheResponse: Response?,\n  /**\n   * Returns the response for the HTTP redirect or authorization challenge that triggered this\n   * response, or null if this response wasn't triggered by an automatic retry. The body of the\n   * returned response should not be read because it has already been consumed by the redirecting\n   * client.\n   */\n  @get:JvmName(\"priorResponse\") val priorResponse: Response?,\n  /**\n   * Returns a [timestamp][System.currentTimeMillis] taken immediately before OkHttp\n   * transmitted the initiating request over the network. If this response is being served from the\n   * cache then this is the timestamp of the original request.\n   */\n  @get:JvmName(\"sentRequestAtMillis\") val sentRequestAtMillis: Long,\n  /**\n   * Returns a [timestamp][System.currentTimeMillis] taken immediately after OkHttp\n   * received this response's headers from the network. If this response is being served from the\n   * cache then this is the timestamp of the original response.\n   */\n  @get:JvmName(\"receivedResponseAtMillis\") val receivedResponseAtMillis: Long,\n  @get:JvmName(\"exchange\") internal val exchange: Exchange?,\n  private var trailersSource: TrailersSource,\n) : Closeable {\n  internal var lazyCacheControl: CacheControl? = null\n\n  @JvmName(\"-deprecated_request\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"request\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun request(): Request = request\n\n  @JvmName(\"-deprecated_protocol\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"protocol\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun protocol(): Protocol = protocol\n\n  @JvmName(\"-deprecated_code\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"code\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun code(): Int = code\n\n  /**\n   * Returns true if the code is in [200..300), which means the request was successfully received,\n   * understood, and accepted.\n   */\n  val isSuccessful: Boolean = code in 200..299\n\n  @JvmName(\"-deprecated_message\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"message\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun message(): String = message\n\n  @JvmName(\"-deprecated_handshake\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"handshake\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun handshake(): Handshake? = handshake\n\n  fun headers(name: String): List<String> = headers.values(name)\n\n  @JvmOverloads\n  fun header(\n    name: String,\n    defaultValue: String? = null,\n  ): String? = headers[name] ?: defaultValue\n\n  @JvmName(\"-deprecated_headers\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"headers\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun headers(): Headers = headers\n\n  /**\n   * Returns the trailers after the HTTP response, which may be empty. This blocks until the\n   * trailers are available to read.\n   *\n   * It is not safe to call this concurrently with code that is processing the response body. If you\n   * call this without consuming the complete response body, any remaining bytes in the response\n   * body will be discarded before trailers are returned.\n   *\n   * If [Call.cancel] is called while this is blocking, this call will immediately throw.\n   *\n   * @throws IllegalStateException if the response is closed.\n   * @throws IOException if the trailers cannot be loaded, such as if the network connection is\n   *     dropped.\n   */\n  @Throws(IOException::class)\n  fun trailers(): Headers = trailersSource.get()\n\n  /**\n   * Returns the trailers after the HTTP response, if they are available to read immediately. Unlike\n   * [trailers], this doesn't block if the trailers are not immediately available, and instead\n   * returns null.\n   *\n   * This will typically return null until [ResponseBody.source] has buffered the last byte of the\n   * response body. Call `body.source().request(1024 * 1024)` to block until either that's done, or\n   * 1 MiB of response data is loaded into memory. (You could use any size here, though large values\n   * risk exhausting memory.)\n   *\n   * This returns an empty value if the trailers are available, but have no data.\n   *\n   * It is not safe to call this concurrently with code that is processing the response body.\n   *\n   * @throws IOException if the trailers cannot be loaded, such as if the network connection is\n   *     dropped.\n   */\n  @Throws(IOException::class)\n  fun peekTrailers(): Headers? = trailersSource.peek()\n\n  /**\n   * Peeks up to [byteCount] bytes from the response body and returns them as a new response\n   * body. If fewer than [byteCount] bytes are in the response body, the full response body is\n   * returned. If more than [byteCount] bytes are in the response body, the returned value\n   * will be truncated to [byteCount] bytes.\n   *\n   * It is an error to call this method after the body has been consumed.\n   *\n   * **Warning:** this method loads the requested bytes into memory. Most applications should set\n   * a modest limit on `byteCount`, such as 1 MiB.\n   */\n  @Throws(IOException::class)\n  fun peekBody(byteCount: Long): ResponseBody {\n    val peeked = body.source().peek()\n    val buffer = Buffer()\n    peeked.request(byteCount)\n    buffer.write(peeked, minOf(byteCount, peeked.buffer.size))\n    return buffer.asResponseBody(body.contentType(), buffer.size)\n  }\n\n  @JvmName(\"-deprecated_body\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"body\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun body() = body\n\n  fun newBuilder(): Builder = Builder(this)\n\n  /** Returns true if this response redirects to another resource. */\n  val isRedirect: Boolean =\n    when (code) {\n      HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> true\n      else -> false\n    }\n\n  @JvmName(\"-deprecated_networkResponse\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"networkResponse\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun networkResponse(): Response? = networkResponse\n\n  @JvmName(\"-deprecated_cacheResponse\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"cacheResponse\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun cacheResponse(): Response? = cacheResponse\n\n  @JvmName(\"-deprecated_priorResponse\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"priorResponse\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun priorResponse(): Response? = priorResponse\n\n  /**\n   * Returns the RFC 7235 authorization challenges appropriate for this response's code. If the\n   * response code is 401 unauthorized, this returns the \"WWW-Authenticate\" challenges. If the\n   * response code is 407 proxy unauthorized, this returns the \"Proxy-Authenticate\" challenges.\n   * Otherwise this returns an empty list of challenges.\n   *\n   * If a challenge uses the `token68` variant instead of auth params, there is exactly one\n   * auth param in the challenge at key null. Invalid headers and challenges are ignored.\n   * No semantic validation is done, for example that `Basic` auth must have a `realm`\n   * auth param, this is up to the caller that interprets these challenges.\n   */\n  fun challenges(): List<Challenge> {\n    return headers.parseChallenges(\n      when (code) {\n        HTTP_UNAUTHORIZED -> \"WWW-Authenticate\"\n        HTTP_PROXY_AUTH -> \"Proxy-Authenticate\"\n        else -> return emptyList()\n      },\n    )\n  }\n\n  /**\n   * Returns the cache control directives for this response. This is never null, even if this\n   * response contains no `Cache-Control` header.\n   */\n  @get:JvmName(\"cacheControl\")\n  val cacheControl: CacheControl\n    get() {\n      var result = lazyCacheControl\n      if (result == null) {\n        result = CacheControl.parse(headers)\n        lazyCacheControl = result\n      }\n      return result\n    }\n\n  @JvmName(\"-deprecated_cacheControl\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"cacheControl\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun cacheControl(): CacheControl = cacheControl\n\n  @JvmName(\"-deprecated_sentRequestAtMillis\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"sentRequestAtMillis\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun sentRequestAtMillis(): Long = sentRequestAtMillis\n\n  @JvmName(\"-deprecated_receivedResponseAtMillis\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"receivedResponseAtMillis\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun receivedResponseAtMillis(): Long = receivedResponseAtMillis\n\n  /**\n   * Closes the response body. Equivalent to `body().close()`.\n   *\n   * Prior to OkHttp 5.0, it was an error to close a response that is not eligible for a body. This\n   * includes the responses returned from [cacheResponse], [networkResponse], and [priorResponse].\n   */\n  override fun close() {\n    body.close()\n  }\n\n  override fun toString(): String = \"Response{protocol=$protocol, code=$code, message=$message, url=${request.url}}\"\n\n  open class Builder {\n    internal var request: Request? = null\n    internal var protocol: Protocol? = null\n    internal var code = -1\n    internal var message: String? = null\n    internal var handshake: Handshake? = null\n    internal var headers: Headers.Builder\n    internal var body: ResponseBody = ResponseBody.EMPTY\n    internal var socket: Socket? = null\n    internal var networkResponse: Response? = null\n    internal var cacheResponse: Response? = null\n    internal var priorResponse: Response? = null\n    internal var sentRequestAtMillis: Long = 0\n    internal var receivedResponseAtMillis: Long = 0\n    internal var exchange: Exchange? = null\n    internal var trailersSource: TrailersSource = TrailersSource.EMPTY\n\n    constructor() {\n      headers = Headers.Builder()\n    }\n\n    internal constructor(response: Response) {\n      this.request = response.request\n      this.protocol = response.protocol\n      this.code = response.code\n      this.message = response.message\n      this.handshake = response.handshake\n      this.headers = response.headers.newBuilder()\n      this.body = response.body\n      this.socket = response.socket\n      this.networkResponse = response.networkResponse\n      this.cacheResponse = response.cacheResponse\n      this.priorResponse = response.priorResponse\n      this.sentRequestAtMillis = response.sentRequestAtMillis\n      this.receivedResponseAtMillis = response.receivedResponseAtMillis\n      this.exchange = response.exchange\n      this.trailersSource = response.trailersSource\n    }\n\n    open fun request(request: Request) =\n      apply {\n        this.request = request\n      }\n\n    open fun protocol(protocol: Protocol) =\n      apply {\n        this.protocol = protocol\n      }\n\n    open fun code(code: Int) =\n      apply {\n        this.code = code\n      }\n\n    open fun message(message: String) =\n      apply {\n        this.message = message\n      }\n\n    open fun handshake(handshake: Handshake?) =\n      apply {\n        this.handshake = handshake\n      }\n\n    /**\n     * Sets the header named [name] to [value]. If this request already has any headers\n     * with that name, they are all replaced.\n     */\n    open fun header(\n      name: String,\n      value: String,\n    ) = apply {\n      headers[name] = value\n    }\n\n    /**\n     * Adds a header with [name] to [value]. Prefer this method for multiply-valued\n     * headers like \"Set-Cookie\".\n     */\n    open fun addHeader(\n      name: String,\n      value: String,\n    ) = apply {\n      headers.add(name, value)\n    }\n\n    /** Removes all headers named [name] on this builder. */\n    open fun removeHeader(name: String) =\n      apply {\n        headers.removeAll(name)\n      }\n\n    /** Removes all headers on this builder and adds [headers]. */\n    open fun headers(headers: Headers) =\n      apply {\n        this.headers = headers.newBuilder()\n      }\n\n    open fun body(body: ResponseBody) =\n      apply {\n        this.body = body\n      }\n\n    open fun socket(socket: Socket) =\n      apply {\n        this.socket = socket\n      }\n\n    open fun networkResponse(networkResponse: Response?) =\n      apply {\n        checkSupportResponse(\"networkResponse\", networkResponse)\n        this.networkResponse = networkResponse\n      }\n\n    open fun cacheResponse(cacheResponse: Response?) =\n      apply {\n        checkSupportResponse(\"cacheResponse\", cacheResponse)\n        this.cacheResponse = cacheResponse\n      }\n\n    private fun checkSupportResponse(\n      name: String,\n      response: Response?,\n    ) {\n      response?.apply {\n        require(networkResponse == null) { \"$name.networkResponse != null\" }\n        require(cacheResponse == null) { \"$name.cacheResponse != null\" }\n        require(priorResponse == null) { \"$name.priorResponse != null\" }\n      }\n    }\n\n    open fun priorResponse(priorResponse: Response?) =\n      apply {\n        this.priorResponse = priorResponse\n      }\n\n    open fun trailers(trailersSource: TrailersSource): Builder =\n      apply {\n        this.trailersSource = trailersSource\n      }\n\n    open fun sentRequestAtMillis(sentRequestAtMillis: Long) =\n      apply {\n        this.sentRequestAtMillis = sentRequestAtMillis\n      }\n\n    open fun receivedResponseAtMillis(receivedResponseAtMillis: Long) =\n      apply {\n        this.receivedResponseAtMillis = receivedResponseAtMillis\n      }\n\n    internal fun initExchange(exchange: Exchange) {\n      this.exchange = exchange\n    }\n\n    open fun build(): Response {\n      check(code >= 0) { \"code < 0: $code\" }\n      return Response(\n        checkNotNull(request) { \"request == null\" },\n        checkNotNull(protocol) { \"protocol == null\" },\n        checkNotNull(message) { \"message == null\" },\n        code,\n        handshake,\n        headers.build(),\n        body,\n        socket,\n        networkResponse,\n        cacheResponse,\n        priorResponse,\n        sentRequestAtMillis,\n        receivedResponseAtMillis,\n        exchange,\n        trailersSource,\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/ResponseBody.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.InputStreamReader\nimport java.io.Reader\nimport java.nio.charset.Charset\nimport okhttp3.internal.charsetOrUtf8\nimport okhttp3.internal.chooseCharset\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.readBomAsCharset\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.use\n\n/**\n * A one-shot stream from the origin server to the client application with the raw bytes of the\n * response body. Each response body is supported by an active connection to the webserver. This\n * imposes both obligations and limits on the client application.\n *\n * ### The response body must be closed.\n *\n * Each response body is backed by a limited resource like a socket (live network responses) or\n * an open file (for cached responses). Failing to close the response body will leak resources and\n * may ultimately cause the application to slow down or crash.\n *\n * Both this class and [Response] implement [Closeable]. Closing a response simply\n * closes its response body. If you invoke [Call.execute] or implement [Callback.onResponse] you\n * must close this body by calling any of the following methods:\n *\n * * `Response.close()`\n * * `Response.body().close()`\n * * `Response.body().source().close()`\n * * `Response.body().charStream().close()`\n * * `Response.body().byteStream().close()`\n * * `Response.body().bytes()`\n * * `Response.body().string()`\n *\n * There is no benefit to invoking multiple `close()` methods for the same response body.\n *\n * For synchronous calls, the easiest way to make sure a response body is closed is with a `try`\n * block. With this structure the compiler inserts an implicit `finally` clause that calls\n * [close()][Response.close] for you.\n *\n * ```java\n * Call call = client.newCall(request);\n * try (Response response = call.execute()) {\n * ... // Use the response.\n * }\n * ```\n *\n * You can use a similar block for asynchronous calls:\n *\n * ```java\n * Call call = client.newCall(request);\n * call.enqueue(new Callback() {\n *   public void onResponse(Call call, Response response) throws IOException {\n *     try (ResponseBody responseBody = response.body()) {\n *     ... // Use the response.\n *     }\n *   }\n *\n *   public void onFailure(Call call, IOException e) {\n *   ... // Handle the failure.\n *   }\n * });\n * ```\n *\n * These examples will not work if you're consuming the response body on another thread. In such\n * cases the consuming thread must call [close] when it has finished reading the response\n * body.\n *\n * ### The response body can be consumed only once.\n *\n * This class may be used to stream very large responses. For example, it is possible to use this\n * class to read a response that is larger than the entire memory allocated to the current process.\n * It can even stream a response larger than the total storage on the current device, which is a\n * common requirement for video streaming applications.\n *\n * Because this class does not buffer the full response in memory, the application may not\n * re-read the bytes of the response. Use this one shot to read the entire response into memory with\n * [bytes] or [string]. Or stream the response with either [source], [byteStream], or [charStream].\n */\nabstract class ResponseBody : Closeable {\n  /** Multiple calls to [charStream] must return the same instance. */\n  private var reader: Reader? = null\n\n  abstract fun contentType(): MediaType?\n\n  /**\n   * Returns the number of bytes in that will returned by [bytes], or [byteStream], or -1 if\n   * unknown.\n   */\n  abstract fun contentLength(): Long\n\n  fun byteStream(): InputStream = source().inputStream()\n\n  abstract fun source(): BufferedSource\n\n  /**\n   * Returns the response as a byte array.\n   *\n   * This method loads entire response body into memory. If the response body is very large this\n   * may trigger an [OutOfMemoryError]. Prefer to stream the response body if this is a\n   * possibility for your response.\n   */\n  @Throws(IOException::class)\n  fun bytes() = consumeSource(BufferedSource::readByteArray) { it.size }\n\n  /**\n   * Returns the response as a [ByteString].\n   *\n   * This method loads entire response body into memory. If the response body is very large this\n   * may trigger an [OutOfMemoryError]. Prefer to stream the response body if this is a\n   * possibility for your response.\n   */\n  @Throws(IOException::class)\n  fun byteString() = consumeSource(BufferedSource::readByteString) { it.size }\n\n  private inline fun <T : Any> ResponseBody.consumeSource(\n    consumer: (BufferedSource) -> T,\n    sizeMapper: (T) -> Int,\n  ): T {\n    val contentLength = contentLength()\n    if (contentLength > Int.MAX_VALUE) {\n      throw IOException(\"Cannot buffer entire body for content length: $contentLength\")\n    }\n\n    val bytes = source().use(consumer)\n    val size = sizeMapper(bytes)\n    if (contentLength != -1L && contentLength != size.toLong()) {\n      throw IOException(\"Content-Length ($contentLength) and stream length ($size) disagree\")\n    }\n    return bytes\n  }\n\n  /**\n   * Returns the response as a character stream.\n   *\n   * If the response starts with a\n   * [Byte Order Mark (BOM)](https://en.wikipedia.org/wiki/Byte_order_mark), it is consumed and\n   * used to determine the charset of the response bytes.\n   *\n   * Otherwise if the response has a `Content-Type` header that specifies a charset, that is used\n   * to determine the charset of the response bytes.\n   *\n   * Otherwise the response bytes are decoded as UTF-8.\n   */\n  fun charStream(): Reader =\n    reader ?: BomAwareReader(source(), charset()).also {\n      reader = it\n    }\n\n  /**\n   * Returns the response as a string.\n   *\n   * If the response starts with a\n   * [Byte Order Mark (BOM)](https://en.wikipedia.org/wiki/Byte_order_mark), it is consumed and\n   * used to determine the charset of the response bytes.\n   *\n   * Otherwise if the response has a `Content-Type` header that specifies a charset, that is used\n   * to determine the charset of the response bytes.\n   *\n   * Otherwise the response bytes are decoded as UTF-8.\n   *\n   * This method loads entire response body into memory. If the response body is very large this\n   * may trigger an [OutOfMemoryError]. Prefer to stream the response body if this is a\n   * possibility for your response.\n   */\n  @Throws(IOException::class)\n  fun string(): String =\n    source().use { source ->\n      source.readString(charset = source.readBomAsCharset(charset()))\n    }\n\n  private fun charset() = contentType().charsetOrUtf8()\n\n  override fun close() = source().closeQuietly()\n\n  internal class BomAwareReader(\n    private val source: BufferedSource,\n    private val charset: Charset,\n  ) : Reader() {\n    private var closed: Boolean = false\n    private var delegate: Reader? = null\n\n    @Throws(IOException::class)\n    override fun read(\n      cbuf: CharArray,\n      off: Int,\n      len: Int,\n    ): Int {\n      if (closed) throw IOException(\"Stream closed\")\n\n      val finalDelegate =\n        delegate ?: InputStreamReader(\n          source.inputStream(),\n          source.readBomAsCharset(charset),\n        ).also {\n          delegate = it\n        }\n      return finalDelegate.read(cbuf, off, len)\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n      closed = true\n      delegate?.close() ?: run { source.close() }\n    }\n  }\n\n  companion object {\n    /** Empty response body with no content-type. Closing this response body does nothing. */\n    @JvmField\n    val EMPTY: ResponseBody = ByteString.EMPTY.toResponseBody()\n\n    /**\n     * Returns a new response body that transmits this string. If [contentType] is non-null and\n     * has a charset other than utf-8 the behaviour differs by platform.\n     *\n     * On the JVM the encoding will be used instead of utf-8.\n     *\n     * On non JVM platforms, this method will fail for encodings other than utf-8.\n     */\n    @JvmStatic\n    @JvmName(\"create\")\n    fun String.toResponseBody(contentType: MediaType? = null): ResponseBody {\n      val (charset, finalContentType) = contentType.chooseCharset()\n      val buffer = Buffer().writeString(this, charset)\n      return buffer.asResponseBody(finalContentType, buffer.size)\n    }\n\n    /** Returns a new response body that transmits this byte array. */\n    @JvmStatic\n    @JvmName(\"create\")\n    fun ByteArray.toResponseBody(contentType: MediaType? = null): ResponseBody =\n      Buffer()\n        .write(this)\n        .asResponseBody(contentType, size.toLong())\n\n    /** Returns a new response body that transmits this byte string. */\n    @JvmStatic\n    @JvmName(\"create\")\n    fun ByteString.toResponseBody(contentType: MediaType? = null): ResponseBody =\n      Buffer()\n        .write(this)\n        .asResponseBody(contentType, size.toLong())\n\n    /** Returns a new response body that transmits this source. */\n    @JvmStatic\n    @JvmName(\"create\")\n    fun BufferedSource.asResponseBody(\n      contentType: MediaType? = null,\n      contentLength: Long = -1L,\n    ): ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = contentType\n\n        override fun contentLength(): Long = contentLength\n\n        override fun source(): BufferedSource = this@asResponseBody\n      }\n\n    @JvmStatic\n    @Deprecated(\n      message = \"Moved to extension function. Put the 'content' argument first to fix Java\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"content.toResponseBody(contentType)\",\n          imports = [\"okhttp3.ResponseBody.Companion.toResponseBody\"],\n        ),\n      level = DeprecationLevel.WARNING,\n    )\n    fun create(\n      contentType: MediaType?,\n      content: String,\n    ): ResponseBody = content.toResponseBody(contentType)\n\n    @JvmStatic\n    @Deprecated(\n      message = \"Moved to extension function. Put the 'content' argument first to fix Java\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"content.toResponseBody(contentType)\",\n          imports = [\"okhttp3.ResponseBody.Companion.toResponseBody\"],\n        ),\n      level = DeprecationLevel.WARNING,\n    )\n    fun create(\n      contentType: MediaType?,\n      content: ByteArray,\n    ): ResponseBody = content.toResponseBody(contentType)\n\n    @JvmStatic\n    @Deprecated(\n      message = \"Moved to extension function. Put the 'content' argument first to fix Java\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"content.toResponseBody(contentType)\",\n          imports = [\"okhttp3.ResponseBody.Companion.toResponseBody\"],\n        ),\n      level = DeprecationLevel.WARNING,\n    )\n    fun create(\n      contentType: MediaType?,\n      content: ByteString,\n    ): ResponseBody = content.toResponseBody(contentType)\n\n    @JvmStatic\n    @Deprecated(\n      message = \"Moved to extension function. Put the 'content' argument first to fix Java\",\n      replaceWith =\n        ReplaceWith(\n          expression = \"content.asResponseBody(contentType, contentLength)\",\n          imports = [\"okhttp3.ResponseBody.Companion.asResponseBody\"],\n        ),\n      level = DeprecationLevel.WARNING,\n    )\n    fun create(\n      contentType: MediaType?,\n      contentLength: Long,\n      content: BufferedSource,\n    ): ResponseBody = content.asResponseBody(contentType, contentLength)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/Route.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport okhttp3.internal.toCanonicalHost\n\n/**\n * The concrete route used by a connection to reach an abstract origin server. When creating a\n * connection the client has many options:\n *\n *  * **HTTP proxy:** a proxy server may be explicitly configured for the client. Otherwise, the\n *    [proxy selector][java.net.ProxySelector] is used. It may return multiple proxies to attempt.\n *  * **IP address:** whether connecting directly to an origin server or a proxy, opening a socket\n *    requires an IP address. The DNS server may return multiple IP addresses to attempt.\n *\n * Each route is a specific selection of these options.\n */\nclass Route(\n  @get:JvmName(\"address\") val address: Address,\n  /**\n   * Returns the [Proxy] of this route.\n   *\n   * **Warning:** This may disagree with [Address.proxy] when it is null. When the address's proxy\n   * is null, the proxy selector is used.\n   */\n  @get:JvmName(\"proxy\") val proxy: Proxy,\n  @get:JvmName(\"socketAddress\") val socketAddress: InetSocketAddress,\n) {\n  @JvmName(\"-deprecated_address\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"address\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun address(): Address = address\n\n  @JvmName(\"-deprecated_proxy\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"proxy\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun proxy(): Proxy = proxy\n\n  @JvmName(\"-deprecated_socketAddress\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"socketAddress\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun socketAddress(): InetSocketAddress = socketAddress\n\n  /**\n   * Returns true if this route tunnels HTTPS or HTTP/2 through an HTTP proxy.\n   * See [RFC 2817, Section 5.2][rfc_2817].\n   *\n   * [rfc_2817]: http://www.ietf.org/rfc/rfc2817.txt\n   */\n  fun requiresTunnel(): Boolean {\n    if (proxy.type() != Proxy.Type.HTTP) return false\n    return (address.sslSocketFactory != null) ||\n      (Protocol.H2_PRIOR_KNOWLEDGE in address.protocols)\n  }\n\n  override fun equals(other: Any?): Boolean =\n    other is Route &&\n      other.address == address &&\n      other.proxy == proxy &&\n      other.socketAddress == socketAddress\n\n  override fun hashCode(): Int {\n    var result = 17\n    result = 31 * result + address.hashCode()\n    result = 31 * result + proxy.hashCode()\n    result = 31 * result + socketAddress.hashCode()\n    return result\n  }\n\n  /**\n   * Returns a string with the URL hostname, socket IP address, and socket port, like one of these:\n   *\n   *  * `example.com:80 at 1.2.3.4:8888`\n   *  * `example.com:443 via proxy [::1]:8888`\n   *\n   * This omits duplicate information when possible.\n   */\n  override fun toString(): String =\n    buildString {\n      val addressHostname = address.url.host // Already in canonical form.\n      val socketHostname = socketAddress.address?.hostAddress?.toCanonicalHost()\n\n      when {\n        ':' in addressHostname -> append(\"[\").append(addressHostname).append(\"]\")\n        else -> append(addressHostname)\n      }\n      if (address.url.port != socketAddress.port || addressHostname == socketHostname) {\n        append(\":\")\n        append(address.url.port)\n      }\n\n      if (addressHostname != socketHostname) {\n        when (proxy) {\n          Proxy.NO_PROXY -> append(\" at \")\n          else -> append(\" via proxy \")\n        }\n\n        when {\n          socketHostname == null -> append(\"<unresolved>\")\n          ':' in socketHostname -> append(\"[\").append(socketHostname).append(\"]\")\n          else -> append(socketHostname)\n        }\n        append(\":\")\n        append(socketAddress.port)\n      }\n    }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/TlsVersion.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\n/**\n * Versions of TLS that can be offered when negotiating a secure socket. See\n * [javax.net.ssl.SSLSocket.setEnabledProtocols].\n */\nenum class TlsVersion(\n  @get:JvmName(\"javaName\") val javaName: String,\n) {\n  TLS_1_3(\"TLSv1.3\"), // 2016.\n  TLS_1_2(\"TLSv1.2\"), // 2008.\n  TLS_1_1(\"TLSv1.1\"), // 2006.\n  TLS_1_0(\"TLSv1\"), // 1999.\n  SSL_3_0(\"SSLv3\"), // 1996.\n  ;\n\n  @JvmName(\"-deprecated_javaName\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"javaName\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun javaName(): String = javaName\n\n  companion object {\n    @JvmStatic\n    fun forJavaName(javaName: String): TlsVersion =\n      when (javaName) {\n        \"TLSv1.3\" -> TLS_1_3\n        \"TLSv1.2\" -> TLS_1_2\n        \"TLSv1.1\" -> TLS_1_1\n        \"TLSv1\" -> TLS_1_0\n        \"SSLv3\" -> SSL_3_0\n        else -> throw IllegalArgumentException(\"Unexpected TLS version: $javaName\")\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/TrailersSource.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okio.IOException\n\n/**\n * Returns the trailers that follow an HTTP response, blocking if they aren't ready yet.\n * Implementations of this interface should respond to [Call.cancel] by immediately throwing an\n * [IOException].\n *\n * Most callers won't need this interface, and should use [Response.trailers] instead.\n *\n * This interface is for test and production code that creates [Response] instances without making\n * an HTTP call to a remote server.\n */\ninterface TrailersSource {\n  @Throws(IOException::class)\n  fun peek(): Headers? = null\n\n  @Throws(IOException::class)\n  fun get(): Headers\n\n  companion object {\n    @JvmField\n    val EMPTY: TrailersSource =\n      object : TrailersSource {\n        override fun peek() = Headers.EMPTY\n\n        override fun get() = Headers.EMPTY\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/WebSocket.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okio.ByteString\n\n/**\n * A non-blocking interface to a web socket. Use the [factory][WebSocket.Factory] to create\n * instances; usually this is [OkHttpClient].\n *\n * ## Web Socket Lifecycle\n *\n * Upon normal operation each web socket progresses through a sequence of states:\n *\n *  * **Connecting:** the initial state of each web socket. Messages may be enqueued but they won't\n *    be transmitted until the web socket is open.\n *\n *  * **Open:** the web socket has been accepted by the remote peer and is fully operational.\n *    Messages in either direction are enqueued for immediate transmission.\n *\n *  * **Closing:** one of the peers on the web socket has initiated a graceful shutdown. The web\n *    socket will continue to transmit already-enqueued messages but will refuse to enqueue new\n *    ones.\n *\n *  * **Closed:** the web socket has transmitted all of its messages and has received all messages\n *    from the peer.\n *\n * Web sockets may fail due to HTTP upgrade problems, connectivity problems, or if either peer\n * chooses to short-circuit the graceful shutdown process:\n *\n *  * **Canceled:** the web socket connection failed. Messages that were successfully enqueued by\n *    either peer may not have been transmitted to the other.\n *\n * Note that the state progression is independent for each peer. Arriving at a gracefully-closed\n * state indicates that a peer has sent all of its outgoing messages and received all of its\n * incoming messages. But it does not guarantee that the other peer will successfully receive all of\n * its incoming messages.\n *\n * ## Message Queue\n *\n * Messages enqueued with [send] are buffered in an outgoing message queue. This queue has a 16 MiB\n * limit. If a call to [send] would cause the queue to exceed this limit, the web socket will\n * initiate a graceful shutdown (close code 1001) and `send()` will return `false`. No exception is\n * thrown and no [WebSocketListener.onFailure] callback is triggered, so callers should always check\n * the return value of `send()`.\n *\n * Use [queueSize] to monitor backpressure before sending. For large payloads, consider breaking\n * them into smaller messages or using HTTP requests instead.\n */\ninterface WebSocket {\n  /** Returns the original request that initiated this web socket. */\n  fun request(): Request\n\n  /**\n   * Returns the size in bytes of all messages enqueued to be transmitted to the server. This\n   * doesn't include framing overhead. If compression is enabled, uncompressed messages size\n   * is used to calculate this value. It also doesn't include any bytes buffered by the operating\n   * system or network intermediaries. This method returns 0 if no messages are waiting in the\n   * queue. If may return a nonzero value after the web socket has been canceled; this indicates\n   * that enqueued messages were not transmitted.\n   *\n   * Use this to monitor backpressure and avoid exceeding the 16 MiB outgoing message buffer limit.\n   * When that limit is exceeded, the web socket is gracefully shut down.\n   */\n  fun queueSize(): Long\n\n  /**\n   * Attempts to enqueue `text` to be UTF-8 encoded and sent as a the data of a text (type `0x1`)\n   * message.\n   *\n   * This method returns true if the message was enqueued. Messages that would overflow the outgoing\n   * message buffer (16 MiB) will be rejected and trigger a [graceful shutdown][close] of this web\n   * socket. This method returns false in that case, and in any other case where this web socket is\n   * closing, closed, or canceled.\n   *\n   * This method returns immediately.\n   */\n  fun send(text: String): Boolean\n\n  /**\n   * Attempts to enqueue `bytes` to be sent as a the data of a binary (type `0x2`) message.\n   *\n   * This method returns true if the message was enqueued. Messages that would overflow the outgoing\n   * message buffer (16 MiB) will be rejected and trigger a [graceful shutdown][close] of this web\n   * socket. This method returns false in that case, and in any other case where this web socket is\n   * closing, closed, or canceled.\n   *\n   * This method returns immediately.\n   */\n  fun send(bytes: ByteString): Boolean\n\n  /**\n   * Attempts to initiate a graceful shutdown of this web socket. Any already-enqueued messages will\n   * be transmitted before the close message is sent but subsequent calls to [send] will return\n   * false and their messages will not be enqueued.\n   *\n   * This returns true if a graceful shutdown was initiated by this call. It returns false if\n   * a graceful shutdown was already underway or if the web socket is already closed or canceled.\n   *\n   * @param code Status code as defined by\n   *     [Section 7.4 of RFC 6455](http://tools.ietf.org/html/rfc6455#section-7.4).\n   * @param reason Reason for shutting down, no longer than 123 bytes of UTF-8 encoded data (**not** characters) or null.\n   * @throws IllegalArgumentException if [code] is invalid or [reason] is too long.\n   */\n  fun close(\n    code: Int,\n    reason: String?,\n  ): Boolean\n\n  /**\n   * Immediately and violently release resources held by this web socket, discarding any enqueued\n   * messages. This does nothing if the web socket has already been closed or canceled.\n   */\n  fun cancel()\n\n  fun interface Factory {\n    /**\n     * Creates a new web socket and immediately returns it. Creating a web socket initiates an\n     * asynchronous process to connect the socket. Once that succeeds or fails, `listener` will be\n     * notified. The caller must either close or cancel the returned web socket when it is no longer\n     * in use.\n     */\n    fun newWebSocket(\n      request: Request,\n      listener: WebSocketListener,\n    ): WebSocket\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/WebSocketListener.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okio.ByteString\n\nabstract class WebSocketListener {\n  /**\n   * Invoked when a web socket has been accepted by the remote peer and may begin transmitting\n   * messages.\n   */\n  open fun onOpen(\n    webSocket: WebSocket,\n    response: Response,\n  ) {\n  }\n\n  /** Invoked when a text (type `0x1`) message has been received. */\n  open fun onMessage(\n    webSocket: WebSocket,\n    text: String,\n  ) {\n  }\n\n  /** Invoked when a binary (type `0x2`) message has been received. */\n  open fun onMessage(\n    webSocket: WebSocket,\n    bytes: ByteString,\n  ) {\n  }\n\n  /**\n   * Invoked when the remote peer has indicated that no more incoming messages will be transmitted.\n   */\n  open fun onClosing(\n    webSocket: WebSocket,\n    code: Int,\n    reason: String,\n  ) {\n  }\n\n  /**\n   * Invoked when both peers have indicated that no more messages will be transmitted and the\n   * connection has been successfully released. No further calls to this listener will be made.\n   */\n  open fun onClosed(\n    webSocket: WebSocket,\n    code: Int,\n    reason: String,\n  ) {\n  }\n\n  /**\n   * Invoked when a web socket has been closed due to an error reading from or writing to the\n   * network. Both outgoing and incoming messages may have been lost. No further calls to this\n   * listener will be made.\n   */\n  open fun onFailure(\n    webSocket: WebSocket,\n    t: Throwable,\n    response: Response?,\n  ) {\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/-CacheControlCommon.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal\n\nimport kotlin.time.Duration.Companion.seconds\nimport okhttp3.CacheControl\nimport okhttp3.Headers\n\ninternal fun CacheControl.commonToString(): String {\n  var result = headerValue\n  if (result == null) {\n    result =\n      buildString {\n        if (noCache) append(\"no-cache, \")\n        if (noStore) append(\"no-store, \")\n        if (maxAgeSeconds != -1) append(\"max-age=\").append(maxAgeSeconds).append(\", \")\n        if (sMaxAgeSeconds != -1) append(\"s-maxage=\").append(sMaxAgeSeconds).append(\", \")\n        if (isPrivate) append(\"private, \")\n        if (isPublic) append(\"public, \")\n        if (mustRevalidate) append(\"must-revalidate, \")\n        if (maxStaleSeconds != -1) append(\"max-stale=\").append(maxStaleSeconds).append(\", \")\n        if (minFreshSeconds != -1) append(\"min-fresh=\").append(minFreshSeconds).append(\", \")\n        if (onlyIfCached) append(\"only-if-cached, \")\n        if (noTransform) append(\"no-transform, \")\n        if (immutable) append(\"immutable, \")\n        if (isEmpty()) return \"\"\n        deleteRange(length - 2, length)\n      }\n    headerValue = result\n  }\n  return result\n}\n\ninternal fun Long.commonClampToInt(): Int =\n  when {\n    this > Int.MAX_VALUE -> Int.MAX_VALUE\n    else -> toInt()\n  }\n\ninternal fun CacheControl.Companion.commonForceNetwork() =\n  CacheControl\n    .Builder()\n    .noCache()\n    .build()\n\ninternal fun CacheControl.Companion.commonForceCache() =\n  CacheControl\n    .Builder()\n    .onlyIfCached()\n    .maxStale(Int.MAX_VALUE.seconds)\n    .build()\n\ninternal fun CacheControl.Builder.commonBuild(): CacheControl =\n  CacheControl(\n    noCache = noCache,\n    noStore = noStore,\n    maxAgeSeconds = maxAgeSeconds,\n    sMaxAgeSeconds = -1,\n    isPrivate = false,\n    isPublic = false,\n    mustRevalidate = false,\n    maxStaleSeconds = maxStaleSeconds,\n    minFreshSeconds = minFreshSeconds,\n    onlyIfCached = onlyIfCached,\n    noTransform = noTransform,\n    immutable = immutable,\n    headerValue = null,\n  )\n\ninternal fun CacheControl.Builder.commonNoCache() =\n  apply {\n    this.noCache = true\n  }\n\ninternal fun CacheControl.Builder.commonNoStore() =\n  apply {\n    this.noStore = true\n  }\n\ninternal fun CacheControl.Builder.commonOnlyIfCached() =\n  apply {\n    this.onlyIfCached = true\n  }\n\ninternal fun CacheControl.Builder.commonNoTransform() =\n  apply {\n    this.noTransform = true\n  }\n\ninternal fun CacheControl.Builder.commonImmutable() =\n  apply {\n    this.immutable = true\n  }\n\ninternal fun CacheControl.Companion.commonParse(headers: Headers): CacheControl {\n  var noCache = false\n  var noStore = false\n  var maxAgeSeconds = -1\n  var sMaxAgeSeconds = -1\n  var isPrivate = false\n  var isPublic = false\n  var mustRevalidate = false\n  var maxStaleSeconds = -1\n  var minFreshSeconds = -1\n  var onlyIfCached = false\n  var noTransform = false\n  var immutable = false\n\n  var canUseHeaderValue = true\n  var headerValue: String? = null\n\n  loop@ for (i in 0 until headers.size) {\n    val name = headers.name(i)\n    val value = headers.value(i)\n\n    when {\n      name.equals(\"Cache-Control\", ignoreCase = true) -> {\n        if (headerValue != null) {\n          // Multiple cache-control headers means we can't use the raw value.\n          canUseHeaderValue = false\n        } else {\n          headerValue = value\n        }\n      }\n\n      name.equals(\"Pragma\", ignoreCase = true) -> {\n        // Might specify additional cache-control params. We invalidate just in case.\n        canUseHeaderValue = false\n      }\n\n      else -> {\n        continue@loop\n      }\n    }\n\n    var pos = 0\n    while (pos < value.length) {\n      val tokenStart = pos\n      pos = value.indexOfElement(\"=,;\", pos)\n      val directive = value.substring(tokenStart, pos).trim()\n      val parameter: String?\n\n      if (pos == value.length || value[pos] == ',' || value[pos] == ';') {\n        pos++ // Consume ',' or ';' (if necessary).\n        parameter = null\n      } else {\n        pos++ // Consume '='.\n        pos = value.indexOfNonWhitespace(pos)\n\n        if (pos < value.length && value[pos] == '\\\"') {\n          // Quoted string.\n          pos++ // Consume '\"' open quote.\n          val parameterStart = pos\n          pos = value.indexOf('\"', pos)\n          parameter = value.substring(parameterStart, pos)\n          pos++ // Consume '\"' close quote (if necessary).\n        } else {\n          // Unquoted string.\n          val parameterStart = pos\n          pos = value.indexOfElement(\",;\", pos)\n          parameter = value.substring(parameterStart, pos).trim()\n        }\n      }\n\n      when {\n        \"no-cache\".equals(directive, ignoreCase = true) -> {\n          noCache = true\n        }\n\n        \"no-store\".equals(directive, ignoreCase = true) -> {\n          noStore = true\n        }\n\n        \"max-age\".equals(directive, ignoreCase = true) -> {\n          maxAgeSeconds = parameter.toNonNegativeInt(-1)\n        }\n\n        \"s-maxage\".equals(directive, ignoreCase = true) -> {\n          sMaxAgeSeconds = parameter.toNonNegativeInt(-1)\n        }\n\n        \"private\".equals(directive, ignoreCase = true) -> {\n          isPrivate = true\n        }\n\n        \"public\".equals(directive, ignoreCase = true) -> {\n          isPublic = true\n        }\n\n        \"must-revalidate\".equals(directive, ignoreCase = true) -> {\n          mustRevalidate = true\n        }\n\n        \"max-stale\".equals(directive, ignoreCase = true) -> {\n          maxStaleSeconds = parameter.toNonNegativeInt(Int.MAX_VALUE)\n        }\n\n        \"min-fresh\".equals(directive, ignoreCase = true) -> {\n          minFreshSeconds = parameter.toNonNegativeInt(-1)\n        }\n\n        \"only-if-cached\".equals(directive, ignoreCase = true) -> {\n          onlyIfCached = true\n        }\n\n        \"no-transform\".equals(directive, ignoreCase = true) -> {\n          noTransform = true\n        }\n\n        \"immutable\".equals(directive, ignoreCase = true) -> {\n          immutable = true\n        }\n      }\n    }\n  }\n\n  if (!canUseHeaderValue) {\n    headerValue = null\n  }\n\n  return CacheControl(\n    noCache = noCache,\n    noStore = noStore,\n    maxAgeSeconds = maxAgeSeconds,\n    sMaxAgeSeconds = sMaxAgeSeconds,\n    isPrivate = isPrivate,\n    isPublic = isPublic,\n    mustRevalidate = mustRevalidate,\n    maxStaleSeconds = maxStaleSeconds,\n    minFreshSeconds = minFreshSeconds,\n    onlyIfCached = onlyIfCached,\n    noTransform = noTransform,\n    immutable = immutable,\n    headerValue = headerValue,\n  )\n}\n\n/**\n * Returns the next index in this at or after [startIndex] that is a character from\n * [characters]. Returns the input length if none of the requested characters can be found.\n */\nprivate fun String.indexOfElement(\n  characters: String,\n  startIndex: Int = 0,\n): Int {\n  for (i in startIndex until length) {\n    if (this[i] in characters) {\n      return i\n    }\n  }\n  return length\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/-HeadersCommon.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal\n\nimport okhttp3.Headers\n\n/** This is the same as Chrome's limit. */\ninternal const val HEADER_LIMIT = 256 * 1024L\n\ninternal fun Headers.commonName(index: Int): String = namesAndValues.getOrNull(index * 2) ?: throw IndexOutOfBoundsException(\"name[$index]\")\n\ninternal fun Headers.commonValue(index: Int): String =\n  namesAndValues.getOrNull(index * 2 + 1) ?: throw IndexOutOfBoundsException(\"value[$index]\")\n\ninternal fun Headers.commonValues(name: String): List<String> {\n  var result: MutableList<String>? = null\n  for (i in 0 until size) {\n    if (name.equals(name(i), ignoreCase = true)) {\n      if (result == null) result = ArrayList(2)\n      result.add(value(i))\n    }\n  }\n  return result?.unmodifiable().orEmpty()\n}\n\ninternal fun Headers.commonIterator(): Iterator<Pair<String, String>> = Array(size) { name(it) to value(it) }.iterator()\n\ninternal fun Headers.commonNewBuilder(): Headers.Builder {\n  val result = Headers.Builder()\n  result.namesAndValues += namesAndValues\n  return result\n}\n\ninternal fun Headers.commonEquals(other: Any?): Boolean = other is Headers && namesAndValues.contentEquals(other.namesAndValues)\n\ninternal fun Headers.commonHashCode(): Int = namesAndValues.contentHashCode()\n\ninternal fun Headers.commonToString(): String =\n  buildString {\n    for (i in 0 until size) {\n      val name = name(i)\n      val value = value(i)\n      append(name)\n      append(\": \")\n      append(if (isSensitiveHeader(name)) \"██\" else value)\n      append(\"\\n\")\n    }\n  }\n\ninternal fun commonHeadersGet(\n  namesAndValues: Array<String>,\n  name: String,\n): String? {\n  for (i in namesAndValues.size - 2 downTo 0 step 2) {\n    if (name.equals(namesAndValues[i], ignoreCase = true)) {\n      return namesAndValues[i + 1]\n    }\n  }\n  return null\n}\n\ninternal fun Headers.Builder.commonAdd(\n  name: String,\n  value: String,\n) = apply {\n  headersCheckName(name)\n  headersCheckValue(value, name)\n  commonAddLenient(name, value)\n}\n\ninternal fun Headers.Builder.commonAddAll(headers: Headers) =\n  apply {\n    for (i in 0 until headers.size) {\n      commonAddLenient(headers.name(i), headers.value(i))\n    }\n  }\n\ninternal fun Headers.Builder.commonAddLenient(\n  name: String,\n  value: String,\n) = apply {\n  namesAndValues.add(name)\n  namesAndValues.add(value.trim())\n}\n\ninternal fun Headers.Builder.commonRemoveAll(name: String) =\n  apply {\n    var i = 0\n    while (i < namesAndValues.size) {\n      if (name.equals(namesAndValues[i], ignoreCase = true)) {\n        namesAndValues.removeAt(i) // name\n        namesAndValues.removeAt(i) // value\n        i -= 2\n      }\n      i += 2\n    }\n  }\n\n/**\n * Set a field with the specified value. If the field is not found, it is added. If the field is\n * found, the existing values are replaced.\n */\ninternal fun Headers.Builder.commonSet(\n  name: String,\n  value: String,\n) = apply {\n  headersCheckName(name)\n  headersCheckValue(value, name)\n  removeAll(name)\n  commonAddLenient(name, value)\n}\n\n/** Equivalent to `build().get(name)`, but potentially faster. */\ninternal fun Headers.Builder.commonGet(name: String): String? {\n  for (i in namesAndValues.size - 2 downTo 0 step 2) {\n    if (name.equals(namesAndValues[i], ignoreCase = true)) {\n      return namesAndValues[i + 1]\n    }\n  }\n  return null\n}\n\ninternal fun Headers.Builder.commonBuild(): Headers = Headers(namesAndValues.toTypedArray())\n\ninternal fun headersCheckName(name: String) {\n  require(name.isNotEmpty()) { \"name is empty\" }\n  for (i in name.indices) {\n    val c = name[i]\n    require(c in '\\u0021'..'\\u007e') {\n      \"Unexpected char 0x${c.charCode()} at $i in header name: $name\"\n    }\n  }\n}\n\ninternal fun headersCheckValue(\n  value: String,\n  name: String,\n) {\n  for (i in value.indices) {\n    val c = value[i]\n    require(c == '\\t' || c in '\\u0020'..'\\u007e') {\n      \"Unexpected char 0x${c.charCode()} at $i in $name value\" +\n        (if (isSensitiveHeader(name)) \"\" else \": $value\")\n    }\n  }\n}\n\nprivate fun Char.charCode() =\n  code.toString(16).let {\n    if (it.length < 2) {\n      \"0$it\"\n    } else {\n      it\n    }\n  }\n\ninternal fun commonHeadersOf(vararg inputNamesAndValues: String): Headers {\n  require(inputNamesAndValues.size % 2 == 0) { \"Expected alternating header names and values\" }\n\n  // Make a defensive copy and clean it up.\n  val namesAndValues: Array<String> = arrayOf(*inputNamesAndValues)\n  for (i in namesAndValues.indices) {\n    @Suppress(\"SENSELESS_COMPARISON\")\n    require(namesAndValues[i] != null) { \"Headers cannot be null\" }\n    namesAndValues[i] = inputNamesAndValues[i].trim()\n  }\n\n  // Check for malformed headers.\n  for (i in namesAndValues.indices step 2) {\n    val name = namesAndValues[i]\n    val value = namesAndValues[i + 1]\n    headersCheckName(name)\n    headersCheckValue(value, name)\n  }\n\n  return Headers(namesAndValues)\n}\n\ninternal fun Map<String, String>.commonToHeaders(): Headers {\n  // Make a defensive copy and clean it up.\n  val namesAndValues = arrayOfNulls<String>(size * 2)\n  var i = 0\n  for ((k, v) in this) {\n    val name = k.trim()\n    val value = v.trim()\n    headersCheckName(name)\n    headersCheckValue(value, name)\n    namesAndValues[i] = name\n    namesAndValues[i + 1] = value\n    i += 2\n  }\n\n  @Suppress(\"UNCHECKED_CAST\")\n  return Headers(namesAndValues as Array<String>)\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/-HostnamesCommon.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal\n\nimport okhttp3.internal.idn.IDNA_MAPPING_TABLE\nimport okhttp3.internal.idn.Punycode\nimport okio.Buffer\n\n/**\n * Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation\n * of Android's private InetAddress#isNumeric API.\n *\n * This matches IPv6 addresses as a hex string containing at least one colon, and possibly\n * including dots after the first colon. It matches IPv4 addresses as strings containing only\n * decimal digits and dots. This pattern matches strings like \"a:.23\" and \"54\" that are neither IP\n * addresses nor hostnames; they will be verified as IP addresses (which is a more strict\n * verification).\n */\nprivate val VERIFY_AS_IP_ADDRESS = \"([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\\\d.]+)\".toRegex()\n\n/** Returns true if this string is not a host name and might be an IP address. */\nfun String.canParseAsIpAddress(): Boolean = VERIFY_AS_IP_ADDRESS.matches(this)\n\n/**\n * Returns true if the length is not valid for DNS (empty or greater than 253 characters), or if any\n * label is longer than 63 characters. Trailing dots are okay.\n */\ninternal fun String.containsInvalidLabelLengths(): Boolean {\n  if (length !in 1..253) return true\n\n  var labelStart = 0\n  while (true) {\n    val dot = indexOf('.', startIndex = labelStart)\n    val labelLength =\n      when (dot) {\n        -1 -> length - labelStart\n        else -> dot - labelStart\n      }\n    if (labelLength !in 1..63) return true\n    if (dot == -1) break\n    if (dot == length - 1) break // Trailing '.' is allowed.\n    labelStart = dot + 1\n  }\n\n  return false\n}\n\ninternal fun String.containsInvalidHostnameAsciiCodes(): Boolean {\n  for (i in 0 until length) {\n    val c = this[i]\n    // The WHATWG Host parsing rules accepts some character codes which are invalid by\n    // definition for OkHttp's host header checks (and the WHATWG Host syntax definition). Here\n    // we rule out characters that would cause problems in host headers.\n    if (c <= '\\u001f' || c >= '\\u007f') {\n      return true\n    }\n    // Check for the characters mentioned in the WHATWG Host parsing spec:\n    // U+0000, U+0009, U+000A, U+000D, U+0020, \"#\", \"%\", \"/\", \":\", \"?\", \"@\", \"[\", \"\\\", and \"]\"\n    // (excluding the characters covered above).\n    if (\" #%/:?@[\\\\]\".indexOf(c) != -1) {\n      return true\n    }\n  }\n  return false\n}\n\n/** Decodes an IPv6 address like 1111:2222:3333:4444:5555:6666:7777:8888 or ::1. */\ninternal fun decodeIpv6(\n  input: String,\n  pos: Int,\n  limit: Int,\n): ByteArray? {\n  val address = ByteArray(16)\n  var b = 0\n  var compress = -1\n  var groupOffset = -1\n\n  var i = pos\n  while (i < limit) {\n    if (b == address.size) return null // Too many groups.\n\n    // Read a delimiter.\n    if (i + 2 <= limit && input.startsWith(\"::\", startIndex = i)) {\n      // Compression \"::\" delimiter, which is anywhere in the input, including its prefix.\n      if (compress != -1) return null // Multiple \"::\" delimiters.\n      i += 2\n      b += 2\n      compress = b\n      if (i == limit) break\n    } else if (b != 0) {\n      // Group separator \":\" delimiter.\n      if (input.startsWith(\":\", startIndex = i)) {\n        i++\n      } else if (input.startsWith(\".\", startIndex = i)) {\n        // If we see a '.', rewind to the beginning of the previous group and parse as IPv4.\n        if (!decodeIpv4Suffix(input, groupOffset, limit, address, b - 2)) return null\n        b += 2 // We rewound two bytes and then added four.\n        break\n      } else {\n        return null // Wrong delimiter.\n      }\n    }\n\n    // Read a group, one to four hex digits.\n    var value = 0\n    groupOffset = i\n    while (i < limit) {\n      val hexDigit = input[i].parseHexDigit()\n      if (hexDigit == -1) break\n      value = (value shl 4) + hexDigit\n      i++\n    }\n    val groupLength = i - groupOffset\n    if (groupLength == 0 || groupLength > 4) return null // Group is the wrong size.\n\n    // We've successfully read a group. Assign its value to our byte array.\n    address[b++] = (value.ushr(8) and 0xff).toByte()\n    address[b++] = (value and 0xff).toByte()\n  }\n\n  // All done. If compression happened, we need to move bytes to the right place in the\n  // address. Here's a sample:\n  //\n  //      input: \"1111:2222:3333::7777:8888\"\n  //     before: { 11, 11, 22, 22, 33, 33, 00, 00, 77, 77, 88, 88, 00, 00, 00, 00  }\n  //   compress: 6\n  //          b: 10\n  //      after: { 11, 11, 22, 22, 33, 33, 00, 00, 00, 00, 00, 00, 77, 77, 88, 88 }\n  //\n  if (b != address.size) {\n    if (compress == -1) return null // Address didn't have compression or enough groups.\n    address.copyInto(address, address.size - (b - compress), compress, b)\n    address.fill(0.toByte(), compress, compress + (address.size - b))\n  }\n\n  return address\n}\n\n/** Decodes an IPv4 address suffix of an IPv6 address, like 1111::5555:6666:192.168.0.1. */\ninternal fun decodeIpv4Suffix(\n  input: String,\n  pos: Int,\n  limit: Int,\n  address: ByteArray,\n  addressOffset: Int,\n): Boolean {\n  var b = addressOffset\n\n  var i = pos\n  while (i < limit) {\n    if (b == address.size) return false // Too many groups.\n\n    // Read a delimiter.\n    if (b != addressOffset) {\n      if (input[i] != '.') return false // Wrong delimiter.\n      i++\n    }\n\n    // Read 1 or more decimal digits for a value in 0..255.\n    var value = 0\n    val groupOffset = i\n    while (i < limit) {\n      val c = input[i]\n      if (c < '0' || c > '9') break\n      if (value == 0 && groupOffset != i) return false // Reject unnecessary leading '0's.\n      value = value * 10 + c.code - '0'.code\n      if (value > 255) return false // Value out of range.\n      i++\n    }\n    val groupLength = i - groupOffset\n    if (groupLength == 0) return false // No digits.\n\n    // We've successfully read a byte.\n    address[b++] = value.toByte()\n  }\n\n  // Check for too few groups. We wanted exactly four.\n  return b == addressOffset + 4\n}\n\n/** Encodes an IPv6 address in canonical form according to RFC 5952. */\ninternal fun inet6AddressToAscii(address: ByteArray): String {\n  // Go through the address looking for the longest run of 0s. Each group is 2-bytes.\n  // A run must be longer than one group (section 4.2.2).\n  // If there are multiple equal runs, the first one must be used (section 4.2.3).\n  var longestRunOffset = -1\n  var longestRunLength = 0\n  run {\n    var i = 0\n    while (i < address.size) {\n      val currentRunOffset = i\n      while (i < 16 && address[i].toInt() == 0 && address[i + 1].toInt() == 0) {\n        i += 2\n      }\n      val currentRunLength = i - currentRunOffset\n      if (currentRunLength > longestRunLength && currentRunLength >= 4) {\n        longestRunOffset = currentRunOffset\n        longestRunLength = currentRunLength\n      }\n      i += 2\n    }\n  }\n\n  // Emit each 2-byte group in hex, separated by ':'. The longest run of zeroes is \"::\".\n  val result = Buffer()\n  var i = 0\n  while (i < address.size) {\n    if (i == longestRunOffset) {\n      result.writeByte(':'.code)\n      i += longestRunLength\n      if (i == 16) result.writeByte(':'.code)\n    } else {\n      if (i > 0) result.writeByte(':'.code)\n      val group = address[i] and 0xff shl 8 or (address[i + 1] and 0xff)\n      result.writeHexadecimalUnsignedLong(group.toLong())\n      i += 2\n    }\n  }\n  return result.readUtf8()\n}\n\n/**\n * Returns the canonical address for [address]. If [address] is an IPv6 address that is mapped to an\n * IPv4 address, this returns the IPv4-mapped address. Otherwise, this returns [address].\n *\n * https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses\n */\ninternal fun canonicalizeInetAddress(address: ByteArray): ByteArray =\n  when {\n    isMappedIpv4Address(address) -> address.sliceArray(12 until 16)\n    else -> address\n  }\n\n/** Returns true for IPv6 addresses like `0000:0000:0000:0000:0000:ffff:XXXX:XXXX`. */\nprivate fun isMappedIpv4Address(address: ByteArray): Boolean {\n  if (address.size != 16) return false\n\n  for (i in 0 until 10) {\n    if (address[i] != 0.toByte()) return false\n  }\n\n  if (address[10] != 255.toByte()) return false\n  if (address[11] != 255.toByte()) return false\n\n  return true\n}\n\n/** Encodes an IPv4 address in canonical form according to RFC 4001. */\ninternal fun inet4AddressToAscii(address: ByteArray): String {\n  require(address.size == 4)\n  return Buffer()\n    .writeDecimalLong((address[0] and 0xff).toLong())\n    .writeByte('.'.code)\n    .writeDecimalLong((address[1] and 0xff).toLong())\n    .writeByte('.'.code)\n    .writeDecimalLong((address[2] and 0xff).toLong())\n    .writeByte('.'.code)\n    .writeDecimalLong((address[3] and 0xff).toLong())\n    .readUtf8()\n}\n\n/**\n * If this is an IP address, this returns the IP address in canonical form.\n *\n * Otherwise, this performs IDN ToASCII encoding and canonicalize the result to lowercase. For\n * example this converts `☃.net` to `xn--n3h.net`, and `WwW.GoOgLe.cOm` to `www.google.com`.\n * `null` will be returned if the host cannot be ToASCII encoded or if the result contains\n * unsupported ASCII characters.\n */\ninternal fun String.toCanonicalHost(): String? {\n  val host: String = this\n\n  // If the input contains a :, it’s an IPv6 address.\n  if (\":\" in host) {\n    // If the input is encased in square braces \"[...]\", drop 'em.\n    val inetAddressByteArray =\n      (\n        if (host.startsWith(\"[\") && host.endsWith(\"]\")) {\n          decodeIpv6(host, 1, host.length - 1)\n        } else {\n          decodeIpv6(host, 0, host.length)\n        }\n      ) ?: return null\n\n    val address = canonicalizeInetAddress(inetAddressByteArray)\n    if (address.size == 16) return inet6AddressToAscii(address)\n    if (address.size == 4) return inet4AddressToAscii(address) // An IPv4-mapped IPv6 address.\n    throw AssertionError(\"Invalid IPv6 address: '$host'\")\n  }\n\n  val result = idnToAscii(host) ?: return null\n  if (result.isEmpty()) return null\n  if (result.containsInvalidHostnameAsciiCodes()) return null\n  if (result.containsInvalidLabelLengths()) return null\n\n  return result\n}\n\ninternal fun idnToAscii(host: String): String? {\n  val bufferA = Buffer().writeUtf8(host)\n  val bufferB = Buffer()\n\n  // 1. Map, from bufferA to bufferB.\n  while (!bufferA.exhausted()) {\n    val codePoint = bufferA.readUtf8CodePoint()\n    if (!IDNA_MAPPING_TABLE.map(codePoint, bufferB)) return null\n  }\n\n  // 2. Normalize, from bufferB to bufferA.\n  val normalized = normalizeNfc(bufferB.readUtf8())\n  bufferA.writeUtf8(normalized)\n\n  // 3. For each label, convert/validate Punycode.\n  val decoded = Punycode.decode(bufferA.readUtf8()) ?: return null\n\n  // 4.1 Validate.\n\n  // Must be NFC.\n  if (decoded != normalizeNfc(decoded)) return null\n\n  // TODO: Must not begin with a combining mark.\n  // TODO: Each character must be 'valid' or 'deviation'. Not mapped.\n  // TODO: CheckJoiners from IDNA 2008\n  // TODO: CheckBidi from IDNA 2008, RFC 5893, Section 2.\n\n  return Punycode.encode(decoded)\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/-NormalizeJvm.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal\n\nimport java.text.Normalizer\nimport java.text.Normalizer.Form.NFC\n\ninternal fun normalizeNfc(string: String): String = Normalizer.normalize(string, NFC)\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/-UtilCommon.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal\n\nimport okio.ArrayIndexOutOfBoundsException\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString.Companion.decodeHex\nimport okio.Closeable\nimport okio.FileNotFoundException\nimport okio.FileSystem\nimport okio.IOException\nimport okio.Options\nimport okio.Path\nimport okio.use\n\n@JvmField\ninternal val EMPTY_BYTE_ARRAY: ByteArray = ByteArray(0)\n\n/** Byte order marks. */\ninternal val UNICODE_BOMS =\n  Options.of(\n    // UTF-8.\n    \"efbbbf\".decodeHex(),\n    // UTF-16BE.\n    \"feff\".decodeHex(),\n    // UTF-32LE.\n    \"fffe0000\".decodeHex(),\n    // UTF-16LE.\n    \"fffe\".decodeHex(),\n    // UTF-32BE.\n    \"0000feff\".decodeHex(),\n  )\n\n/**\n * Returns an array containing only elements found in this array and also in [other]. The returned\n * elements are in the same order as in this.\n */\ninternal fun Array<String>.intersect(\n  other: Array<String>,\n  comparator: Comparator<in String>,\n): Array<String> {\n  val result = mutableListOf<String>()\n  for (a in this) {\n    for (b in other) {\n      if (comparator.compare(a, b) == 0) {\n        result.add(a)\n        break\n      }\n    }\n  }\n  return result.toTypedArray()\n}\n\n/**\n * Returns true if there is an element in this array that is also in [other]. This method terminates\n * if any intersection is found. The sizes of both arguments are assumed to be so small, and the\n * likelihood of an intersection so great, that it is not worth the CPU cost of sorting or the\n * memory cost of hashing.\n */\ninternal fun Array<String>.hasIntersection(\n  other: Array<String>?,\n  comparator: Comparator<in String>,\n): Boolean {\n  if (isEmpty() || other == null || other.isEmpty()) {\n    return false\n  }\n  for (a in this) {\n    for (b in other) {\n      if (comparator.compare(a, b) == 0) {\n        return true\n      }\n    }\n  }\n  return false\n}\n\ninternal fun Array<String>.indexOf(\n  value: String,\n  comparator: Comparator<String>,\n): Int = indexOfFirst { comparator.compare(it, value) == 0 }\n\n@Suppress(\"UNCHECKED_CAST\")\ninternal fun Array<String>.concat(value: String): Array<String> {\n  val result = copyOf(size + 1)\n  result[result.lastIndex] = value\n  return result as Array<String>\n}\n\n/** Increments [startIndex] until this string is not ASCII whitespace. Stops at [endIndex]. */\ninternal fun String.indexOfFirstNonAsciiWhitespace(\n  startIndex: Int = 0,\n  endIndex: Int = length,\n): Int {\n  for (i in startIndex until endIndex) {\n    when (this[i]) {\n      '\\t', '\\n', '\\u000C', '\\r', ' ' -> Unit\n      else -> return i\n    }\n  }\n  return endIndex\n}\n\n/**\n * Decrements [endIndex] until `input[endIndex - 1]` is not ASCII whitespace. Stops at [startIndex].\n */\ninternal fun String.indexOfLastNonAsciiWhitespace(\n  startIndex: Int = 0,\n  endIndex: Int = length,\n): Int {\n  for (i in endIndex - 1 downTo startIndex) {\n    when (this[i]) {\n      '\\t', '\\n', '\\u000C', '\\r', ' ' -> Unit\n      else -> return i + 1\n    }\n  }\n  return startIndex\n}\n\n/** Equivalent to `string.substring(startIndex, endIndex).trim()`. */\nfun String.trimSubstring(\n  startIndex: Int = 0,\n  endIndex: Int = length,\n): String {\n  val start = indexOfFirstNonAsciiWhitespace(startIndex, endIndex)\n  val end = indexOfLastNonAsciiWhitespace(start, endIndex)\n  return substring(start, end)\n}\n\n/**\n * Returns the index of the first character in this string that contains a character in\n * [delimiters]. Returns endIndex if there is no such character.\n */\nfun String.delimiterOffset(\n  delimiters: String,\n  startIndex: Int = 0,\n  endIndex: Int = length,\n): Int {\n  for (i in startIndex until endIndex) {\n    if (this[i] in delimiters) return i\n  }\n  return endIndex\n}\n\n/**\n * Returns the index of the first character in this string that is [delimiter]. Returns [endIndex]\n * if there is no such character.\n */\nfun String.delimiterOffset(\n  delimiter: Char,\n  startIndex: Int = 0,\n  endIndex: Int = length,\n): Int {\n  for (i in startIndex until endIndex) {\n    if (this[i] == delimiter) return i\n  }\n  return endIndex\n}\n\n/**\n * Returns the index of the first character in this string that is either a control character (like\n * `\\u0000` or `\\n`) or a non-ASCII character. Returns -1 if this string has no such characters.\n */\ninternal fun String.indexOfControlOrNonAscii(): Int {\n  for (i in 0 until length) {\n    val c = this[i]\n    if (c <= '\\u001f' || c >= '\\u007f') {\n      return i\n    }\n  }\n  return -1\n}\n\n/** Returns true if we should void putting this this header in an exception or toString(). */\ninternal fun isSensitiveHeader(name: String): Boolean =\n  name.equals(\"Authorization\", ignoreCase = true) ||\n    name.equals(\"Cookie\", ignoreCase = true) ||\n    name.equals(\"Proxy-Authorization\", ignoreCase = true) ||\n    name.equals(\"Set-Cookie\", ignoreCase = true)\n\ninternal fun Char.parseHexDigit(): Int =\n  when (this) {\n    in '0'..'9' -> this - '0'\n    in 'a'..'f' -> this - 'a' + 10\n    in 'A'..'F' -> this - 'A' + 10\n    else -> -1\n  }\n\ninternal infix fun Byte.and(mask: Int): Int = toInt() and mask\n\ninternal infix fun Short.and(mask: Int): Int = toInt() and mask\n\ninternal infix fun Int.and(mask: Long): Long = toLong() and mask\n\n@Throws(IOException::class)\ninternal fun BufferedSink.writeMedium(medium: Int) {\n  writeByte(medium.ushr(16) and 0xff)\n  writeByte(medium.ushr(8) and 0xff)\n  writeByte(medium and 0xff)\n}\n\n@Throws(IOException::class)\ninternal fun BufferedSource.readMedium(): Int =\n  (\n    readByte() and 0xff shl 16\n      or (readByte() and 0xff shl 8)\n      or (readByte() and 0xff)\n  )\n\n/** Run [block] until it either throws an [IOException] or completes. */\ninternal inline fun ignoreIoExceptions(block: () -> Unit) {\n  try {\n    block()\n  } catch (_: IOException) {\n  }\n}\n\ninternal fun Buffer.skipAll(b: Byte): Int {\n  var count = 0\n  while (!exhausted() && this[0] == b) {\n    count++\n    readByte()\n  }\n  return count\n}\n\n/**\n * Returns the index of the next non-whitespace character in this. Result is undefined if input\n * contains newline characters.\n */\ninternal fun String.indexOfNonWhitespace(startIndex: Int = 0): Int {\n  for (i in startIndex until length) {\n    val c = this[i]\n    if (c != ' ' && c != '\\t') {\n      return i\n    }\n  }\n  return length\n}\n\nfun String.toLongOrDefault(defaultValue: Long): Long =\n  try {\n    toLong()\n  } catch (_: NumberFormatException) {\n    defaultValue\n  }\n\n/**\n * Returns this as a non-negative integer, or 0 if it is negative, or [Int.MAX_VALUE] if it is too\n * large, or [defaultValue] if it cannot be parsed.\n */\ninternal fun String?.toNonNegativeInt(defaultValue: Int): Int {\n  try {\n    val value = this?.toLong() ?: return defaultValue\n    return when {\n      value > Int.MAX_VALUE -> Int.MAX_VALUE\n      value < 0 -> 0\n      else -> value.toInt()\n    }\n  } catch (_: NumberFormatException) {\n    return defaultValue\n  }\n}\n\n/** Closes this, ignoring any checked exceptions. */\nfun Closeable.closeQuietly() {\n  try {\n    close()\n  } catch (rethrown: RuntimeException) {\n    throw rethrown\n  } catch (_: Exception) {\n  }\n}\n\n/**\n * Returns true if file streams can be manipulated independently of their paths. This is typically\n * true for systems like Mac, Unix, and Linux that use inodes in their file system interface. It is\n * typically false on Windows.\n *\n * If this returns false we won't permit simultaneous reads and writes. When writes commit we need\n * to delete the previous snapshots, and that won't succeed if the file is open. (We do permit\n * multiple simultaneous reads.)\n *\n * @param file a file in the directory to check. This file shouldn't already exist!\n */\ninternal fun FileSystem.isCivilized(file: Path): Boolean {\n  sink(file).use {\n    try {\n      delete(file)\n      return true\n    } catch (_: IOException) {\n    }\n  }\n  delete(file)\n  return false\n}\n\n/** Delete file we expect but don't require to exist. */\ninternal fun FileSystem.deleteIfExists(path: Path) {\n  try {\n    delete(path)\n  } catch (fnfe: FileNotFoundException) {\n    return\n  }\n}\n\n/** Tolerant delete, try to clear as many files as possible even after a failure. */\ninternal fun FileSystem.deleteContents(directory: Path) {\n  var exception: IOException? = null\n  val files =\n    try {\n      list(directory)\n    } catch (fnfe: FileNotFoundException) {\n      return\n    }\n  for (file in files) {\n    try {\n      if (metadata(file).isDirectory) {\n        deleteContents(file)\n      }\n\n      delete(file)\n    } catch (ioe: IOException) {\n      if (exception == null) {\n        exception = ioe\n      }\n    }\n  }\n  if (exception != null) {\n    throw exception\n  }\n}\n\ninternal fun <E> MutableList<E>.addIfAbsent(element: E) {\n  if (!contains(element)) add(element)\n}\n\ninternal fun Exception.withSuppressed(suppressed: List<Exception>): Throwable =\n  apply {\n    for (e in suppressed) addSuppressed(e)\n  }\n\ninternal inline fun <T> Iterable<T>.filterList(predicate: T.() -> Boolean): List<T> {\n  var result: List<T> = emptyList()\n  for (i in this) {\n    if (predicate(i)) {\n      if (result.isEmpty()) result = mutableListOf()\n      (result as MutableList<T>).add(i)\n    }\n  }\n  return result\n}\n\ninternal const val USER_AGENT: String = \"okhttp/${CONST_VERSION}\"\n\ninternal fun checkOffsetAndCount(\n  arrayLength: Long,\n  offset: Long,\n  count: Long,\n) {\n  if (offset or count < 0L || offset > arrayLength || arrayLength - offset < count) {\n    throw ArrayIndexOutOfBoundsException(\"length=$arrayLength, offset=$offset, count=$offset\")\n  }\n}\n\ninternal fun <T> interleave(\n  a: Iterable<T>,\n  b: Iterable<T>,\n): List<T> {\n  val ia = a.iterator()\n  val ib = b.iterator()\n\n  return buildList {\n    while (ia.hasNext() || ib.hasNext()) {\n      if (ia.hasNext()) {\n        add(ia.next())\n      }\n      if (ib.hasNext()) {\n        add(ib.next())\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/-UtilJvm.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal\n\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.net.SocketTimeoutException\nimport java.nio.charset.Charset\nimport java.util.Collections\nimport java.util.Locale\nimport java.util.TimeZone\nimport java.util.concurrent.ThreadFactory\nimport java.util.concurrent.TimeUnit\nimport kotlin.text.Charsets.UTF_16BE\nimport kotlin.text.Charsets.UTF_16LE\nimport kotlin.text.Charsets.UTF_32BE\nimport kotlin.text.Charsets.UTF_32LE\nimport kotlin.text.Charsets.UTF_8\nimport kotlin.time.Duration\nimport okhttp3.Dispatcher\nimport okhttp3.EventListener\nimport okhttp3.Headers\nimport okhttp3.HttpUrl\nimport okhttp3.HttpUrl.Companion.defaultPort\nimport okhttp3.OkHttpClient\nimport okhttp3.Response\nimport okhttp3.internal.http2.Header\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.Source\n\n/** GMT and UTC are equivalent for our purposes. */\n@JvmField\ninternal val UTC: TimeZone = TimeZone.getTimeZone(\"GMT\")!!\n\ninternal fun threadFactory(\n  name: String,\n  daemon: Boolean,\n): ThreadFactory =\n  ThreadFactory { runnable ->\n    Thread(runnable, name).apply {\n      isDaemon = daemon\n    }\n  }\n\ninternal fun HttpUrl.toHostHeader(includeDefaultPort: Boolean = false): String {\n  val host =\n    if (\":\" in host) {\n      \"[$host]\"\n    } else {\n      host\n    }\n  return if (includeDefaultPort || port != defaultPort(scheme)) {\n    \"$host:$port\"\n  } else {\n    host\n  }\n}\n\n/** Returns a [Locale.US] formatted [String]. */\ninternal fun format(\n  format: String,\n  vararg args: Any,\n): String = String.format(Locale.US, format, *args)\n\n/**\n * will also strip BOM from the source\n */\n@Throws(IOException::class)\ninternal fun BufferedSource.readBomAsCharset(default: Charset): Charset =\n  when (select(UNICODE_BOMS)) {\n    // a mapping from the index of encoding methods in UNICODE_BOMS to its corresponding encoding method\n    0 -> UTF_8\n\n    1 -> UTF_16BE\n\n    2 -> UTF_32LE\n\n    3 -> UTF_16LE\n\n    4 -> UTF_32BE\n\n    -1 -> default\n\n    else -> throw AssertionError()\n  }\n\ninternal fun checkDuration(\n  name: String,\n  duration: Long,\n  unit: TimeUnit,\n): Int {\n  check(duration >= 0L) { \"$name < 0\" }\n  val millis = unit.toMillis(duration)\n  require(millis <= Integer.MAX_VALUE) { \"$name too large\" }\n  require(millis != 0L || duration <= 0L) { \"$name too small\" }\n  return millis.toInt()\n}\n\ninternal fun checkDuration(\n  name: String,\n  duration: Duration,\n): Int {\n  check(!duration.isNegative()) { \"$name < 0\" }\n  val millis = duration.inWholeMilliseconds\n  require(millis <= Integer.MAX_VALUE) { \"$name too large\" }\n  require(millis != 0L || !duration.isPositive()) { \"$name too small\" }\n  return millis.toInt()\n}\n\ninternal fun List<Header>.toHeaders(): Headers {\n  val builder = Headers.Builder()\n  for ((name, value) in this) {\n    builder.addLenient(name.utf8(), value.utf8())\n  }\n  return builder.build()\n}\n\ninternal fun Headers.toHeaderList(): List<Header> =\n  (0 until size).map {\n    Header(name(it), value(it))\n  }\n\n/** Returns true if an HTTP request for this URL and [other] can reuse a connection. */\ninternal fun HttpUrl.canReuseConnectionFor(other: HttpUrl): Boolean =\n  host == other.host &&\n    port == other.port &&\n    scheme == other.scheme\n\ninternal fun EventListener.asFactory() = EventListener.Factory { this }\n\n/**\n * Reads until this is exhausted or the deadline has been reached. This is careful to not extend the\n * deadline if one exists already.\n */\n@Throws(IOException::class)\ninternal fun Source.skipAll(\n  duration: Int,\n  timeUnit: TimeUnit,\n): Boolean {\n  val nowNs = System.nanoTime()\n  val originalDurationNs =\n    if (timeout().hasDeadline()) {\n      timeout().deadlineNanoTime() - nowNs\n    } else {\n      Long.MAX_VALUE\n    }\n  timeout().deadlineNanoTime(nowNs + minOf(originalDurationNs, timeUnit.toNanos(duration.toLong())))\n  return try {\n    val skipBuffer = Buffer()\n    while (read(skipBuffer, 8192) != -1L) {\n      skipBuffer.clear()\n    }\n    true // Success! The source has been exhausted.\n  } catch (_: InterruptedIOException) {\n    false // We ran out of time before exhausting the source.\n  } finally {\n    if (originalDurationNs == Long.MAX_VALUE) {\n      timeout().clearDeadline()\n    } else {\n      timeout().deadlineNanoTime(nowNs + originalDurationNs)\n    }\n  }\n}\n\n@Throws(IOException::class)\ninternal fun BufferedSource.skipAll() {\n  while (!exhausted()) {\n    skip(buffer.size)\n  }\n}\n\n/**\n * Attempts to exhaust this, returning true if successful. This is useful when reading a complete\n * source is helpful, such as when doing so completes a cache body or frees a socket connection for\n * reuse.\n */\ninternal fun Source.discard(\n  timeout: Int,\n  timeUnit: TimeUnit,\n): Boolean =\n  try {\n    this.skipAll(timeout, timeUnit)\n  } catch (_: IOException) {\n    false\n  }\n\n/**\n * Returns true if new reads and writes should be attempted on this.\n *\n * Unfortunately Java's networking APIs don't offer a good health check, so we go on our own by\n * attempting to read with a short timeout. If the fails immediately we know the socket is\n * unhealthy.\n *\n * @param source the source used to read bytes from the socket.\n */\ninternal fun Socket.isHealthy(source: BufferedSource): Boolean =\n  try {\n    val readTimeout = soTimeout\n    try {\n      soTimeout = 1\n      !source.exhausted()\n    } finally {\n      soTimeout = readTimeout\n    }\n  } catch (_: SocketTimeoutException) {\n    true // Read timed out; socket is good.\n  } catch (_: IOException) {\n    false // Couldn't read; socket is closed.\n  }\n\ninternal inline fun threadName(\n  name: String,\n  block: () -> Unit,\n) {\n  val currentThread = Thread.currentThread()\n  val oldName = currentThread.name\n  currentThread.name = name\n  try {\n    block()\n  } finally {\n    currentThread.name = oldName\n  }\n}\n\n/** Returns the Content-Length as reported by the response headers. */\ninternal fun Response.headersContentLength(): Long = headers[\"Content-Length\"]?.toLongOrDefault(-1L) ?: -1L\n\n/** Returns an immutable wrap of this. */\n@Suppress(\"NOTHING_TO_INLINE\")\ninternal inline fun <T> List<T>.unmodifiable(): List<T> = Collections.unmodifiableList(this)\n\n/** Returns an immutable wrap of this. */\n@Suppress(\"NOTHING_TO_INLINE\")\ninternal inline fun <T> Set<T>.unmodifiable(): Set<T> = Collections.unmodifiableSet(this)\n\n/** Returns an immutable wrap of this. */\n@Suppress(\"NOTHING_TO_INLINE\")\ninternal inline fun <K, V> Map<K, V>.unmodifiable(): Map<K, V> = Collections.unmodifiableMap(this)\n\n/** Returns an immutable copy of this. */\n@Suppress(\"UNCHECKED_CAST\")\ninternal fun <T> List<T>.toImmutableList(): List<T> =\n  when {\n    this.isEmpty() -> emptyList()\n\n    this.size == 1 -> Collections.singletonList(this[0])\n\n    // Collection.toArray returns Object[] (covariant).\n    // It is faster than creating real T[] via reflection (Arrays.copyOf).\n    else -> (this as java.util.Collection<*>).toArray().asList().unmodifiable() as List<T>\n  }\n\n/** Returns an immutable list containing [elements]. */\n@SafeVarargs\ninternal fun <T> immutableListOf(vararg elements: T): List<T> = elements.toImmutableList()\n\n/** Returns an immutable list from copy of this. */\ninternal fun <T> Array<out T>?.toImmutableList(): List<T> =\n  when {\n    this.isNullOrEmpty() -> emptyList()\n    this.size == 1 -> Collections.singletonList(this[0])\n    else -> this.clone().asList().unmodifiable()\n  }\n\n/** Closes this, ignoring any checked exceptions. */\ninternal fun Socket.closeQuietly() {\n  try {\n    close()\n  } catch (e: AssertionError) {\n    throw e\n  } catch (rethrown: RuntimeException) {\n    if (rethrown.message == \"bio == null\") {\n      // Conscrypt in Android 10 and 11 may throw closing an SSLSocket. This is safe to ignore.\n      // https://issuetracker.google.com/issues/177450597\n      return\n    }\n    throw rethrown\n  } catch (_: Exception) {\n  }\n}\n\n/** Closes this, ignoring any checked exceptions.  */\ninternal fun ServerSocket.closeQuietly() {\n  try {\n    close()\n  } catch (rethrown: RuntimeException) {\n    throw rethrown\n  } catch (_: Exception) {\n  }\n}\n\ninternal fun Long.toHexString(): String = java.lang.Long.toHexString(this)\n\ninternal fun Int.toHexString(): String = Integer.toHexString(this)\n\ninternal fun <T> readFieldOrNull(\n  instance: Any,\n  fieldType: Class<T>,\n  fieldName: String,\n): T? {\n  var c: Class<*> = instance.javaClass\n  while (c != Any::class.java) {\n    try {\n      val field = c.getDeclaredField(fieldName)\n      field.isAccessible = true\n      val value = field.get(instance)\n      return if (!fieldType.isInstance(value)) null else fieldType.cast(value)\n    } catch (_: NoSuchFieldException) {\n    }\n\n    c = c.superclass\n  }\n\n  // Didn't find the field we wanted. As a last gasp attempt,\n  // try to find the value on a delegate.\n  if (fieldName != \"delegate\") {\n    val delegate = readFieldOrNull(instance, Any::class.java, \"delegate\")\n    if (delegate != null) return readFieldOrNull(delegate, fieldType, fieldName)\n  }\n\n  return null\n}\n\n@JvmField\ninternal val assertionsEnabled: Boolean = OkHttpClient::class.java.desiredAssertionStatus()\n\n/** Dispatcher is not [Lockable] because we don't want that type in our public API. */\ninternal fun Dispatcher.assertLockNotHeld() {\n  if (assertionsEnabled && Thread.holdsLock(this)) {\n    throw AssertionError(\"Thread ${Thread.currentThread().name} MUST NOT hold lock on $this\")\n  }\n}\n\n/**\n * Returns the string \"OkHttp\" unless the library has been shaded for inclusion in another library,\n * or obfuscated with tools like R8 or ProGuard. In such cases it'll return a longer string like\n * \"com.example.shaded.okhttp3.OkHttp\". In large applications it's possible to have multiple OkHttp\n * instances; this makes it clear which is which.\n */\n@JvmField\ninternal val okHttpName: String =\n  OkHttpClient::class.java.name\n    .removePrefix(\"okhttp3.\")\n    .removeSuffix(\"Client\")\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/IsProbablyUtf8.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport java.io.EOFException\nimport okio.BufferedSource\n\n/**\n * Returns true if the body in question probably contains human-readable text. Uses a small\n * sample of code points to detect Unicode control characters commonly used in binary file\n * signatures.\n *\n * @param codePointLimit the number of code points to read in order to make a decision.\n */\ninternal fun BufferedSource.isProbablyUtf8(codePointLimit: Long = Long.MAX_VALUE): Boolean {\n  try {\n    val peek = peek()\n    for (i in 0 until codePointLimit) {\n      if (peek.exhausted()) {\n        break\n      }\n      val codePoint = peek.readUtf8CodePoint()\n      if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {\n        return false\n      }\n    }\n    return true\n  } catch (_: EOFException) {\n    return false // Truncated UTF-8 sequence.\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/NativeImageTestsAccessors.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport okhttp3.Cache\nimport okhttp3.Dispatcher\nimport okhttp3.Response\nimport okhttp3.internal.connection.Exchange\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.connection.RealConnection\nimport okio.FileSystem\nimport okio.Path\n\ninternal fun buildCache(\n  file: Path,\n  maxSize: Long,\n  fileSystem: FileSystem,\n): Cache = Cache(fileSystem, file, maxSize)\n\ninternal var RealConnection.idleAtNsAccessor: Long\n  get() = idleAtNs\n  set(value) {\n    idleAtNs = value\n  }\n\ninternal val Response.exchangeAccessor: Exchange?\n  get() = this.exchange\n\ninternal val Exchange.connectionAccessor: RealConnection\n  get() = this.connection\n\ninternal fun Dispatcher.finishedAccessor(call: RealCall.AsyncCall) = this.finished(call)\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/SuppressSignatureCheck.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport kotlin.annotation.AnnotationRetention.BINARY\nimport kotlin.annotation.AnnotationTarget.CLASS\nimport kotlin.annotation.AnnotationTarget.CONSTRUCTOR\nimport kotlin.annotation.AnnotationTarget.FUNCTION\n\n@Retention(BINARY)\n@MustBeDocumented\n@Target(CONSTRUCTOR, CLASS, FUNCTION)\ninternal annotation class SuppressSignatureCheck\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/Tags.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport java.util.concurrent.atomic.AtomicReference\nimport kotlin.reflect.KClass\n\n/**\n * An immutable collection of key-value pairs implemented as a singly-linked list.\n *\n * Build up a collection by starting with [EmptyTags] and repeatedly calling [plus]. Each such call\n * returns a new instance.\n *\n * This collection is optimized for safe concurrent access over a very small number of elements.\n *\n * This collection and is expected to hold fewer than 10 elements. Each operation is _O(N)_, and so\n * building an instance with _N_ elements is _O(N**2)_.\n */\ninternal sealed class Tags {\n  /**\n   * Returns a tags instance that maps [key] to [value]. If [value] is null, this returns a tags\n   * instance that does not have any mapping for [key].\n   */\n  abstract fun <T : Any> plus(\n    key: KClass<T>,\n    value: T?,\n  ): Tags\n\n  abstract operator fun <T : Any> get(key: KClass<T>): T?\n}\n\n/** An empty tags. This is always the tail of a [LinkedTags] chain. */\ninternal object EmptyTags : Tags() {\n  override fun <T : Any> plus(\n    key: KClass<T>,\n    value: T?,\n  ): Tags =\n    when {\n      value != null -> LinkedTags(key, value, this)\n      else -> this\n    }\n\n  override fun <T : Any> get(key: KClass<T>): T? = null\n\n  override fun toString() = \"{}\"\n}\n\n/**\n * An invariant of this implementation is that [next] must not contain a mapping for [key].\n * Otherwise, we would have two values for the same key.\n */\nprivate class LinkedTags<K : Any>(\n  private val key: KClass<K>,\n  private val value: K,\n  private val next: Tags,\n) : Tags() {\n  override fun <T : Any> plus(\n    key: KClass<T>,\n    value: T?,\n  ): Tags {\n    // Create a copy of this `LinkedTags` that doesn't have a mapping for `key`.\n    val thisMinusKey =\n      when {\n        key == this.key -> {\n          next\n        }\n\n        // Subtract this!\n\n        else -> {\n          val nextMinusKey = next.plus(key, null)\n          when {\n            nextMinusKey === next -> this\n\n            // Same as the following line, but with fewer allocations.\n            else -> LinkedTags(this.key, this.value, nextMinusKey)\n          }\n        }\n      }\n\n    // Return a new `Tags` that maps `key` to `value`.\n    return when {\n      value != null -> LinkedTags(key, value, thisMinusKey)\n      else -> thisMinusKey\n    }\n  }\n\n  override fun <T : Any> get(key: KClass<T>): T? =\n    when {\n      key == this.key -> key.java.cast(value)\n      else -> next[key]\n    }\n\n  /** Returns a [toString] consistent with [Map], with elements in insertion order. */\n  override fun toString(): String =\n    generateSequence<LinkedTags<*>>(seed = this) { it.next as? LinkedTags<*> }\n      .toList()\n      .reversed()\n      .joinToString(prefix = \"{\", postfix = \"}\") { \"${it.key}=${it.value}\" }\n}\n\ninternal fun <T : Any> AtomicReference<Tags>.computeIfAbsent(\n  type: KClass<T>,\n  compute: () -> T,\n): T {\n  var computed: T? = null\n\n  while (true) {\n    val tags = get()\n\n    // If the element is already present. Return it.\n    val existing = tags[type]\n    if (existing != null) return existing\n\n    if (computed == null) {\n      computed = compute()\n    }\n\n    // If we successfully add the computed element, we're done.\n    val newTags = tags.plus(type, computed)\n    if (compareAndSet(tags, newTags)) return computed\n\n    // We lost the race. Possibly to other code that was putting a *different* key. Try again!\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/UnreadableResponseBody.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport okhttp3.MediaType\nimport okhttp3.Response\nimport okhttp3.ResponseBody\nimport okio.Buffer\nimport okio.Source\nimport okio.Timeout\nimport okio.buffer\n\ninternal class UnreadableResponseBody(\n  private val mediaType: MediaType?,\n  private val contentLength: Long,\n) : ResponseBody(),\n  Source {\n  override fun contentType() = mediaType\n\n  override fun contentLength() = contentLength\n\n  override fun source() = buffer()\n\n  override fun read(\n    sink: Buffer,\n    byteCount: Long,\n  ): Long =\n    throw IllegalStateException(\n      \"\"\"\n      |Unreadable ResponseBody! These Response objects have bodies that are stripped:\n      | * Response.cacheResponse\n      | * Response.networkResponse\n      | * Response.priorResponse\n      | * EventSourceListener\n      | * WebSocketListener\n      |(It is safe to call contentType() and contentLength() on these response bodies.)\n      \"\"\".trimMargin(),\n    )\n\n  override fun timeout() = Timeout.NONE\n\n  override fun close() {\n  }\n}\n\nfun Response.stripBody(): Response =\n  newBuilder()\n    .body(UnreadableResponseBody(body.contentType(), body.contentLength()))\n    .build()\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/authenticator/JavaNetAuthenticator.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.authenticator\n\nimport java.io.IOException\nimport java.net.Authenticator\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport okhttp3.Credentials\nimport okhttp3.Dns\nimport okhttp3.HttpUrl\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.Route\n\n/**\n * Adapts [Authenticator] to [okhttp3.Authenticator]. Configure OkHttp to use [Authenticator] with\n * [okhttp3.OkHttpClient.Builder.authenticator] or [okhttp3.OkHttpClient.Builder.proxyAuthenticator].\n */\nclass JavaNetAuthenticator(\n  private val defaultDns: Dns = Dns.SYSTEM,\n) : okhttp3.Authenticator {\n  @Throws(IOException::class)\n  override fun authenticate(\n    route: Route?,\n    response: Response,\n  ): Request? {\n    val challenges = response.challenges()\n    val request = response.request\n    val url = request.url\n    val proxyAuthorization = response.code == 407\n    val proxy = route?.proxy ?: Proxy.NO_PROXY\n\n    for (challenge in challenges) {\n      if (!\"Basic\".equals(challenge.scheme, ignoreCase = true)) {\n        continue\n      }\n\n      val dns = route?.address?.dns ?: defaultDns\n      val auth =\n        if (proxyAuthorization) {\n          val proxyAddress = proxy.address() as InetSocketAddress\n          Authenticator.requestPasswordAuthentication(\n            proxyAddress.hostName,\n            proxy.connectToInetAddress(url, dns),\n            proxyAddress.port,\n            url.scheme,\n            challenge.realm,\n            challenge.scheme,\n            url.toUrl(),\n            Authenticator.RequestorType.PROXY,\n          )\n        } else {\n          Authenticator.requestPasswordAuthentication(\n            url.host,\n            proxy.connectToInetAddress(url, dns),\n            url.port,\n            url.scheme,\n            challenge.realm,\n            challenge.scheme,\n            url.toUrl(),\n            Authenticator.RequestorType.SERVER,\n          )\n        }\n\n      if (auth != null) {\n        val credentialHeader = if (proxyAuthorization) \"Proxy-Authorization\" else \"Authorization\"\n        val credential =\n          Credentials.basic(\n            auth.userName,\n            String(auth.password),\n            challenge.charset,\n          )\n        return request\n          .newBuilder()\n          .header(credentialHeader, credential)\n          .build()\n      }\n    }\n\n    return null // No challenges were satisfied!\n  }\n\n  @Throws(IOException::class)\n  private fun Proxy.connectToInetAddress(\n    url: HttpUrl,\n    dns: Dns,\n  ): InetAddress =\n    when (type()) {\n      Proxy.Type.DIRECT -> dns.lookup(url.host).first()\n      else -> (address() as InetSocketAddress).address\n    }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/cache/CacheInterceptor.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3.internal.cache\n\nimport java.io.IOException\nimport java.net.HttpURLConnection.HTTP_GATEWAY_TIMEOUT\nimport java.net.HttpURLConnection.HTTP_NOT_MODIFIED\nimport java.util.concurrent.TimeUnit.MILLISECONDS\nimport okhttp3.Cache\nimport okhttp3.Headers\nimport okhttp3.Interceptor\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.discard\nimport okhttp3.internal.http.ExchangeCodec\nimport okhttp3.internal.http.HttpMethod\nimport okhttp3.internal.http.RealResponseBody\nimport okhttp3.internal.http.promisesBody\nimport okhttp3.internal.stripBody\nimport okio.Buffer\nimport okio.Source\nimport okio.Timeout\nimport okio.buffer\n\n/** Serves requests from the cache and writes responses to the cache. */\nclass CacheInterceptor : Interceptor {\n  @Throws(IOException::class)\n  override fun intercept(chain: Interceptor.Chain): Response {\n    val call = chain.call()\n    val cache = chain.cache\n    val cacheCandidate = cache?.get(chain.request().requestForCache())\n\n    val now = System.currentTimeMillis()\n\n    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()\n    val networkRequest = strategy.networkRequest\n    val cacheResponse = strategy.cacheResponse\n\n    cache?.trackResponse(strategy)\n\n    if (cacheCandidate != null && cacheResponse == null) {\n      // The cache candidate wasn't applicable. Close it.\n      cacheCandidate.body.closeQuietly()\n    }\n\n    // If we're forbidden from using the network and the cache is insufficient, fail.\n    if (networkRequest == null && cacheResponse == null) {\n      return Response\n        .Builder()\n        .request(chain.request())\n        .protocol(Protocol.HTTP_1_1)\n        .code(HTTP_GATEWAY_TIMEOUT)\n        .message(\"Unsatisfiable Request (only-if-cached)\")\n        .sentRequestAtMillis(-1L)\n        .receivedResponseAtMillis(System.currentTimeMillis())\n        .build()\n        .also {\n          chain.eventListener.satisfactionFailure(call, it)\n        }\n    }\n\n    // If we don't need the network, we're done.\n    if (networkRequest == null) {\n      return cacheResponse!!\n        .newBuilder()\n        .cacheResponse(cacheResponse.stripBody())\n        .build()\n        .also {\n          chain.eventListener.cacheHit(call, it)\n        }\n    }\n\n    if (cacheResponse != null) {\n      chain.eventListener.cacheConditionalHit(call, cacheResponse)\n    } else if (cache != null) {\n      chain.eventListener.cacheMiss(call)\n    }\n\n    var networkResponse: Response? = null\n    try {\n      networkResponse = chain.proceed(networkRequest)\n    } finally {\n      // If we're crashing on I/O or otherwise, don't leak the cache body.\n      if (networkResponse == null && cacheCandidate != null) {\n        cacheCandidate.body.closeQuietly()\n      }\n    }\n\n    // If we have a cache response too, then we're doing a conditional get.\n    if (cacheResponse != null) {\n      if (networkResponse?.code == HTTP_NOT_MODIFIED) {\n        val response =\n          cacheResponse\n            .newBuilder()\n            .headers(combine(cacheResponse.headers, networkResponse.headers))\n            .sentRequestAtMillis(networkResponse.sentRequestAtMillis)\n            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)\n            .cacheResponse(cacheResponse.stripBody())\n            .networkResponse(networkResponse.stripBody())\n            .build()\n\n        networkResponse.body.close()\n\n        // Update the cache after combining headers but before stripping the\n        // Content-Encoding header (as performed by initContentStream()).\n        cache!!.trackConditionalCacheHit()\n        cache.update(cacheResponse, response)\n        return response.also {\n          chain.eventListener.cacheHit(call, it)\n        }\n      } else {\n        cacheResponse.body.closeQuietly()\n      }\n    }\n\n    val response =\n      networkResponse!!\n        .newBuilder()\n        .cacheResponse(cacheResponse?.stripBody())\n        .networkResponse(networkResponse.stripBody())\n        .build()\n\n    if (cache != null) {\n      val cacheNetworkRequest = networkRequest.requestForCache()\n\n      if (response.promisesBody() && CacheStrategy.isCacheable(response, cacheNetworkRequest)) {\n        // Offer this request to the cache.\n        val cacheRequest = cache.put(response.newBuilder().request(cacheNetworkRequest).build())\n        return cacheWritingResponse(cacheRequest, response).also {\n          if (cacheResponse != null) {\n            // This will log a conditional cache miss only.\n            chain.eventListener.cacheMiss(call)\n          }\n        }\n      }\n\n      if (HttpMethod.invalidatesCache(networkRequest.method)) {\n        try {\n          cache.remove(networkRequest)\n        } catch (_: IOException) {\n          // The cache cannot be written.\n        }\n      }\n    }\n\n    return response\n  }\n\n  /**\n   * Returns a new source that writes bytes to [cacheRequest] as they are read by the source\n   * consumer. This is careful to discard bytes left over when the stream is closed; otherwise we\n   * may never exhaust the source stream and therefore not complete the cached response.\n   */\n  @Throws(IOException::class)\n  private fun cacheWritingResponse(\n    cacheRequest: CacheRequest?,\n    response: Response,\n  ): Response {\n    // Some apps return a null body; for compatibility we treat that like a null cache request.\n    if (cacheRequest == null) return response\n    val cacheBodyUnbuffered = cacheRequest.body()\n\n    val source = response.body.source()\n    val cacheBody = cacheBodyUnbuffered.buffer()\n\n    val cacheWritingSource =\n      object : Source {\n        private var cacheRequestClosed = false\n\n        @Throws(IOException::class)\n        override fun read(\n          sink: Buffer,\n          byteCount: Long,\n        ): Long {\n          val bytesRead: Long\n          try {\n            bytesRead = source.read(sink, byteCount)\n          } catch (e: IOException) {\n            if (!cacheRequestClosed) {\n              cacheRequestClosed = true\n              cacheRequest.abort() // Failed to write a complete cache response.\n            }\n            throw e\n          }\n\n          if (bytesRead == -1L) {\n            if (!cacheRequestClosed) {\n              cacheRequestClosed = true\n              cacheBody.close() // The cache response is complete!\n            }\n            return -1\n          }\n\n          sink.copyTo(cacheBody.buffer, sink.size - bytesRead, bytesRead)\n          cacheBody.emitCompleteSegments()\n          return bytesRead\n        }\n\n        override fun timeout(): Timeout = source.timeout()\n\n        @Throws(IOException::class)\n        override fun close() {\n          if (!cacheRequestClosed &&\n            !discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)\n          ) {\n            cacheRequestClosed = true\n            cacheRequest.abort()\n          }\n          source.close()\n        }\n      }\n\n    val contentType = response.header(\"Content-Type\")\n    val contentLength = response.body.contentLength()\n    return response\n      .newBuilder()\n      .body(RealResponseBody(contentType, contentLength, cacheWritingSource.buffer()))\n      .build()\n  }\n\n  companion object {\n    /** Combines cached headers with a network headers as defined by RFC 7234, 4.3.4. */\n    private fun combine(\n      cachedHeaders: Headers,\n      networkHeaders: Headers,\n    ): Headers {\n      val result = Headers.Builder()\n\n      for (index in 0 until cachedHeaders.size) {\n        val fieldName = cachedHeaders.name(index)\n        val value = cachedHeaders.value(index)\n        if (\"Warning\".equals(fieldName, ignoreCase = true) && value.startsWith(\"1\")) {\n          // Drop 100-level freshness warnings.\n          continue\n        }\n        if (isContentSpecificHeader(fieldName) ||\n          !isEndToEnd(fieldName) ||\n          networkHeaders[fieldName] == null\n        ) {\n          result.addLenient(fieldName, value)\n        }\n      }\n\n      for (index in 0 until networkHeaders.size) {\n        val fieldName = networkHeaders.name(index)\n        if (!isContentSpecificHeader(fieldName) && isEndToEnd(fieldName)) {\n          result.addLenient(fieldName, networkHeaders.value(index))\n        }\n      }\n\n      return result.build()\n    }\n\n    /**\n     * Returns true if [fieldName] is an end-to-end HTTP header, as defined by RFC 2616,\n     * 13.5.1.\n     */\n    private fun isEndToEnd(fieldName: String): Boolean =\n      !\"Connection\".equals(fieldName, ignoreCase = true) &&\n        !\"Keep-Alive\".equals(fieldName, ignoreCase = true) &&\n        !\"Proxy-Authenticate\".equals(fieldName, ignoreCase = true) &&\n        !\"Proxy-Authorization\".equals(fieldName, ignoreCase = true) &&\n        !\"TE\".equals(fieldName, ignoreCase = true) &&\n        !\"Trailers\".equals(fieldName, ignoreCase = true) &&\n        !\"Transfer-Encoding\".equals(fieldName, ignoreCase = true) &&\n        !\"Upgrade\".equals(fieldName, ignoreCase = true)\n\n    /**\n     * Returns true if [fieldName] is content specific and therefore should always be used\n     * from cached headers.\n     */\n    private fun isContentSpecificHeader(fieldName: String): Boolean =\n      \"Content-Length\".equals(fieldName, ignoreCase = true) ||\n        \"Content-Encoding\".equals(fieldName, ignoreCase = true) ||\n        \"Content-Type\".equals(fieldName, ignoreCase = true)\n  }\n}\n\nprivate fun Request.requestForCache(): Request {\n  val cacheUrlOverride = cacheUrlOverride\n\n  // Allow POST and QUERY caching only when there is a cacheUrlOverride\n  return if (cacheUrlOverride != null && (HttpMethod.isCacheable(method) || method == \"POST\")) {\n    newBuilder()\n      .get()\n      .url(cacheUrlOverride)\n      .cacheUrlOverride(null)\n      .build()\n  } else {\n    this\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/cache/CacheRequest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.cache\n\nimport java.io.IOException\nimport okio.Sink\n\ninterface CacheRequest {\n  @Throws(IOException::class)\n  fun body(): Sink\n\n  fun abort()\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/cache/CacheStrategy.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.cache\n\nimport java.net.HttpURLConnection.HTTP_BAD_METHOD\nimport java.net.HttpURLConnection.HTTP_GONE\nimport java.net.HttpURLConnection.HTTP_MOVED_PERM\nimport java.net.HttpURLConnection.HTTP_MOVED_TEMP\nimport java.net.HttpURLConnection.HTTP_MULT_CHOICE\nimport java.net.HttpURLConnection.HTTP_NOT_AUTHORITATIVE\nimport java.net.HttpURLConnection.HTTP_NOT_FOUND\nimport java.net.HttpURLConnection.HTTP_NOT_IMPLEMENTED\nimport java.net.HttpURLConnection.HTTP_NO_CONTENT\nimport java.net.HttpURLConnection.HTTP_OK\nimport java.net.HttpURLConnection.HTTP_REQ_TOO_LONG\nimport java.util.Date\nimport java.util.concurrent.TimeUnit.SECONDS\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.http.HTTP_PERM_REDIRECT\nimport okhttp3.internal.http.HTTP_TEMP_REDIRECT\nimport okhttp3.internal.http.toHttpDateOrNull\nimport okhttp3.internal.toNonNegativeInt\n\n/**\n * Given a request and cached response, this figures out whether to use the network, the cache, or\n * both.\n *\n * Selecting a cache strategy may add conditions to the request (like the \"If-Modified-Since\" header\n * for conditional GETs) or warnings to the cached response (if the cached data is potentially\n * stale).\n */\nclass CacheStrategy internal constructor(\n  /** The request to send on the network, or null if this call doesn't use the network. */\n  val networkRequest: Request?,\n  /** The cached response to return or validate; or null if this call doesn't use a cache. */\n  val cacheResponse: Response?,\n) {\n  class Factory(\n    private val nowMillis: Long,\n    internal val request: Request,\n    private val cacheResponse: Response?,\n  ) {\n    /** The server's time when the cached response was served, if known. */\n    private var servedDate: Date? = null\n    private var servedDateString: String? = null\n\n    /** The last modified date of the cached response, if known. */\n    private var lastModified: Date? = null\n    private var lastModifiedString: String? = null\n\n    /**\n     * The expiration date of the cached response, if known. If both this field and the max age are\n     * set, the max age is preferred.\n     */\n    private var expires: Date? = null\n\n    /**\n     * Extension header set by OkHttp specifying the timestamp when the cached HTTP request was\n     * first initiated.\n     */\n    private var sentRequestMillis = 0L\n\n    /**\n     * Extension header set by OkHttp specifying the timestamp when the cached HTTP response was\n     * first received.\n     */\n    private var receivedResponseMillis = 0L\n\n    /** Etag of the cached response. */\n    private var etag: String? = null\n\n    /** Age of the cached response. */\n    private var ageSeconds = -1\n\n    /**\n     * Returns true if computeFreshnessLifetime used a heuristic. If we used a heuristic to serve a\n     * cached response older than 24 hours, we are required to attach a warning.\n     */\n    private fun isFreshnessLifetimeHeuristic(): Boolean = cacheResponse!!.cacheControl.maxAgeSeconds == -1 && expires == null\n\n    init {\n      if (cacheResponse != null) {\n        this.sentRequestMillis = cacheResponse.sentRequestAtMillis\n        this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis\n        val headers = cacheResponse.headers\n        for (i in 0 until headers.size) {\n          val fieldName = headers.name(i)\n          val value = headers.value(i)\n          when {\n            fieldName.equals(\"Date\", ignoreCase = true) -> {\n              servedDate = value.toHttpDateOrNull()\n              servedDateString = value\n            }\n\n            fieldName.equals(\"Expires\", ignoreCase = true) -> {\n              expires = value.toHttpDateOrNull()\n            }\n\n            fieldName.equals(\"Last-Modified\", ignoreCase = true) -> {\n              lastModified = value.toHttpDateOrNull()\n              lastModifiedString = value\n            }\n\n            fieldName.equals(\"ETag\", ignoreCase = true) -> {\n              etag = value\n            }\n\n            fieldName.equals(\"Age\", ignoreCase = true) -> {\n              ageSeconds = value.toNonNegativeInt(-1)\n            }\n          }\n        }\n      }\n    }\n\n    /** Returns a strategy to satisfy [request] using [cacheResponse]. */\n    fun compute(): CacheStrategy {\n      val candidate = computeCandidate()\n\n      // We're forbidden from using the network and the cache is insufficient.\n      if (candidate.networkRequest != null && request.cacheControl.onlyIfCached) {\n        return CacheStrategy(null, null)\n      }\n\n      return candidate\n    }\n\n    /** Returns a strategy to use assuming the request can use the network. */\n    private fun computeCandidate(): CacheStrategy {\n      // No cached response.\n      if (cacheResponse == null) {\n        return CacheStrategy(request, null)\n      }\n\n      // Drop the cached response if it's missing a required handshake.\n      if (request.isHttps && cacheResponse.handshake == null) {\n        return CacheStrategy(request, null)\n      }\n\n      // If this response shouldn't have been stored, it should never be used as a response source.\n      // This check should be redundant as long as the persistence store is well-behaved and the\n      // rules are constant.\n      if (!isCacheable(cacheResponse, request)) {\n        return CacheStrategy(request, null)\n      }\n\n      val requestCaching = request.cacheControl\n      if (requestCaching.noCache || hasConditions(request)) {\n        return CacheStrategy(request, null)\n      }\n\n      val responseCaching = cacheResponse.cacheControl\n\n      val ageMillis = cacheResponseAge()\n      var freshMillis = computeFreshnessLifetime()\n\n      if (requestCaching.maxAgeSeconds != -1) {\n        freshMillis = minOf(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds.toLong()))\n      }\n\n      var minFreshMillis: Long = 0\n      if (requestCaching.minFreshSeconds != -1) {\n        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds.toLong())\n      }\n\n      var maxStaleMillis: Long = 0\n      if (!responseCaching.mustRevalidate && requestCaching.maxStaleSeconds != -1) {\n        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds.toLong())\n      }\n\n      if (!responseCaching.noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {\n        val builder = cacheResponse.newBuilder()\n        if (ageMillis + minFreshMillis >= freshMillis) {\n          builder.addHeader(\"Warning\", \"110 HttpURLConnection \\\"Response is stale\\\"\")\n        }\n        val oneDayMillis = 24 * 60 * 60 * 1000L\n        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {\n          builder.addHeader(\"Warning\", \"113 HttpURLConnection \\\"Heuristic expiration\\\"\")\n        }\n        return CacheStrategy(null, builder.build())\n      }\n\n      // Find a condition to add to the request. If the condition is satisfied, the response body\n      // will not be transmitted.\n      val conditionName: String\n      val conditionValue: String?\n      when {\n        etag != null -> {\n          conditionName = \"If-None-Match\"\n          conditionValue = etag\n        }\n\n        lastModified != null -> {\n          conditionName = \"If-Modified-Since\"\n          conditionValue = lastModifiedString\n        }\n\n        servedDate != null -> {\n          conditionName = \"If-Modified-Since\"\n          conditionValue = servedDateString\n        }\n\n        else -> {\n          return CacheStrategy(request, null)\n        } // No condition! Make a regular request.\n      }\n\n      val conditionalRequestHeaders = request.headers.newBuilder()\n      conditionalRequestHeaders.addLenient(conditionName, conditionValue!!)\n\n      val conditionalRequest =\n        request\n          .newBuilder()\n          .headers(conditionalRequestHeaders.build())\n          .build()\n      return CacheStrategy(conditionalRequest, cacheResponse)\n    }\n\n    /**\n     * Returns the number of milliseconds that the response was fresh for, starting from the served\n     * date.\n     */\n    private fun computeFreshnessLifetime(): Long {\n      val responseCaching = cacheResponse!!.cacheControl\n      if (responseCaching.maxAgeSeconds != -1) {\n        return SECONDS.toMillis(responseCaching.maxAgeSeconds.toLong())\n      }\n\n      val expires = this.expires\n      if (expires != null) {\n        val servedMillis = servedDate?.time ?: receivedResponseMillis\n        val delta = expires.time - servedMillis\n        return if (delta > 0L) delta else 0L\n      }\n\n      if (lastModified != null && cacheResponse.request.url.query == null) {\n        // As recommended by the HTTP RFC and implemented in Firefox, the max age of a document\n        // should be defaulted to 10% of the document's age at the time it was served. Default\n        // expiration dates aren't used for URIs containing a query.\n        val servedMillis = servedDate?.time ?: sentRequestMillis\n        val delta = servedMillis - lastModified!!.time\n        return if (delta > 0L) delta / 10 else 0L\n      }\n\n      return 0L\n    }\n\n    /**\n     * Returns the current age of the response, in milliseconds. The calculation is specified by RFC\n     * 7234, 4.2.3 Calculating Age.\n     */\n    private fun cacheResponseAge(): Long {\n      val servedDate = this.servedDate\n      val apparentReceivedAge =\n        if (servedDate != null) {\n          maxOf(0, receivedResponseMillis - servedDate.time)\n        } else {\n          0\n        }\n\n      val receivedAge =\n        if (ageSeconds != -1) {\n          maxOf(apparentReceivedAge, SECONDS.toMillis(ageSeconds.toLong()))\n        } else {\n          apparentReceivedAge\n        }\n\n      val responseDuration = maxOf(0, receivedResponseMillis - sentRequestMillis)\n      val residentDuration = maxOf(0, nowMillis - receivedResponseMillis)\n      return receivedAge + responseDuration + residentDuration\n    }\n\n    /**\n     * Returns true if the request contains conditions that save the server from sending a response\n     * that the client has locally. When a request is enqueued with its own conditions, the built-in\n     * response cache won't be used.\n     */\n    private fun hasConditions(request: Request): Boolean =\n      request.header(\"If-Modified-Since\") != null || request.header(\"If-None-Match\") != null\n  }\n\n  companion object {\n    /** Returns true if [response] can be stored to later serve another request. */\n    fun isCacheable(\n      response: Response,\n      request: Request,\n    ): Boolean {\n      // Always go to network for uncacheable response codes (RFC 7231 section 6.1), This\n      // implementation doesn't support caching partial content.\n      when (response.code) {\n        HTTP_OK,\n        HTTP_NOT_AUTHORITATIVE,\n        HTTP_NO_CONTENT,\n        HTTP_MULT_CHOICE,\n        HTTP_MOVED_PERM,\n        HTTP_NOT_FOUND,\n        HTTP_BAD_METHOD,\n        HTTP_GONE,\n        HTTP_REQ_TOO_LONG,\n        HTTP_NOT_IMPLEMENTED,\n        HTTP_PERM_REDIRECT,\n        -> {\n          // These codes can be cached unless headers forbid it.\n        }\n\n        HTTP_MOVED_TEMP,\n        HTTP_TEMP_REDIRECT,\n        -> {\n          // These codes can only be cached with the right response headers.\n          // http://tools.ietf.org/html/rfc7234#section-3\n          // s-maxage is not checked because OkHttp is a private cache that should ignore s-maxage.\n          if (response.header(\"Expires\") == null &&\n            response.cacheControl.maxAgeSeconds == -1 &&\n            !response.cacheControl.isPublic &&\n            !response.cacheControl.isPrivate\n          ) {\n            return false\n          }\n        }\n\n        else -> {\n          // All other codes cannot be cached.\n          return false\n        }\n      }\n\n      // A 'no-store' directive on request or response prevents the response from being cached.\n      return !response.cacheControl.noStore && !request.cacheControl.noStore\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/cache/DiskLruCache.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.cache\n\nimport java.io.Closeable\nimport java.io.EOFException\nimport java.io.Flushable\nimport java.io.IOException\nimport okhttp3.internal.cache.DiskLruCache.Editor\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.Lockable\nimport okhttp3.internal.concurrent.Task\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.assertLockHeld\nimport okhttp3.internal.deleteContents\nimport okhttp3.internal.deleteIfExists\nimport okhttp3.internal.isCivilized\nimport okhttp3.internal.okHttpName\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.Platform.Companion.WARN\nimport okio.BufferedSink\nimport okio.FileNotFoundException\nimport okio.FileSystem\nimport okio.ForwardingFileSystem\nimport okio.ForwardingSource\nimport okio.Path\nimport okio.Sink\nimport okio.Source\nimport okio.blackholeSink\nimport okio.buffer\n\n/**\n * A cache that uses a bounded amount of space on a filesystem. Each cache entry has a string key\n * and a fixed number of values. Each key must match the regex `[a-z0-9_-]{1,64}`. Values are byte\n * sequences, accessible as streams or files. Each value must be between `0` and `Int.MAX_VALUE`\n * bytes in length.\n *\n * The cache stores its data in a directory on the filesystem. This directory must be exclusive to\n * the cache; the cache may delete or overwrite files from its directory. It is an error for\n * multiple processes to use the same cache directory at the same time.\n *\n * This cache limits the number of bytes that it will store on the filesystem. When the number of\n * stored bytes exceeds the limit, the cache will remove entries in the background until the limit\n * is satisfied. The limit is not strict: the cache may temporarily exceed it while waiting for\n * files to be deleted. The limit does not include filesystem overhead or the cache journal so\n * space-sensitive applications should set a conservative limit.\n *\n * Clients call [edit] to create or update the values of an entry. An entry may have only one editor\n * at one time; if a value is not available to be edited then [edit] will return null.\n *\n *  * When an entry is being **created** it is necessary to supply a full set of values; the empty\n *    value should be used as a placeholder if necessary.\n *\n *  * When an entry is being **edited**, it is not necessary to supply data for every value; values\n *    default to their previous value.\n *\n * Every [edit] call must be matched by a call to [Editor.commit] or [Editor.abort]. Committing is\n * atomic: a read observes the full set of values as they were before or after the commit, but never\n * a mix of values.\n *\n * Clients call [get] to read a snapshot of an entry. The read will observe the value at the time\n * that [get] was called. Updates and removals after the call do not impact ongoing reads.\n *\n * This class is tolerant of some I/O errors. If files are missing from the filesystem, the\n * corresponding entries will be dropped from the cache. If an error occurs while writing a cache\n * value, the edit will fail silently. Callers should handle other problems by catching\n * `IOException` and responding appropriately.\n *\n * @constructor Create a cache which will reside in [directory]. This cache is lazily initialized on\n *     first access and will be created if it does not exist.\n * @param directory a writable directory.\n * @param valueCount the number of values per cache entry. Must be positive.\n * @param maxSize the maximum number of bytes this cache should use to store.\n */\nclass DiskLruCache(\n  fileSystem: FileSystem,\n  /** Returns the directory where this cache stores its data. */\n  val directory: Path,\n  private val appVersion: Int,\n  internal val valueCount: Int,\n  /** Returns the maximum number of bytes that this cache should use to store its data. */\n  maxSize: Long,\n  /** Used for asynchronous journal rebuilds. */\n  taskRunner: TaskRunner,\n) : Closeable,\n  Flushable,\n  Lockable {\n  internal val fileSystem: FileSystem =\n    object : ForwardingFileSystem(fileSystem) {\n      override fun sink(\n        file: Path,\n        mustCreate: Boolean,\n      ): Sink {\n        file.parent?.let {\n          createDirectories(it)\n        }\n        return super.sink(file, mustCreate)\n      }\n    }\n\n  /** The maximum number of bytes that this cache should use to store its data. */\n  @get:Synchronized @set:Synchronized\n  var maxSize: Long = maxSize\n    set(value) {\n      field = value\n      if (initialized) {\n        cleanupQueue.schedule(cleanupTask) // Trim the existing store if necessary.\n      }\n    }\n\n  /*\n   * This cache uses a journal file named \"journal\". A typical journal file looks like this:\n   *\n   *     libcore.io.DiskLruCache\n   *     1\n   *     100\n   *     2\n   *\n   *     CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054\n   *     DIRTY 335c4c6028171cfddfbaae1a9c313c52\n   *     CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342\n   *     REMOVE 335c4c6028171cfddfbaae1a9c313c52\n   *     DIRTY 1ab96a171faeeee38496d8b330771a7a\n   *     CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234\n   *     READ 335c4c6028171cfddfbaae1a9c313c52\n   *     READ 3400330d1dfc7f3f7f4b8d4d803dfcf6\n   *\n   * The first five lines of the journal form its header. They are the constant string\n   * \"libcore.io.DiskLruCache\", the disk cache's version, the application's version, the value\n   * count, and a blank line.\n   *\n   * Each of the subsequent lines in the file is a record of the state of a cache entry. Each line\n   * contains space-separated values: a state, a key, and optional state-specific values.\n   *\n   *   o DIRTY lines track that an entry is actively being created or updated. Every successful\n   *     DIRTY action should be followed by a CLEAN or REMOVE action. DIRTY lines without a matching\n   *     CLEAN or REMOVE indicate that temporary files may need to be deleted.\n   *\n   *   o CLEAN lines track a cache entry that has been successfully published and may be read. A\n   *     publish line is followed by the lengths of each of its values.\n   *\n   *   o READ lines track accesses for LRU.\n   *\n   *   o REMOVE lines track entries that have been deleted.\n   *\n   * The journal file is appended to as cache operations occur. The journal may occasionally be\n   * compacted by dropping redundant lines. A temporary file named \"journal.tmp\" will be used during\n   * compaction; that file should be deleted if it exists when the cache is opened.\n   */\n\n  private val journalFile: Path\n  private val journalFileTmp: Path\n  private val journalFileBackup: Path\n  private var size: Long = 0L\n  private var journalWriter: BufferedSink? = null\n  internal val lruEntries = LinkedHashMap<String, Entry>(0, 0.75f, true)\n  private var redundantOpCount: Int = 0\n  private var hasJournalErrors: Boolean = false\n  private var civilizedFileSystem: Boolean = false\n\n  // Must be read and written when synchronized on 'this'.\n  private var initialized: Boolean = false\n  internal var closed: Boolean = false\n  private var mostRecentTrimFailed: Boolean = false\n  private var mostRecentRebuildFailed: Boolean = false\n\n  /**\n   * To differentiate between old and current snapshots, each entry is given a sequence number each\n   * time an edit is committed. A snapshot is stale if its sequence number is not equal to its\n   * entry's sequence number.\n   */\n  private var nextSequenceNumber: Long = 0\n\n  private val cleanupQueue = taskRunner.newQueue()\n  private val cleanupTask =\n    object : Task(\"$okHttpName Cache\") {\n      override fun runOnce(): Long {\n        synchronized(this@DiskLruCache) {\n          if (!initialized || closed) {\n            return -1L // Nothing to do.\n          }\n\n          try {\n            trimToSize()\n          } catch (_: IOException) {\n            mostRecentTrimFailed = true\n          }\n\n          try {\n            if (journalRebuildRequired()) {\n              rebuildJournal()\n              redundantOpCount = 0\n            }\n          } catch (_: IOException) {\n            mostRecentRebuildFailed = true\n            journalWriter?.closeQuietly()\n            journalWriter = blackholeSink().buffer()\n          }\n\n          return -1L\n        }\n      }\n    }\n\n  init {\n    require(maxSize > 0L) { \"maxSize <= 0\" }\n    require(valueCount > 0) { \"valueCount <= 0\" }\n\n    this.journalFile = directory / JOURNAL_FILE\n    this.journalFileTmp = directory / JOURNAL_FILE_TEMP\n    this.journalFileBackup = directory / JOURNAL_FILE_BACKUP\n  }\n\n  @Synchronized\n  @Throws(IOException::class)\n  fun initialize() {\n    assertLockHeld()\n\n    if (initialized) {\n      return // Already initialized.\n    }\n\n    // If a bkp file exists, use it instead.\n    if (fileSystem.exists(journalFileBackup)) {\n      // If journal file also exists just delete backup file.\n      if (fileSystem.exists(journalFile)) {\n        fileSystem.delete(journalFileBackup)\n      } else {\n        fileSystem.atomicMove(journalFileBackup, journalFile)\n      }\n    }\n\n    civilizedFileSystem = fileSystem.isCivilized(journalFileBackup)\n\n    // Prefer to pick up where we left off.\n    if (fileSystem.exists(journalFile)) {\n      try {\n        readJournal()\n        processJournal()\n        initialized = true\n        return\n      } catch (journalIsCorrupt: IOException) {\n        Platform.get().log(\n          \"DiskLruCache $directory is corrupt: ${journalIsCorrupt.message}, removing\",\n          WARN,\n          journalIsCorrupt,\n        )\n      }\n\n      // The cache is corrupted, attempt to delete the contents of the directory. This can throw and\n      // we'll let that propagate out as it likely means there is a severe filesystem problem.\n      try {\n        delete()\n      } finally {\n        closed = false\n      }\n    }\n\n    rebuildJournal()\n\n    initialized = true\n  }\n\n  @Throws(IOException::class)\n  private fun readJournal() {\n    fileSystem.read(journalFile) {\n      val magic = readUtf8LineStrict()\n      val version = readUtf8LineStrict()\n      val appVersionString = readUtf8LineStrict()\n      val valueCountString = readUtf8LineStrict()\n      val blank = readUtf8LineStrict()\n\n      if (MAGIC != magic ||\n        VERSION_1 != version ||\n        appVersion.toString() != appVersionString ||\n        valueCount.toString() != valueCountString ||\n        blank.isNotEmpty()\n      ) {\n        throw IOException(\n          \"unexpected journal header: [$magic, $version, $valueCountString, $blank]\",\n        )\n      }\n\n      var lineCount = 0\n      while (true) {\n        try {\n          readJournalLine(readUtf8LineStrict())\n          lineCount++\n        } catch (_: EOFException) {\n          break // End of journal.\n        }\n      }\n\n      redundantOpCount = lineCount - lruEntries.size\n\n      // If we ended on a truncated line, rebuild the journal before appending to it.\n      if (!exhausted()) {\n        rebuildJournal()\n      } else {\n        journalWriter?.closeQuietly()\n        journalWriter = newJournalWriter()\n      }\n    }\n  }\n\n  @Throws(FileNotFoundException::class)\n  private fun newJournalWriter(): BufferedSink {\n    val fileSink = fileSystem.appendingSink(journalFile)\n    val faultHidingSink =\n      FaultHidingSink(fileSink) {\n        assertLockHeld()\n        hasJournalErrors = true\n      }\n    return faultHidingSink.buffer()\n  }\n\n  @Throws(IOException::class)\n  private fun readJournalLine(line: String) {\n    val firstSpace = line.indexOf(' ')\n    if (firstSpace == -1) throw IOException(\"unexpected journal line: $line\")\n\n    val keyBegin = firstSpace + 1\n    val secondSpace = line.indexOf(' ', keyBegin)\n    val key: String\n    if (secondSpace == -1) {\n      key = line.substring(keyBegin)\n      if (firstSpace == REMOVE.length && line.startsWith(REMOVE)) {\n        lruEntries.remove(key)\n        return\n      }\n    } else {\n      key = line.substring(keyBegin, secondSpace)\n    }\n\n    var entry: Entry? = lruEntries[key]\n    if (entry == null) {\n      entry = Entry(key)\n      lruEntries[key] = entry\n    }\n\n    when {\n      secondSpace != -1 && firstSpace == CLEAN.length && line.startsWith(CLEAN) -> {\n        val parts =\n          line\n            .substring(secondSpace + 1)\n            .split(' ')\n        entry.readable = true\n        entry.currentEditor = null\n        entry.setLengths(parts)\n      }\n\n      secondSpace == -1 && firstSpace == DIRTY.length && line.startsWith(DIRTY) -> {\n        entry.currentEditor = Editor(entry)\n      }\n\n      secondSpace == -1 && firstSpace == READ.length && line.startsWith(READ) -> {\n        // This work was already done by calling lruEntries.get().\n      }\n\n      else -> {\n        throw IOException(\"unexpected journal line: $line\")\n      }\n    }\n  }\n\n  /**\n   * Computes the initial size and collects garbage as a part of opening the cache. Dirty entries\n   * are assumed to be inconsistent and will be deleted.\n   */\n  @Throws(IOException::class)\n  private fun processJournal() {\n    fileSystem.deleteIfExists(journalFileTmp)\n    val i = lruEntries.values.iterator()\n    while (i.hasNext()) {\n      val entry = i.next()\n      if (entry.currentEditor == null) {\n        for (t in 0 until valueCount) {\n          size += entry.lengths[t]\n        }\n      } else {\n        entry.currentEditor = null\n        for (t in 0 until valueCount) {\n          fileSystem.deleteIfExists(entry.cleanFiles[t])\n          fileSystem.deleteIfExists(entry.dirtyFiles[t])\n        }\n        i.remove()\n      }\n    }\n  }\n\n  /**\n   * Creates a new journal that omits redundant information. This replaces the current journal if it\n   * exists.\n   */\n  @Synchronized\n  @Throws(IOException::class)\n  internal fun rebuildJournal() {\n    journalWriter?.close()\n\n    fileSystem.write(journalFileTmp) {\n      writeUtf8(MAGIC).writeByte('\\n'.code)\n      writeUtf8(VERSION_1).writeByte('\\n'.code)\n      writeDecimalLong(appVersion.toLong()).writeByte('\\n'.code)\n      writeDecimalLong(valueCount.toLong()).writeByte('\\n'.code)\n      writeByte('\\n'.code)\n\n      for (entry in lruEntries.values) {\n        if (entry.currentEditor != null) {\n          writeUtf8(DIRTY).writeByte(' '.code)\n          writeUtf8(entry.key)\n          writeByte('\\n'.code)\n        } else {\n          writeUtf8(CLEAN).writeByte(' '.code)\n          writeUtf8(entry.key)\n          entry.writeLengths(this)\n          writeByte('\\n'.code)\n        }\n      }\n    }\n\n    if (fileSystem.exists(journalFile)) {\n      fileSystem.atomicMove(journalFile, journalFileBackup)\n      fileSystem.atomicMove(journalFileTmp, journalFile)\n      fileSystem.deleteIfExists(journalFileBackup)\n    } else {\n      fileSystem.atomicMove(journalFileTmp, journalFile)\n    }\n\n    journalWriter?.closeQuietly()\n    journalWriter = newJournalWriter()\n    hasJournalErrors = false\n    mostRecentRebuildFailed = false\n  }\n\n  /**\n   * Returns a snapshot of the entry named [key], or null if it doesn't exist is not currently\n   * readable. If a value is returned, it is moved to the head of the LRU queue.\n   */\n  @Synchronized\n  @Throws(IOException::class)\n  operator fun get(key: String): Snapshot? {\n    initialize()\n\n    checkNotClosed()\n    validateKey(key)\n    val entry = lruEntries[key] ?: return null\n    val snapshot = entry.snapshot() ?: return null\n\n    redundantOpCount++\n    journalWriter!!\n      .writeUtf8(READ)\n      .writeByte(' '.code)\n      .writeUtf8(key)\n      .writeByte('\\n'.code)\n    if (journalRebuildRequired()) {\n      cleanupQueue.schedule(cleanupTask)\n    }\n\n    return snapshot\n  }\n\n  /** Returns an editor for the entry named [key], or null if another edit is in progress. */\n  @Synchronized\n  @Throws(IOException::class)\n  @JvmOverloads\n  fun edit(\n    key: String,\n    expectedSequenceNumber: Long = ANY_SEQUENCE_NUMBER,\n  ): Editor? {\n    initialize()\n\n    checkNotClosed()\n    validateKey(key)\n    var entry: Entry? = lruEntries[key]\n    if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER &&\n      (entry == null || entry.sequenceNumber != expectedSequenceNumber)\n    ) {\n      return null // Snapshot is stale.\n    }\n\n    if (entry?.currentEditor != null) {\n      return null // Another edit is in progress.\n    }\n\n    if (entry != null && entry.lockingSourceCount != 0) {\n      return null // We can't write this file because a reader is still reading it.\n    }\n\n    if (mostRecentTrimFailed || mostRecentRebuildFailed) {\n      // The OS has become our enemy! If the trim job failed, it means we are storing more data than\n      // requested by the user. Do not allow edits so we do not go over that limit any further. If\n      // the journal rebuild failed, the journal writer will not be active, meaning we will not be\n      // able to record the edit, causing file leaks. In both cases, we want to retry the clean up\n      // so we can get out of this state!\n      cleanupQueue.schedule(cleanupTask)\n      return null\n    }\n\n    // Flush the journal before creating files to prevent file leaks.\n    val journalWriter = this.journalWriter!!\n    journalWriter\n      .writeUtf8(DIRTY)\n      .writeByte(' '.code)\n      .writeUtf8(key)\n      .writeByte('\\n'.code)\n    journalWriter.flush()\n\n    if (hasJournalErrors) {\n      return null // Don't edit; the journal can't be written.\n    }\n\n    if (entry == null) {\n      entry = Entry(key)\n      lruEntries[key] = entry\n    }\n    val editor = Editor(entry)\n    entry.currentEditor = editor\n    return editor\n  }\n\n  /**\n   * Returns the number of bytes currently being used to store the values in this cache. This may be\n   * greater than the max size if a background deletion is pending.\n   */\n  @Synchronized\n  @Throws(IOException::class)\n  fun size(): Long {\n    initialize()\n    return size\n  }\n\n  @Synchronized\n  @Throws(IOException::class)\n  internal fun completeEdit(\n    editor: Editor,\n    success: Boolean,\n  ) {\n    val entry = editor.entry\n    check(entry.currentEditor == editor)\n\n    // If this edit is creating the entry for the first time, every index must have a value.\n    if (success && !entry.readable) {\n      for (i in 0 until valueCount) {\n        if (!editor.written!![i]) {\n          editor.abort()\n          throw IllegalStateException(\"Newly created entry didn't create value for index $i\")\n        }\n        if (!fileSystem.exists(entry.dirtyFiles[i])) {\n          editor.abort()\n          return\n        }\n      }\n    }\n\n    for (i in 0 until valueCount) {\n      val dirty = entry.dirtyFiles[i]\n      if (success && !entry.zombie) {\n        if (fileSystem.exists(dirty)) {\n          val clean = entry.cleanFiles[i]\n          fileSystem.atomicMove(dirty, clean)\n          val oldLength = entry.lengths[i]\n          // TODO check null behaviour\n          val newLength = fileSystem.metadata(clean).size ?: 0\n          entry.lengths[i] = newLength\n          size = size - oldLength + newLength\n        }\n      } else {\n        fileSystem.deleteIfExists(dirty)\n      }\n    }\n\n    entry.currentEditor = null\n    if (entry.zombie) {\n      removeEntry(entry)\n      return\n    }\n\n    redundantOpCount++\n    journalWriter!!.apply {\n      if (entry.readable || success) {\n        entry.readable = true\n        writeUtf8(CLEAN).writeByte(' '.code)\n        writeUtf8(entry.key)\n        entry.writeLengths(this)\n        writeByte('\\n'.code)\n        if (success) {\n          entry.sequenceNumber = nextSequenceNumber++\n        }\n      } else {\n        lruEntries.remove(entry.key)\n        writeUtf8(REMOVE).writeByte(' '.code)\n        writeUtf8(entry.key)\n        writeByte('\\n'.code)\n      }\n      flush()\n    }\n\n    if (size > maxSize || journalRebuildRequired()) {\n      cleanupQueue.schedule(cleanupTask)\n    }\n  }\n\n  /**\n   * We only rebuild the journal when it will halve the size of the journal and eliminate at least\n   * 2000 ops.\n   */\n  private fun journalRebuildRequired(): Boolean {\n    val redundantOpCompactThreshold = 2000\n    return redundantOpCount >= redundantOpCompactThreshold &&\n      redundantOpCount >= lruEntries.size\n  }\n\n  /**\n   * Drops the entry for [key] if it exists and can be removed. If the entry for [key] is currently\n   * being edited, that edit will complete normally but its value will not be stored.\n   *\n   * @return true if an entry was removed.\n   */\n  @Synchronized\n  @Throws(IOException::class)\n  fun remove(key: String): Boolean {\n    initialize()\n\n    checkNotClosed()\n    validateKey(key)\n    val entry = lruEntries[key] ?: return false\n    val removed = removeEntry(entry)\n    if (removed && size <= maxSize) mostRecentTrimFailed = false\n    return removed\n  }\n\n  @Throws(IOException::class)\n  internal fun removeEntry(entry: Entry): Boolean {\n    // If we can't delete files that are still open, mark this entry as a zombie so its files will\n    // be deleted when those files are closed.\n    if (!civilizedFileSystem) {\n      if (entry.lockingSourceCount > 0) {\n        // Mark this entry as 'DIRTY' so that if the process crashes this entry won't be used.\n        journalWriter?.let {\n          it.writeUtf8(DIRTY)\n          it.writeByte(' '.code)\n          it.writeUtf8(entry.key)\n          it.writeByte('\\n'.code)\n          it.flush()\n        }\n      }\n      if (entry.lockingSourceCount > 0 || entry.currentEditor != null) {\n        entry.zombie = true\n        return true\n      }\n    }\n\n    entry.currentEditor?.detach() // Prevent the edit from completing normally.\n\n    for (i in 0 until valueCount) {\n      fileSystem.deleteIfExists(entry.cleanFiles[i])\n      size -= entry.lengths[i]\n      entry.lengths[i] = 0\n    }\n\n    redundantOpCount++\n    journalWriter?.let {\n      it.writeUtf8(REMOVE)\n      it.writeByte(' '.code)\n      it.writeUtf8(entry.key)\n      it.writeByte('\\n'.code)\n    }\n    lruEntries.remove(entry.key)\n\n    if (journalRebuildRequired()) {\n      cleanupQueue.schedule(cleanupTask)\n    }\n\n    return true\n  }\n\n  @Synchronized private fun checkNotClosed() {\n    check(!closed) { \"cache is closed\" }\n  }\n\n  /** Force buffered operations to the filesystem. */\n  @Synchronized\n  @Throws(IOException::class)\n  override fun flush() {\n    if (!initialized) return\n\n    checkNotClosed()\n    trimToSize()\n    journalWriter!!.flush()\n  }\n\n  @Synchronized fun isClosed(): Boolean = closed\n\n  /** Closes this cache. Stored values will remain on the filesystem. */\n  @Synchronized\n  @Throws(IOException::class)\n  override fun close() {\n    if (!initialized || closed) {\n      closed = true\n      return\n    }\n\n    // Copying for concurrent iteration.\n    for (entry in lruEntries.values.toTypedArray()) {\n      if (entry.currentEditor != null) {\n        entry.currentEditor?.detach() // Prevent the edit from completing normally.\n      }\n    }\n\n    trimToSize()\n    journalWriter?.closeQuietly()\n    journalWriter = null\n    closed = true\n  }\n\n  @Throws(IOException::class)\n  fun trimToSize() {\n    while (size > maxSize) {\n      if (!removeOldestEntry()) return\n    }\n    mostRecentTrimFailed = false\n  }\n\n  /** Returns true if an entry was removed. This will return false if all entries are zombies. */\n  private fun removeOldestEntry(): Boolean {\n    for (toEvict in lruEntries.values) {\n      if (!toEvict.zombie) {\n        removeEntry(toEvict)\n        return true\n      }\n    }\n    return false\n  }\n\n  /**\n   * Closes the cache and deletes all of its stored values. This will delete all files in the cache\n   * directory including files that weren't created by the cache.\n   */\n  @Throws(IOException::class)\n  fun delete() {\n    close()\n    fileSystem.deleteContents(directory)\n  }\n\n  /**\n   * Deletes all stored values from the cache. In-flight edits will complete normally but their\n   * values will not be stored.\n   */\n  @Synchronized\n  @Throws(IOException::class)\n  fun evictAll() {\n    initialize()\n    // Copying for concurrent iteration.\n    for (entry in lruEntries.values.toTypedArray()) {\n      removeEntry(entry)\n    }\n    mostRecentTrimFailed = false\n  }\n\n  private fun validateKey(key: String) {\n    require(LEGAL_KEY_PATTERN.matches(key)) { \"keys must match regex [a-z0-9_-]{1,120}: \\\"$key\\\"\" }\n  }\n\n  /**\n   * Returns an iterator over the cache's current entries. This iterator doesn't throw\n   * `ConcurrentModificationException`, but if new entries are added while iterating, those new\n   * entries will not be returned by the iterator. If existing entries are removed during iteration,\n   * they will be absent (unless they were already returned).\n   *\n   * If there are I/O problems during iteration, this iterator fails silently. For example, if the\n   * hosting filesystem becomes unreachable, the iterator will omit elements rather than throwing\n   * exceptions.\n   *\n   * **The caller must [close][Snapshot.close]** each snapshot returned by [Iterator.next]. Failing\n   * to do so leaks open files!\n   */\n  @Synchronized\n  @Throws(IOException::class)\n  fun snapshots(): MutableIterator<Snapshot> {\n    initialize()\n    return object : MutableIterator<Snapshot> {\n      /** Iterate a copy of the entries to defend against concurrent modification errors. */\n      private val delegate = ArrayList(lruEntries.values).iterator()\n\n      /** The snapshot to return from [next]. Null if we haven't computed that yet. */\n      private var nextSnapshot: Snapshot? = null\n\n      /** The snapshot to remove with [remove]. Null if removal is illegal. */\n      private var removeSnapshot: Snapshot? = null\n\n      override fun hasNext(): Boolean {\n        if (nextSnapshot != null) return true\n\n        synchronized(this@DiskLruCache) {\n          // If the cache is closed, truncate the iterator.\n          if (closed) return false\n\n          while (delegate.hasNext()) {\n            nextSnapshot = delegate.next()?.snapshot() ?: continue\n            return true\n          }\n        }\n\n        return false\n      }\n\n      override fun next(): Snapshot {\n        if (!hasNext()) throw NoSuchElementException()\n        removeSnapshot = nextSnapshot\n        nextSnapshot = null\n        return removeSnapshot!!\n      }\n\n      override fun remove() {\n        val removeSnapshot = this.removeSnapshot\n        checkNotNull(removeSnapshot) { \"remove() before next()\" }\n        try {\n          this@DiskLruCache.remove(removeSnapshot.key())\n        } catch (_: IOException) {\n          // Nothing useful to do here. We failed to remove from the cache. Most likely that's\n          // because we couldn't update the journal, but the cached entry will still be gone.\n        } finally {\n          this.removeSnapshot = null\n        }\n      }\n    }\n  }\n\n  /** A snapshot of the values for an entry. */\n  inner class Snapshot internal constructor(\n    private val key: String,\n    private val sequenceNumber: Long,\n    private val sources: List<Source>,\n    private val lengths: LongArray,\n  ) : Closeable {\n    fun key(): String = key\n\n    /**\n     * Returns an editor for this snapshot's entry, or null if either the entry has changed since\n     * this snapshot was created or if another edit is in progress.\n     */\n    @Throws(IOException::class)\n    fun edit(): Editor? = this@DiskLruCache.edit(key, sequenceNumber)\n\n    /** Returns the unbuffered stream with the value for [index]. */\n    fun getSource(index: Int): Source = sources[index]\n\n    /** Returns the byte length of the value for [index]. */\n    fun getLength(index: Int): Long = lengths[index]\n\n    override fun close() {\n      for (source in sources) {\n        source.closeQuietly()\n      }\n    }\n  }\n\n  /** Edits the values for an entry. */\n  inner class Editor internal constructor(\n    internal val entry: Entry,\n  ) {\n    internal val written: BooleanArray? = if (entry.readable) null else BooleanArray(valueCount)\n    private var done: Boolean = false\n\n    /**\n     * Prevents this editor from completing normally. This is necessary either when the edit causes\n     * an I/O error, or if the target entry is evicted while this editor is active. In either case\n     * we delete the editor's created files and prevent new files from being created. Note that once\n     * an editor has been detached it is possible for another editor to edit the entry.\n     */\n    internal fun detach() {\n      if (entry.currentEditor == this) {\n        if (civilizedFileSystem) {\n          completeEdit(this, false) // Delete it now.\n        } else {\n          entry.zombie = true // We can't delete it until the current edit completes.\n        }\n      }\n    }\n\n    /**\n     * Returns an unbuffered input stream to read the last committed value, or null if no value has\n     * been committed.\n     */\n    fun newSource(index: Int): Source? {\n      synchronized(this@DiskLruCache) {\n        check(!done)\n        if (!entry.readable || entry.currentEditor != this || entry.zombie) {\n          return null\n        }\n        return try {\n          fileSystem.source(entry.cleanFiles[index])\n        } catch (_: FileNotFoundException) {\n          null\n        }\n      }\n    }\n\n    /**\n     * Returns a new unbuffered output stream to write the value at [index]. If the underlying\n     * output stream encounters errors when writing to the filesystem, this edit will be aborted\n     * when [commit] is called. The returned output stream does not throw IOExceptions.\n     */\n    fun newSink(index: Int): Sink {\n      synchronized(this@DiskLruCache) {\n        check(!done)\n        if (entry.currentEditor != this) {\n          return blackholeSink()\n        }\n        if (!entry.readable) {\n          written!![index] = true\n        }\n        val dirtyFile = entry.dirtyFiles[index]\n        val sink: Sink\n        try {\n          sink = fileSystem.sink(dirtyFile)\n        } catch (_: FileNotFoundException) {\n          return blackholeSink()\n        }\n        return FaultHidingSink(sink) {\n          synchronized(this@DiskLruCache) {\n            detach()\n          }\n        }\n      }\n    }\n\n    /**\n     * Commits this edit so it is visible to readers. This releases the edit lock so another edit\n     * may be started on the same key.\n     */\n    @Throws(IOException::class)\n    fun commit() {\n      synchronized(this@DiskLruCache) {\n        check(!done)\n        if (entry.currentEditor == this) {\n          completeEdit(this, true)\n        }\n        done = true\n      }\n    }\n\n    /**\n     * Aborts this edit. This releases the edit lock so another edit may be started on the same\n     * key.\n     */\n    @Throws(IOException::class)\n    fun abort() {\n      synchronized(this@DiskLruCache) {\n        check(!done)\n        if (entry.currentEditor == this) {\n          completeEdit(this, false)\n        }\n        done = true\n      }\n    }\n  }\n\n  internal inner class Entry internal constructor(\n    internal val key: String,\n  ) {\n    /** Lengths of this entry's files. */\n    internal val lengths: LongArray = LongArray(valueCount)\n    internal val cleanFiles = mutableListOf<Path>()\n    internal val dirtyFiles = mutableListOf<Path>()\n\n    /** True if this entry has ever been published. */\n    internal var readable: Boolean = false\n\n    /** True if this entry must be deleted when the current edit or read completes. */\n    internal var zombie: Boolean = false\n\n    /**\n     * The ongoing edit or null if this entry is not being edited. When setting this to null the\n     * entry must be removed if it is a zombie.\n     */\n    internal var currentEditor: Editor? = null\n\n    /**\n     * Sources currently reading this entry before a write or delete can proceed. When decrementing\n     * this to zero, the entry must be removed if it is a zombie.\n     */\n    internal var lockingSourceCount = 0\n\n    /** The sequence number of the most recently committed edit to this entry. */\n    internal var sequenceNumber: Long = 0\n\n    init {\n      // The names are repetitive so re-use the same builder to avoid allocations.\n      val fileBuilder = StringBuilder(key).append('.')\n      val truncateTo = fileBuilder.length\n      for (i in 0 until valueCount) {\n        fileBuilder.append(i)\n        cleanFiles += directory / fileBuilder.toString()\n        fileBuilder.append(\".tmp\")\n        dirtyFiles += directory / fileBuilder.toString()\n        fileBuilder.setLength(truncateTo)\n      }\n    }\n\n    /** Set lengths using decimal numbers like \"10123\". */\n    @Throws(IOException::class)\n    internal fun setLengths(strings: List<String>) {\n      if (strings.size != valueCount) {\n        invalidLengths(strings)\n      }\n\n      try {\n        for (i in strings.indices) {\n          lengths[i] = strings[i].toLong()\n        }\n      } catch (_: NumberFormatException) {\n        invalidLengths(strings)\n      }\n    }\n\n    /** Append space-prefixed lengths to [writer]. */\n    @Throws(IOException::class)\n    internal fun writeLengths(writer: BufferedSink) {\n      for (length in lengths) {\n        writer.writeByte(' '.code).writeDecimalLong(length)\n      }\n    }\n\n    @Throws(IOException::class)\n    private fun invalidLengths(strings: List<String>): Nothing = throw IOException(\"unexpected journal line: $strings\")\n\n    /**\n     * Returns a snapshot of this entry. This opens all streams eagerly to guarantee that we see a\n     * single published snapshot. If we opened streams lazily then the streams could come from\n     * different edits.\n     */\n    internal fun snapshot(): Snapshot? {\n      assertLockHeld()\n\n      if (!readable) return null\n      if (!civilizedFileSystem && (currentEditor != null || zombie)) return null\n\n      val sources = mutableListOf<Source>()\n      val lengths = this.lengths.clone() // Defensive copy since these can be zeroed out.\n      try {\n        for (i in 0 until valueCount) {\n          sources += newSource(i)\n        }\n        return Snapshot(key, sequenceNumber, sources, lengths)\n      } catch (_: FileNotFoundException) {\n        // A file must have been deleted manually!\n        for (source in sources) {\n          source.closeQuietly()\n        }\n        // Since the entry is no longer valid, remove it so the metadata is accurate (i.e. the cache\n        // size.)\n        try {\n          removeEntry(this)\n        } catch (_: IOException) {\n        }\n        return null\n      }\n    }\n\n    private fun newSource(index: Int): Source {\n      val fileSource = fileSystem.source(cleanFiles[index])\n      if (civilizedFileSystem) return fileSource\n\n      lockingSourceCount++\n      return object : ForwardingSource(fileSource) {\n        private var closed = false\n\n        override fun close() {\n          super.close()\n          if (!closed) {\n            closed = true\n            synchronized(this@DiskLruCache) {\n              lockingSourceCount--\n              if (lockingSourceCount == 0 && zombie) {\n                removeEntry(this@Entry)\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  companion object {\n    @JvmField val JOURNAL_FILE = \"journal\"\n\n    @JvmField val JOURNAL_FILE_TEMP = \"journal.tmp\"\n\n    @JvmField val JOURNAL_FILE_BACKUP = \"journal.bkp\"\n\n    @JvmField val MAGIC = \"libcore.io.DiskLruCache\"\n\n    @JvmField val VERSION_1 = \"1\"\n\n    @JvmField val ANY_SEQUENCE_NUMBER: Long = -1\n\n    @JvmField val LEGAL_KEY_PATTERN = \"[a-z0-9_-]{1,120}\".toRegex()\n\n    @JvmField val CLEAN = \"CLEAN\"\n\n    @JvmField val DIRTY = \"DIRTY\"\n\n    @JvmField val REMOVE = \"REMOVE\"\n\n    @JvmField val READ = \"READ\"\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/cache/FaultHidingSink.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.cache\n\nimport java.io.IOException\nimport okio.Buffer\nimport okio.ForwardingSink\nimport okio.Sink\n\n/** A sink that never throws IOExceptions, even if the underlying sink does. */\ninternal open class FaultHidingSink(\n  delegate: Sink,\n  val onException: (IOException) -> Unit,\n) : ForwardingSink(delegate) {\n  private var hasErrors = false\n\n  override fun write(\n    source: Buffer,\n    byteCount: Long,\n  ) {\n    if (hasErrors) {\n      source.skip(byteCount)\n      return\n    }\n    try {\n      super.write(source, byteCount)\n    } catch (e: IOException) {\n      hasErrors = true\n      onException(e)\n    }\n  }\n\n  override fun flush() {\n    if (hasErrors) {\n      return\n    }\n    try {\n      super.flush()\n    } catch (e: IOException) {\n      hasErrors = true\n      onException(e)\n    }\n  }\n\n  override fun close() {\n    try {\n      super.close()\n    } catch (e: IOException) {\n      hasErrors = true\n      onException(e)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/concurrent/Lockable.kt",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"NOTHING_TO_INLINE\")\n\npackage okhttp3.internal.concurrent\n\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.InvocationKind\nimport kotlin.contracts.contract\nimport okhttp3.internal.assertionsEnabled\n\n/**\n * Marker interface for objects that use the JVM's `synchronized` mechanism and the related\n * `wait()` and `notify()` functions.\n *\n * The Lockable interface is particularly handy because it ensures we lock the right `this` when\n * there are multiple `this` objects in scope.\n */\ninterface Lockable\n\ninternal inline fun Lockable.wait() = (this as Object).wait()\n\ninternal inline fun Lockable.notify() = (this as Object).notify()\n\ninternal inline fun Lockable.notifyAll() = (this as Object).notifyAll()\n\ninternal inline fun Lockable.awaitNanos(nanos: Long) {\n  val ms = nanos / 1_000_000L\n  val ns = nanos - (ms * 1_000_000L)\n  if (ms > 0L || nanos > 0) {\n    (this as Object).wait(ms, ns.toInt())\n  }\n}\n\ninternal inline fun Lockable.assertLockNotHeld() {\n  if (assertionsEnabled && Thread.holdsLock(this)) {\n    throw AssertionError(\"Thread ${Thread.currentThread().name} MUST NOT hold lock on $this\")\n  }\n}\n\ninternal inline fun Lockable.assertLockHeld() {\n  if (assertionsEnabled && !Thread.holdsLock(this)) {\n    throw AssertionError(\"Thread ${Thread.currentThread().name} MUST hold lock on $this\")\n  }\n}\n\n@OptIn(ExperimentalContracts::class)\ninline fun <T> Lockable.withLock(action: () -> T): T {\n  contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }\n  return synchronized(this, action)\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/concurrent/Task.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.concurrent\n\n/**\n * A unit of work that can be executed one or more times.\n *\n * Recurrence\n * ----------\n *\n * Tasks control their recurrence schedule. The [runOnce] function returns -1L to signify that the\n * task should not be executed again. Otherwise it returns a delay until the next execution.\n *\n * A task has at most one next execution. If the same task instance is scheduled multiple times, the\n * earliest one wins. This applies to both executions scheduled with [TaskRunner.Queue.schedule] and\n * those implied by the returned execution delay.\n *\n * Cancellation\n * ------------\n *\n * Tasks may be canceled while they are waiting to be executed, or while they are executing.\n *\n * Canceling a task that is waiting to execute prevents that upcoming execution. Canceling a task\n * that is currently executing does not impact the ongoing run, but it does prevent a recurrence\n * from being scheduled.\n *\n * Tasks may opt-out of cancellation with `cancelable = false`. Such tasks will recur until they\n * decide not to by returning -1L.\n *\n * Task Queues\n * -----------\n *\n * Tasks are bound to the [TaskQueue] they are scheduled in. Each queue is sequential and the tasks\n * within it never execute concurrently. It is an error to use a task in multiple queues.\n */\nabstract class Task(\n  val name: String,\n  val cancelable: Boolean = true,\n) {\n  // Guarded by the TaskRunner.\n  internal var queue: TaskQueue? = null\n\n  /** Undefined unless this is in [TaskQueue.futureTasks]. */\n  internal var nextExecuteNanoTime = -1L\n\n  /** Returns the delay in nanoseconds until the next execution, or -1L to not reschedule. */\n  abstract fun runOnce(): Long\n\n  internal fun initQueue(queue: TaskQueue) {\n    if (this.queue === queue) return\n\n    check(this.queue === null) { \"task is in multiple queues\" }\n    this.queue = queue\n  }\n\n  override fun toString(): String = name\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/concurrent/TaskLogger.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.concurrent\n\nimport java.util.logging.Level\nimport java.util.logging.Logger\n\ninternal inline fun Logger.taskLog(\n  task: Task,\n  queue: TaskQueue,\n  messageBlock: () -> String,\n) {\n  if (isLoggable(Level.FINE)) {\n    log(task, queue, messageBlock())\n  }\n}\n\ninternal inline fun <T> Logger.logElapsed(\n  task: Task,\n  queue: TaskQueue,\n  block: () -> T,\n): T {\n  var startNs = -1L\n  val loggingEnabled = isLoggable(Level.FINE)\n  if (loggingEnabled) {\n    startNs = queue.taskRunner.backend.nanoTime()\n    log(task, queue, \"starting\")\n  }\n\n  var completedNormally = false\n  try {\n    val result = block()\n    completedNormally = true\n    return result\n  } finally {\n    if (loggingEnabled) {\n      val elapsedNs = queue.taskRunner.backend.nanoTime() - startNs\n      if (completedNormally) {\n        log(task, queue, \"finished run in ${formatDuration(elapsedNs)}\")\n      } else {\n        log(task, queue, \"failed a run in ${formatDuration(elapsedNs)}\")\n      }\n    }\n  }\n}\n\nprivate fun Logger.log(\n  task: Task,\n  queue: TaskQueue,\n  message: String,\n) {\n  fine(\"${queue.name} ${String.format(\"%-22s\", message)}: ${task.name}\")\n}\n\n/**\n * Returns a duration in the nearest whole-number units like \"999 µs\" or \"  1 s \". This rounds 0.5\n * units away from 0 and 0.499 towards 0. The smallest unit this returns is \"µs\"; the largest unit\n * it returns is \"s\". For values in [-499..499] this returns \"  0 µs\".\n *\n * The returned string attempts to be column-aligned to 6 characters. For negative and large values\n * the returned string may be longer.\n */\nfun formatDuration(ns: Long): String {\n  val s =\n    when {\n      ns <= -999_500_000 -> \"${(ns - 500_000_000) / 1_000_000_000} s \"\n      ns <= -999_500 -> \"${(ns - 500_000) / 1_000_000} ms\"\n      ns <= 0 -> \"${(ns - 500) / 1_000} µs\"\n      ns < 999_500 -> \"${(ns + 500) / 1_000} µs\"\n      ns < 999_500_000 -> \"${(ns + 500_000) / 1_000_000} ms\"\n      else -> \"${(ns + 500_000_000) / 1_000_000_000} s \"\n    }\n  return String.format(\"%6s\", s)\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/concurrent/TaskQueue.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.concurrent\n\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.RejectedExecutionException\nimport okhttp3.internal.okHttpName\n\n/**\n * A set of tasks that are executed in sequential order.\n *\n * Work within queues is not concurrent. This is equivalent to each queue having a dedicated thread\n * for its work; in practice a set of queues may share a set of threads to save resources.\n */\nclass TaskQueue internal constructor(\n  internal val taskRunner: TaskRunner,\n  internal val name: String,\n) {\n  internal var shutdown = false\n\n  /** This queue's currently-executing task, or null if none is currently executing. */\n  internal var activeTask: Task? = null\n\n  /** Scheduled tasks ordered by [Task.nextExecuteNanoTime]. */\n  internal val futureTasks = mutableListOf<Task>()\n\n  /** True if the [activeTask] should be canceled when it completes. */\n  internal var cancelActiveTask = false\n\n  /**\n   * Returns a snapshot of tasks currently scheduled for execution. Does not include the\n   * currently-executing task unless it is also scheduled for future execution.\n   */\n  val scheduledTasks: List<Task>\n    get() = taskRunner.withLock { futureTasks.toList() }\n\n  /**\n   * Schedules [task] for execution in [delayNanos]. A task may only have one future execution\n   * scheduled. If the task is already in the queue, the earliest execution time is used.\n   *\n   * The target execution time is implemented on a best-effort basis. If another task in this queue\n   * is running when that time is reached, that task is allowed to complete before this task is\n   * started. Similarly the task will be delayed if the host lacks compute resources.\n   *\n   * @throws RejectedExecutionException if the queue is shut down and the task is not cancelable.\n   */\n  fun schedule(\n    task: Task,\n    delayNanos: Long = 0L,\n  ) {\n    taskRunner.withLock {\n      if (shutdown) {\n        if (task.cancelable) {\n          taskRunner.logger.taskLog(task, this) { \"schedule canceled (queue is shutdown)\" }\n          return\n        }\n        taskRunner.logger.taskLog(task, this) { \"schedule failed (queue is shutdown)\" }\n        throw RejectedExecutionException()\n      }\n\n      if (scheduleAndDecide(task, delayNanos, recurrence = false)) {\n        taskRunner.kickCoordinator(this)\n      }\n    }\n  }\n\n  /**\n   * Overload of [schedule] that uses a lambda for a repeating task.\n   *\n   * TODO: make this inline once this is fixed: https://github.com/oracle/graal/issues/3466\n   */\n  fun schedule(\n    name: String,\n    delayNanos: Long = 0L,\n    block: () -> Long,\n  ) {\n    schedule(\n      object : Task(name) {\n        override fun runOnce(): Long = block()\n      },\n      delayNanos,\n    )\n  }\n\n  /**\n   * Executes [block] once on a task runner thread.\n   *\n   * TODO: make this inline once this is fixed: https://github.com/oracle/graal/issues/3466\n   */\n  fun execute(\n    name: String,\n    delayNanos: Long = 0L,\n    cancelable: Boolean = true,\n    block: () -> Unit,\n  ) {\n    schedule(\n      object : Task(name, cancelable) {\n        override fun runOnce(): Long {\n          block()\n          return -1L\n        }\n      },\n      delayNanos,\n    )\n  }\n\n  /** Returns a latch that reaches 0 when the queue is next idle. */\n  fun idleLatch(): CountDownLatch {\n    taskRunner.withLock {\n      // If the queue is already idle, that's easy.\n      if (activeTask == null && futureTasks.isEmpty()) {\n        return CountDownLatch(0)\n      }\n\n      // If there's an existing AwaitIdleTask, use it. This is necessary when the executor is\n      // shutdown but still busy as we can't enqueue in that case.\n      val existingTask = activeTask\n      if (existingTask is AwaitIdleTask) {\n        return existingTask.latch\n      }\n      for (futureTask in futureTasks) {\n        if (futureTask is AwaitIdleTask) {\n          return futureTask.latch\n        }\n      }\n\n      // Don't delegate to schedule() because that enforces shutdown rules.\n      val newTask = AwaitIdleTask()\n      if (scheduleAndDecide(newTask, 0L, recurrence = false)) {\n        taskRunner.kickCoordinator(this)\n      }\n      return newTask.latch\n    }\n  }\n\n  private class AwaitIdleTask : Task(\"$okHttpName awaitIdle\", cancelable = false) {\n    val latch = CountDownLatch(1)\n\n    override fun runOnce(): Long {\n      latch.countDown()\n      return -1L\n    }\n  }\n\n  /** Adds [task] to run in [delayNanos]. Returns true if the coordinator is impacted. */\n  internal fun scheduleAndDecide(\n    task: Task,\n    delayNanos: Long,\n    recurrence: Boolean,\n  ): Boolean {\n    task.initQueue(this)\n\n    val now = taskRunner.backend.nanoTime()\n    val executeNanoTime = now + delayNanos\n\n    // If the task is already scheduled, take the earlier of the two times.\n    val existingIndex = futureTasks.indexOf(task)\n    if (existingIndex != -1) {\n      if (task.nextExecuteNanoTime <= executeNanoTime) {\n        taskRunner.logger.taskLog(task, this) { \"already scheduled\" }\n        return false\n      }\n      futureTasks.removeAt(existingIndex) // Already scheduled later: reschedule below!\n    }\n    task.nextExecuteNanoTime = executeNanoTime\n    taskRunner.logger.taskLog(task, this) {\n      if (recurrence) {\n        \"run again after ${formatDuration(executeNanoTime - now)}\"\n      } else {\n        \"scheduled after ${formatDuration(executeNanoTime - now)}\"\n      }\n    }\n\n    // Insert in chronological order. Always compare deltas because nanoTime() is permitted to wrap.\n    var insertAt = futureTasks.indexOfFirst { it.nextExecuteNanoTime - now > delayNanos }\n    if (insertAt == -1) insertAt = futureTasks.size\n    futureTasks.add(insertAt, task)\n\n    // Impact the coordinator if we inserted at the front.\n    return insertAt == 0\n  }\n\n  /**\n   * Schedules immediate execution of [Task.tryCancel] on all currently-enqueued tasks. These calls\n   * will not be made until any currently-executing task has completed. Tasks that return true will\n   * be removed from the execution schedule.\n   */\n  fun cancelAll() {\n    taskRunner.assertLockNotHeld()\n\n    taskRunner.withLock {\n      if (cancelAllAndDecide()) {\n        taskRunner.kickCoordinator(this)\n      }\n    }\n  }\n\n  fun shutdown() {\n    taskRunner.assertLockNotHeld()\n\n    taskRunner.withLock {\n      shutdown = true\n      if (cancelAllAndDecide()) {\n        taskRunner.kickCoordinator(this)\n      }\n    }\n  }\n\n  /** Returns true if the coordinator is impacted. */\n  internal fun cancelAllAndDecide(): Boolean {\n    if (activeTask != null && activeTask!!.cancelable) {\n      cancelActiveTask = true\n    }\n\n    var tasksCanceled = false\n    for (i in futureTasks.size - 1 downTo 0) {\n      if (futureTasks[i].cancelable) {\n        taskRunner.logger.taskLog(futureTasks[i], this) { \"canceled\" }\n        tasksCanceled = true\n        futureTasks.removeAt(i)\n      }\n    }\n    return tasksCanceled\n  }\n\n  override fun toString(): String = name\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/concurrent/TaskRunner.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.concurrent\n\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.SynchronousQueue\nimport java.util.concurrent.ThreadFactory\nimport java.util.concurrent.ThreadPoolExecutor\nimport java.util.concurrent.TimeUnit\nimport java.util.logging.Logger\nimport okhttp3.internal.addIfAbsent\nimport okhttp3.internal.concurrent.TaskRunner.Companion.INSTANCE\nimport okhttp3.internal.okHttpName\nimport okhttp3.internal.threadFactory\n\n/**\n * A set of worker threads that are shared among a set of task queues.\n *\n * Use [INSTANCE] for a task runner that uses daemon threads. There is not currently a shared\n * instance for non-daemon threads.\n *\n * The task runner is also responsible for releasing held threads when the library is unloaded.\n * This is for the benefit of container environments that implement code unloading.\n *\n * Most applications should share a process-wide [TaskRunner] and use queues for per-client work.\n */\nclass TaskRunner(\n  val backend: Backend,\n  internal val logger: Logger = TaskRunner.logger,\n) : Lockable {\n  private var nextQueueName = 10000\n  private var coordinatorWaiting = false\n  private var coordinatorWakeUpAt = 0L\n\n  /**\n   * When we need a new thread to run tasks, we call [Backend.execute]. A few microseconds later we\n   * expect a newly-started thread to call [Runnable.run]. We shouldn't request new threads until\n   * the already-requested ones are in service, otherwise we might create more threads than we need.\n   *\n   * We use [executeCallCount] and [runCallCount] to defend against starting more threads than we\n   * need. Both fields are guarded by `this`.\n   */\n  private var executeCallCount = 0\n  private var runCallCount = 0\n\n  /** Queues with tasks that are currently executing their [TaskQueue.activeTask]. */\n  private val busyQueues = mutableListOf<TaskQueue>()\n\n  /** Queues not in [busyQueues] that have non-empty [TaskQueue.futureTasks]. */\n  private val readyQueues = mutableListOf<TaskQueue>()\n\n  private val runnable: Runnable =\n    object : Runnable {\n      override fun run() {\n        var task: Task =\n          withLock {\n            runCallCount++\n            awaitTaskToRun()\n          } ?: return\n\n        val currentThread = Thread.currentThread()\n        val oldName = currentThread.name\n        try {\n          while (true) {\n            currentThread.name = task.name\n            val delayNanos =\n              logger.logElapsed(task, task.queue!!) {\n                task.runOnce()\n              }\n\n            // A task ran successfully. Update the execution state and take the next task.\n            task = withLock {\n              afterRun(task, delayNanos, true)\n              awaitTaskToRun()\n            } ?: return\n          }\n        } catch (thrown: Throwable) {\n          // A task failed. Update execution state and re-throw the exception.\n          withLock {\n            afterRun(task, -1L, false)\n          }\n          if (thrown is InterruptedException) {\n            Thread.currentThread().interrupt()\n          } else {\n            throw thrown\n          }\n        } finally {\n          currentThread.name = oldName\n        }\n      }\n    }\n\n  internal fun kickCoordinator(taskQueue: TaskQueue) {\n    assertLockHeld()\n\n    if (taskQueue.activeTask == null) {\n      if (taskQueue.futureTasks.isNotEmpty()) {\n        readyQueues.addIfAbsent(taskQueue)\n      } else {\n        readyQueues.remove(taskQueue)\n      }\n    }\n\n    if (coordinatorWaiting) {\n      backend.coordinatorNotify(this@TaskRunner)\n    } else {\n      startAnotherThread()\n    }\n  }\n\n  private fun beforeRun(task: Task) {\n    assertLockHeld()\n\n    task.nextExecuteNanoTime = -1L\n    val queue = task.queue!!\n    queue.futureTasks.remove(task)\n    readyQueues.remove(queue)\n    queue.activeTask = task\n    busyQueues.add(queue)\n  }\n\n  private fun afterRun(\n    task: Task,\n    delayNanos: Long,\n    completedNormally: Boolean,\n  ) {\n    assertLockHeld()\n\n    val queue = task.queue!!\n    check(queue.activeTask === task)\n\n    val cancelActiveTask = queue.cancelActiveTask\n    queue.cancelActiveTask = false\n    queue.activeTask = null\n    busyQueues.remove(queue)\n\n    if (delayNanos != -1L && !cancelActiveTask && !queue.shutdown) {\n      queue.scheduleAndDecide(task, delayNanos, recurrence = true)\n    }\n\n    if (queue.futureTasks.isNotEmpty()) {\n      readyQueues.add(queue)\n\n      // If the task crashed, start another thread to run the next task.\n      if (!completedNormally) {\n        startAnotherThread()\n      }\n    }\n  }\n\n  /**\n   * Returns an immediately-executable task for the calling thread to execute, sleeping as necessary\n   * until one is ready. If there are no ready queues, or if other threads have everything under\n   * control this will return null. If there is more than a single task ready to execute immediately\n   * this will start another thread to handle that work.\n   */\n  fun awaitTaskToRun(): Task? {\n    assertLockHeld()\n\n    while (true) {\n      if (readyQueues.isEmpty()) {\n        return null // Nothing to do.\n      }\n\n      val now = backend.nanoTime()\n      var minDelayNanos = Long.MAX_VALUE\n      var readyTask: Task? = null\n      var multipleReadyTasks = false\n\n      // Decide what to run. This loop's goal wants to:\n      //  * Find out what this thread should do (either run a task or sleep)\n      //  * Find out if there's enough work to start another thread.\n      eachQueue@ for (queue in readyQueues) {\n        val candidate = queue.futureTasks[0]\n        val candidateDelay = maxOf(0L, candidate.nextExecuteNanoTime - now)\n\n        when {\n          // Compute the delay of the soonest-executable task.\n          candidateDelay > 0L -> {\n            minDelayNanos = minOf(candidateDelay, minDelayNanos)\n            continue@eachQueue\n          }\n\n          // If we already have more than one task, that's enough work for now. Stop searching.\n          readyTask != null -> {\n            multipleReadyTasks = true\n            break@eachQueue\n          }\n\n          // We have a task to execute when we complete the loop.\n          else -> {\n            readyTask = candidate\n          }\n        }\n      }\n\n      // Implement the decision.\n      when {\n        // We have a task ready to go. Get ready.\n        readyTask != null -> {\n          beforeRun(readyTask)\n\n          // Also start another thread if there's more work or scheduling to do.\n          if (multipleReadyTasks || !coordinatorWaiting && readyQueues.isNotEmpty()) {\n            startAnotherThread()\n          }\n\n          return readyTask\n        }\n\n        // Notify the coordinator of a task that's coming up soon.\n        coordinatorWaiting -> {\n          if (minDelayNanos < coordinatorWakeUpAt - now) {\n            backend.coordinatorNotify(this@TaskRunner)\n          }\n          return null\n        }\n\n        // No other thread is coordinating. Become the coordinator!\n        else -> {\n          coordinatorWaiting = true\n          coordinatorWakeUpAt = now + minDelayNanos\n          try {\n            backend.coordinatorWait(this@TaskRunner, minDelayNanos)\n          } catch (_: InterruptedException) {\n            // Will cause all tasks to exit unless more are scheduled!\n            cancelAll()\n          } finally {\n            coordinatorWaiting = false\n          }\n        }\n      }\n    }\n  }\n\n  /** Start another thread, unless a new thread is already scheduled to start. */\n  private fun startAnotherThread() {\n    assertLockHeld()\n    if (executeCallCount > runCallCount) return // A thread is still starting.\n\n    executeCallCount++\n    backend.execute(this@TaskRunner, runnable)\n  }\n\n  fun newQueue(): TaskQueue {\n    val name = this.withLock { nextQueueName++ }\n    return TaskQueue(this, \"Q$name\")\n  }\n\n  /**\n   * Returns a snapshot of queues that currently have tasks scheduled. The task runner does not\n   * necessarily track queues that have no tasks scheduled.\n   */\n  fun activeQueues(): List<TaskQueue> {\n    this.withLock {\n      return busyQueues + readyQueues\n    }\n  }\n\n  fun cancelAll() {\n    assertLockHeld()\n    for (i in busyQueues.size - 1 downTo 0) {\n      busyQueues[i].cancelAllAndDecide()\n    }\n    for (i in readyQueues.size - 1 downTo 0) {\n      val queue = readyQueues[i]\n      queue.cancelAllAndDecide()\n      if (queue.futureTasks.isEmpty()) {\n        readyQueues.removeAt(i)\n      }\n    }\n  }\n\n  interface Backend {\n    fun nanoTime(): Long\n\n    fun coordinatorNotify(taskRunner: TaskRunner)\n\n    fun coordinatorWait(\n      taskRunner: TaskRunner,\n      nanos: Long,\n    )\n\n    fun <T> decorate(queue: BlockingQueue<T>): BlockingQueue<T>\n\n    fun execute(\n      taskRunner: TaskRunner,\n      runnable: Runnable,\n    )\n  }\n\n  class RealBackend(\n    threadFactory: ThreadFactory,\n  ) : Backend {\n    val executor =\n      ThreadPoolExecutor(\n        // corePoolSize:\n        0,\n        // maximumPoolSize:\n        Int.MAX_VALUE,\n        // keepAliveTime:\n        60L,\n        TimeUnit.SECONDS,\n        SynchronousQueue(),\n        threadFactory,\n      )\n\n    override fun nanoTime() = System.nanoTime()\n\n    override fun coordinatorNotify(taskRunner: TaskRunner) {\n      taskRunner.notify()\n    }\n\n    /**\n     * Wait a duration in nanoseconds. Unlike [java.lang.Object.wait] this interprets 0 as\n     * \"don't wait\" instead of \"wait forever\".\n     */\n    @Throws(InterruptedException::class)\n    @Suppress(\"PLATFORM_CLASS_MAPPED_TO_KOTLIN\")\n    override fun coordinatorWait(\n      taskRunner: TaskRunner,\n      nanos: Long,\n    ) {\n      taskRunner.assertLockHeld()\n      if (nanos > 0) {\n        taskRunner.awaitNanos(nanos)\n      }\n    }\n\n    override fun <T> decorate(queue: BlockingQueue<T>) = queue\n\n    override fun execute(\n      taskRunner: TaskRunner,\n      runnable: Runnable,\n    ) {\n      executor.execute(runnable)\n    }\n\n    fun shutdown() {\n      executor.shutdown()\n    }\n  }\n\n  companion object {\n    val logger: Logger = Logger.getLogger(TaskRunner::class.java.name)\n\n    @JvmField\n    val INSTANCE = TaskRunner(RealBackend(threadFactory(\"$okHttpName TaskRunner\", daemon = true)))\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/AddressPolicy.kt",
    "content": "/*\n * Copyright (C) 2024 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\n/**\n * A policy for how the pool should treat a specific address.\n */\nclass AddressPolicy(\n  /**\n   * How many concurrent calls should be possible to make at any time.\n   * The pool will routinely try to pre-emptively open connections to satisfy this minimum.\n   * Connections will still be closed if they idle beyond the keep-alive but will be replaced.\n   */\n  @JvmField val minimumConcurrentCalls: Int = 0,\n  /** How long to wait to retry pre-emptive connection attempts that fail. */\n  @JvmField val backoffDelayMillis: Long = 60 * 1000,\n  /** How much jitter to introduce in connection retry backoff delays */\n  @JvmField val backoffJitterMillis: Int = 100,\n)\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/BufferedSocket.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.net.Socket as JavaNetSocket\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.Socket as OkioSocket\nimport okio.asOkioSocket\nimport okio.buffer\n\ninterface BufferedSocket : OkioSocket {\n  override val source: BufferedSource\n  override val sink: BufferedSink\n}\n\nfun JavaNetSocket.asBufferedSocket(): BufferedSocket = asOkioSocket().asBufferedSocket()\n\nfun OkioSocket.asBufferedSocket(): BufferedSocket =\n  object : BufferedSocket {\n    private val delegate = this@asBufferedSocket\n    override val source = delegate.source.buffer()\n    override val sink = delegate.sink.buffer()\n\n    override fun cancel() {\n      delegate.cancel()\n    }\n  }\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/ConnectInterceptor.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport okhttp3.Interceptor\nimport okhttp3.Response\nimport okhttp3.internal.http.RealInterceptorChain\n\n/**\n * Opens a connection to the target server and proceeds to the next interceptor. The network might\n * be used for the returned response, or to validate a cached response with a conditional GET.\n */\nobject ConnectInterceptor : Interceptor {\n  @Throws(IOException::class)\n  override fun intercept(chain: Interceptor.Chain): Response {\n    val realChain = chain as RealInterceptorChain\n    val exchange = realChain.call.initExchange(realChain)\n    val connectedChain = realChain.copy(exchange = exchange)\n    return connectedChain.proceed(realChain.request)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/ConnectPlan.kt",
    "content": "/*\n * Copyright (C) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport java.net.ConnectException\nimport java.net.HttpURLConnection\nimport java.net.ProtocolException\nimport java.net.Proxy\nimport java.net.Socket as JavaNetSocket\nimport java.net.UnknownServiceException\nimport java.security.cert.X509Certificate\nimport java.util.concurrent.TimeUnit\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSocket\nimport okhttp3.CertificatePinner\nimport okhttp3.ConnectionSpec\nimport okhttp3.Handshake\nimport okhttp3.Handshake.Companion.handshake\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Route\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.connection.RoutePlanner.ConnectResult\nimport okhttp3.internal.http.ExchangeCodec\nimport okhttp3.internal.http1.Http1ExchangeCodec\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.tls.OkHostnameVerifier\nimport okhttp3.internal.toHostHeader\n\n/**\n * A single attempt to connect to a remote server, including these steps:\n *\n *  * [TCP handshake][connectSocket]\n *  * Optional [CONNECT tunnels][connectTunnel]. When using an HTTP proxy to reach an HTTPS server\n *    we must send a `CONNECT` request, and handle authorization challenges from the proxy.\n *  * Optional [TLS handshake][connectTls].\n *\n * Each step may fail. If a retry is possible, a new instance is created with the next plan, which\n * will be configured differently.\n */\nclass ConnectPlan internal constructor(\n  private val taskRunner: TaskRunner,\n  private val connectionPool: RealConnectionPool,\n  private val readTimeoutMillis: Int,\n  private val writeTimeoutMillis: Int,\n  private val socketConnectTimeoutMillis: Int,\n  private val socketReadTimeoutMillis: Int,\n  private val pingIntervalMillis: Int,\n  private val retryOnConnectionFailure: Boolean,\n  private val call: RealCall,\n  private val routePlanner: RealRoutePlanner,\n  // Specifics to this plan.\n  override val route: Route,\n  internal val routes: List<Route>?,\n  private val attempt: Int,\n  private val tunnelRequest: Request?,\n  internal val connectionSpecIndex: Int,\n  internal val isTlsFallback: Boolean,\n) : RoutePlanner.Plan,\n  ExchangeCodec.Carrier {\n  /** True if this connect was canceled; typically because it lost a race. */\n  @Volatile private var canceled = false\n\n  // These properties are initialized by connect() and never reassigned.\n\n  /** The low-level TCP socket. */\n  private var rawSocket: JavaNetSocket? = null\n\n  /**\n   * The application layer socket. Either an [SSLSocket] layered over [rawSocket], or [rawSocket]\n   * itself if this connection does not use SSL.\n   */\n  internal var javaNetSocket: JavaNetSocket? = null\n  private var handshake: Handshake? = null\n  private var protocol: Protocol? = null\n  private lateinit var socket: BufferedSocket\n  private var connection: RealConnection? = null\n\n  /** True if this connection is ready for use, including TCP, tunnels, and TLS. */\n  override val isReady: Boolean\n    get() = protocol != null\n\n  private fun copy(\n    attempt: Int = this.attempt,\n    tunnelRequest: Request? = this.tunnelRequest,\n    connectionSpecIndex: Int = this.connectionSpecIndex,\n    isTlsFallback: Boolean = this.isTlsFallback,\n  ): ConnectPlan =\n    ConnectPlan(\n      taskRunner = taskRunner,\n      connectionPool = connectionPool,\n      readTimeoutMillis = readTimeoutMillis,\n      writeTimeoutMillis = writeTimeoutMillis,\n      socketConnectTimeoutMillis = socketConnectTimeoutMillis,\n      socketReadTimeoutMillis = socketReadTimeoutMillis,\n      pingIntervalMillis = pingIntervalMillis,\n      retryOnConnectionFailure = retryOnConnectionFailure,\n      call = call,\n      routePlanner = routePlanner,\n      route = route,\n      routes = routes,\n      attempt = attempt,\n      tunnelRequest = tunnelRequest,\n      connectionSpecIndex = connectionSpecIndex,\n      isTlsFallback = isTlsFallback,\n    )\n\n  override fun connectTcp(): ConnectResult {\n    check(rawSocket == null) { \"TCP already connected\" }\n\n    var success = false\n\n    // Tell the call about the connecting call so async cancels work.\n    call.plansToCancel += this\n    try {\n      call.eventListener.connectStart(call, route.socketAddress, route.proxy)\n      connectionPool.connectionListener.connectStart(route, call)\n\n      connectSocket()\n      success = true\n      return ConnectResult(plan = this)\n    } catch (e: IOException) {\n      // If we used the ProxySelector, and got a IOException during connect, report the failure.\n      if (route.address.proxy == null && route.proxy.type() != Proxy.Type.DIRECT) {\n        route.address.proxySelector.connectFailed(\n          route.address.url.toUri(),\n          route.proxy.address(),\n          e,\n        )\n      }\n      call.eventListener.connectFailed(call, route.socketAddress, route.proxy, null, e)\n      connectionPool.connectionListener.connectFailed(route, call, e)\n      return ConnectResult(plan = this, throwable = e)\n    } finally {\n      call.plansToCancel -= this\n      if (!success) {\n        rawSocket?.closeQuietly()\n      }\n    }\n  }\n\n  override fun connectTlsEtc(): ConnectResult {\n    val rawSocket = requireNotNull(rawSocket) { \"TCP not connected\" }\n    check(!isReady) { \"already connected\" }\n\n    val connectionSpecs = route.address.connectionSpecs\n    var retryTlsConnection: ConnectPlan? = null\n    var success = false\n\n    // Tell the call about the connecting call so async cancels work.\n    call.plansToCancel += this\n    try {\n      if (tunnelRequest != null) {\n        val tunnelResult = connectTunnel()\n\n        // Tunnel didn't work. Start it all again.\n        if (tunnelResult.nextPlan != null || tunnelResult.throwable != null) {\n          return tunnelResult\n        }\n      }\n\n      if (route.address.sslSocketFactory != null) {\n        // Assume the server won't send a TLS ServerHello until we send a TLS ClientHello. If\n        // that happens, then we will have buffered bytes that are needed by the SSLSocket!\n        // This check is imperfect: it doesn't tell us whether a handshake will succeed, just\n        // that it will almost certainly fail because the proxy has sent unexpected data.\n        if (!socket.source.buffer.exhausted() || !socket.sink.buffer.exhausted()) {\n          throw IOException(\"TLS tunnel buffered too many bytes!\")\n        }\n\n        call.eventListener.secureConnectStart(call)\n\n        // Create the wrapper over the connected socket.\n        val sslSocket =\n          route.address.sslSocketFactory.createSocket(\n            rawSocket,\n            route.address.url.host,\n            route.address.url.port,\n            // autoClose:\n            true,\n          ) as SSLSocket\n\n        val tlsEquipPlan = planWithCurrentOrInitialConnectionSpec(connectionSpecs, sslSocket)\n        val connectionSpec = connectionSpecs[tlsEquipPlan.connectionSpecIndex]\n\n        // Figure out the next connection spec in case we need a retry.\n        retryTlsConnection = tlsEquipPlan.nextConnectionSpec(connectionSpecs, sslSocket)\n\n        connectionSpec.apply(sslSocket, isFallback = tlsEquipPlan.isTlsFallback)\n        connectTls(sslSocket, connectionSpec)\n        call.eventListener.secureConnectEnd(call, handshake)\n      } else {\n        javaNetSocket = rawSocket\n        protocol =\n          when {\n            Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols -> Protocol.H2_PRIOR_KNOWLEDGE\n            else -> Protocol.HTTP_1_1\n          }\n      }\n\n      val connection =\n        RealConnection(\n          taskRunner = taskRunner,\n          connectionPool = connectionPool,\n          route = route,\n          rawSocket = rawSocket,\n          javaNetSocket = javaNetSocket!!,\n          handshake = handshake,\n          protocol = protocol!!,\n          socket = socket,\n          pingIntervalMillis = pingIntervalMillis,\n          connectionListener = connectionPool.connectionListener,\n        )\n      this.connection = connection\n      connection.start()\n\n      // Success.\n      call.eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)\n      success = true\n      return ConnectResult(plan = this)\n    } catch (e: IOException) {\n      call.eventListener.connectFailed(call, route.socketAddress, route.proxy, null, e)\n      connectionPool.connectionListener.connectFailed(route, call, e)\n\n      if (!retryOnConnectionFailure || !retryTlsHandshake(e)) {\n        retryTlsConnection = null\n      }\n\n      return ConnectResult(\n        plan = this,\n        nextPlan = retryTlsConnection,\n        throwable = e,\n      )\n    } finally {\n      call.plansToCancel -= this\n      if (!success) {\n        javaNetSocket?.closeQuietly()\n        rawSocket.closeQuietly()\n      }\n    }\n  }\n\n  /** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */\n  @Throws(IOException::class)\n  private fun connectSocket() {\n    val rawSocket =\n      when (route.proxy.type()) {\n        Proxy.Type.DIRECT, Proxy.Type.HTTP -> route.address.socketFactory.createSocket()!!\n        else -> JavaNetSocket(route.proxy)\n      }\n    this.rawSocket = rawSocket\n\n    // Handle the race where cancel() precedes connectSocket(). We don't want to miss a cancel.\n    if (canceled) {\n      throw IOException(\"canceled\")\n    }\n\n    rawSocket.soTimeout = socketReadTimeoutMillis\n    try {\n      Platform.get().connectSocket(rawSocket, route.socketAddress, socketConnectTimeoutMillis)\n    } catch (e: ConnectException) {\n      throw ConnectException(\"Failed to connect to ${route.socketAddress}\").apply {\n        initCause(e)\n      }\n    }\n\n    // The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0\n    // More details:\n    // https://github.com/square/okhttp/issues/3245\n    // https://android-review.googlesource.com/#/c/271775/\n    try {\n      this.socket = rawSocket.asBufferedSocket()\n    } catch (npe: NullPointerException) {\n      if (npe.message == NPE_THROW_WITH_NULL) {\n        throw IOException(npe)\n      }\n    }\n  }\n\n  /**\n   * Does all the work to build an HTTPS connection over a proxy tunnel. The catch here is that a\n   * proxy server can issue an auth challenge and then close the connection.\n   *\n   * @return the next plan to attempt, or null if no further attempt should be made either because\n   *     we've successfully connected or because no further attempts should be made.\n   */\n  @Throws(IOException::class)\n  internal fun connectTunnel(): ConnectResult {\n    val nextTunnelRequest =\n      createTunnel()\n        ?: return ConnectResult(plan = this) // Success.\n\n    // The proxy decided to close the connection after an auth challenge. Retry with different\n    // auth credentials.\n    rawSocket?.closeQuietly()\n\n    val nextAttempt = attempt + 1\n    return when {\n      nextAttempt < MAX_TUNNEL_ATTEMPTS -> {\n        call.eventListener.connectEnd(call, route.socketAddress, route.proxy, null)\n        ConnectResult(\n          plan = this,\n          nextPlan =\n            copy(\n              attempt = nextAttempt,\n              tunnelRequest = nextTunnelRequest,\n            ),\n        )\n      }\n\n      else -> {\n        val failure =\n          ProtocolException(\n            \"Too many tunnel connections attempted: $MAX_TUNNEL_ATTEMPTS\",\n          )\n        call.eventListener.connectFailed(call, route.socketAddress, route.proxy, null, failure)\n        connectionPool.connectionListener.connectFailed(route, call, failure)\n        return ConnectResult(plan = this, throwable = failure)\n      }\n    }\n  }\n\n  @Throws(IOException::class)\n  private fun connectTls(\n    sslSocket: SSLSocket,\n    connectionSpec: ConnectionSpec,\n  ) {\n    val address = route.address\n    var success = false\n    try {\n      if (connectionSpec.supportsTlsExtensions) {\n        Platform.get().configureTlsExtensions(sslSocket, address.url.host, address.protocols)\n      }\n\n      // Force handshake. This can throw!\n      sslSocket.startHandshake()\n      // block for session establishment\n      val sslSocketSession = sslSocket.session\n      val unverifiedHandshake = sslSocketSession.handshake()\n\n      // Verify that the socket's certificates are acceptable for the target host.\n      if (!address.hostnameVerifier!!.verify(address.url.host, sslSocketSession)) {\n        val peerCertificates = unverifiedHandshake.peerCertificates\n        if (peerCertificates.isNotEmpty()) {\n          val cert = peerCertificates[0] as X509Certificate\n          throw SSLPeerUnverifiedException(\n            \"\"\"\n            |Hostname ${address.url.host} not verified:\n            |    certificate: ${CertificatePinner.pin(cert)}\n            |    DN: ${cert.subjectDN.name}\n            |    subjectAltNames: ${OkHostnameVerifier.allSubjectAltNames(cert)}\n            \"\"\".trimMargin(),\n          )\n        } else {\n          throw SSLPeerUnverifiedException(\n            \"Hostname ${address.url.host} not verified (no certificates)\",\n          )\n        }\n      }\n\n      val certificatePinner = address.certificatePinner!!\n\n      val handshake =\n        Handshake(\n          unverifiedHandshake.tlsVersion,\n          unverifiedHandshake.cipherSuite,\n          unverifiedHandshake.localCertificates,\n        ) {\n          certificatePinner.certificateChainCleaner!!.clean(\n            unverifiedHandshake.peerCertificates,\n            address.url.host,\n          )\n        }\n      this.handshake = handshake\n\n      // Check that the certificate pinner is satisfied by the certificates presented.\n      certificatePinner.check(address.url.host) {\n        handshake.peerCertificates.map { it as X509Certificate }\n      }\n\n      // Success! Save the handshake and the ALPN protocol.\n      val maybeProtocol =\n        if (connectionSpec.supportsTlsExtensions) {\n          Platform.get().getSelectedProtocol(sslSocket)\n        } else {\n          null\n        }\n      javaNetSocket = sslSocket\n      socket = sslSocket.asBufferedSocket()\n      protocol = if (maybeProtocol != null) Protocol.get(maybeProtocol) else Protocol.HTTP_1_1\n      success = true\n    } finally {\n      Platform.get().afterHandshake(sslSocket)\n      if (!success) {\n        sslSocket.closeQuietly()\n      }\n    }\n  }\n\n  /**\n   * To make an HTTPS connection over an HTTP proxy, send an unencrypted CONNECT request to create\n   * the proxy connection. This may need to be retried if the proxy requires authorization.\n   */\n  @Throws(IOException::class)\n  private fun createTunnel(): Request? {\n    var nextRequest = tunnelRequest!!\n    // Make an SSL Tunnel on the first message pair of each SSL + proxy connection.\n    val url = route.address.url\n    val requestLine = \"CONNECT ${url.toHostHeader(includeDefaultPort = true)} HTTP/1.1\"\n    while (true) {\n      val tunnelCodec =\n        Http1ExchangeCodec(\n          // No client for CONNECT tunnels:\n          client = null,\n          carrier = this,\n          socket = socket,\n        )\n      socket.source.timeout().timeout(readTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)\n      socket.sink.timeout().timeout(writeTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)\n      tunnelCodec.writeRequest(nextRequest.headers, requestLine)\n      tunnelCodec.finishRequest()\n      val response =\n        tunnelCodec\n          .readResponseHeaders(false)!!\n          .request(nextRequest)\n          .build()\n      tunnelCodec.skipConnectBody(response)\n\n      when (response.code) {\n        HttpURLConnection.HTTP_OK -> {\n          return null\n        }\n\n        HttpURLConnection.HTTP_PROXY_AUTH -> {\n          nextRequest = route.address.proxyAuthenticator.authenticate(route, response)\n            ?: throw IOException(\"Failed to authenticate with proxy\")\n\n          if (\"close\".equals(response.header(\"Connection\"), ignoreCase = true)) {\n            return nextRequest\n          }\n        }\n\n        else -> {\n          throw IOException(\"Unexpected response code for CONNECT: ${response.code}\")\n        }\n      }\n    }\n  }\n\n  /**\n   * Returns this if its [connectionSpecIndex] is defined, or a new connection with it defined\n   * otherwise.\n   */\n  @Throws(IOException::class)\n  internal fun planWithCurrentOrInitialConnectionSpec(\n    connectionSpecs: List<ConnectionSpec>,\n    sslSocket: SSLSocket,\n  ): ConnectPlan {\n    if (connectionSpecIndex != -1) return this\n    return nextConnectionSpec(connectionSpecs, sslSocket)\n      ?: throw UnknownServiceException(\n        \"Unable to find acceptable protocols.\" +\n          \" isFallback=$isTlsFallback,\" +\n          \" modes=$connectionSpecs,\" +\n          \" supported protocols=${sslSocket.enabledProtocols!!.contentToString()}\",\n      )\n  }\n\n  /**\n   * Returns a copy of this connection with the next connection spec to try, or null if no other\n   * compatible connection specs are available.\n   */\n  internal fun nextConnectionSpec(\n    connectionSpecs: List<ConnectionSpec>,\n    sslSocket: SSLSocket,\n  ): ConnectPlan? {\n    for (i in connectionSpecIndex + 1 until connectionSpecs.size) {\n      if (connectionSpecs[i].isCompatible(sslSocket)) {\n        return copy(connectionSpecIndex = i, isTlsFallback = (connectionSpecIndex != -1))\n      }\n    }\n    return null\n  }\n\n  /** Returns the connection to use, which might be different from [connection]. */\n  override fun handleSuccess(): RealConnection {\n    call.client.routeDatabase.connected(route)\n\n    val connection = this.connection!!\n    connection.connectionListener.connectEnd(connection, route, call)\n\n    // If we raced another call connecting to this host, coalesce the connections. This makes for\n    // 3 different lookups in the connection pool!\n    val pooled3 = routePlanner.planReusePooledConnection(this, routes)\n    if (pooled3 != null) return pooled3.connection\n\n    connection.withLock {\n      connectionPool.put(connection)\n      call.acquireConnectionNoEvents(connection)\n    }\n\n    call.eventListener.connectionAcquired(call, connection)\n    connection.connectionListener.connectionAcquired(connection, call)\n    return connection\n  }\n\n  override fun trackFailure(\n    call: RealCall,\n    e: IOException?,\n  ) {\n    // Do nothing.\n  }\n\n  override fun noNewExchanges() {\n    // Do nothing.\n  }\n\n  override fun cancel() {\n    canceled = true\n    // Close the raw socket so we don't end up doing synchronous I/O.\n    rawSocket?.closeQuietly()\n  }\n\n  override fun retry(): RoutePlanner.Plan =\n    ConnectPlan(\n      taskRunner = taskRunner,\n      connectionPool = connectionPool,\n      readTimeoutMillis = readTimeoutMillis,\n      writeTimeoutMillis = writeTimeoutMillis,\n      socketConnectTimeoutMillis = socketConnectTimeoutMillis,\n      socketReadTimeoutMillis = socketReadTimeoutMillis,\n      pingIntervalMillis = pingIntervalMillis,\n      retryOnConnectionFailure = retryOnConnectionFailure,\n      call = call,\n      routePlanner = routePlanner,\n      route = route,\n      routes = routes,\n      attempt = attempt,\n      tunnelRequest = tunnelRequest,\n      connectionSpecIndex = connectionSpecIndex,\n      isTlsFallback = isTlsFallback,\n    )\n\n  fun closeQuietly() {\n    javaNetSocket?.closeQuietly()\n  }\n\n  companion object {\n    private const val NPE_THROW_WITH_NULL = \"throw with null exception\"\n    private const val MAX_TUNNEL_ATTEMPTS = 21\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/ConnectionListener.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport okhttp3.Call\nimport okhttp3.Connection\nimport okhttp3.Route\nimport okio.IOException\n\n/**\n * Listener for connection events. Extend this class to monitor the new connections and closes.\n *\n * All event methods must execute fast, without external locking, cannot throw exceptions,\n * attempt to mutate the event parameters, or be reentrant back into the client.\n * Any IO - writing to files or network should be done asynchronously.\n */\ninternal abstract class ConnectionListener {\n  /**\n   * Invoked as soon as a call causes a connection to be started.\n   */\n  open fun connectStart(\n    route: Route,\n    call: Call,\n  ) {}\n\n  /**\n   * Invoked when a connection fails to be established.\n   */\n  open fun connectFailed(\n    route: Route,\n    call: Call,\n    failure: IOException,\n  ) {}\n\n  /**\n   * Invoked as soon as a connection is successfully established.\n   */\n  open fun connectEnd(\n    connection: Connection,\n    route: Route,\n    call: Call,\n  ) {}\n\n  /**\n   * Invoked when a connection is released as no longer required.\n   */\n  open fun connectionClosed(connection: Connection) {}\n\n  /**\n   * Invoked when a call is assigned a particular connection.\n   */\n  open fun connectionAcquired(\n    connection: Connection,\n    call: Call,\n  ) {}\n\n  /**\n   * Invoked when a call no longer uses a connection.\n   */\n  open fun connectionReleased(\n    connection: Connection,\n    call: Call,\n  ) {}\n\n  /**\n   * Invoked when a connection is marked for no new exchanges.\n   */\n  open fun noNewExchanges(connection: Connection) {}\n\n  companion object {\n    val NONE: ConnectionListener = object : ConnectionListener() {}\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/Exchange.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport java.net.ProtocolException\nimport okhttp3.EventListener\nimport okhttp3.Headers\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.ResponseBody\nimport okhttp3.internal.http.ExchangeCodec\nimport okhttp3.internal.http.RealResponseBody\nimport okio.Buffer\nimport okio.ForwardingSink\nimport okio.ForwardingSource\nimport okio.Sink\nimport okio.Socket\nimport okio.Source\nimport okio.buffer\n\n/**\n * Transmits a single HTTP request and a response pair. This layers connection management and events\n * on [ExchangeCodec], which handles the actual I/O.\n */\nclass Exchange(\n  internal val call: RealCall,\n  internal val finder: ExchangeFinder,\n  private val codec: ExchangeCodec,\n) {\n  /** True if the request body need not complete before the response body starts. */\n  internal var isDuplex: Boolean = false\n    private set\n\n  /** True if there was an exception on the connection to the peer. */\n  internal var hasFailure: Boolean = false\n    private set\n\n  internal val connection: RealConnection\n    get() = codec.carrier as? RealConnection ?: error(\"no connection for CONNECT tunnels\")\n\n  internal val isCoalescedConnection: Boolean\n    get() = finder.routePlanner.address.url.host != codec.carrier.route.address.url.host\n\n  @Throws(IOException::class)\n  fun writeRequestHeaders(request: Request) {\n    try {\n      call.eventListener.requestHeadersStart(call)\n      codec.writeRequestHeaders(request)\n      call.eventListener.requestHeadersEnd(call, request)\n    } catch (e: IOException) {\n      call.eventListener.requestFailed(call, e)\n      trackFailure(e)\n      throw e\n    }\n  }\n\n  @Throws(IOException::class)\n  fun createRequestBody(\n    request: Request,\n    duplex: Boolean,\n  ): Sink {\n    this.isDuplex = duplex\n    val contentLength = request.body!!.contentLength()\n    call.eventListener.requestBodyStart(call)\n    val rawRequestBody = codec.createRequestBody(request, contentLength)\n    return RequestBodySink(\n      delegate = rawRequestBody,\n      contentLength = contentLength,\n      isSocket = false,\n    )\n  }\n\n  @Throws(IOException::class)\n  fun flushRequest() {\n    try {\n      codec.flushRequest()\n    } catch (e: IOException) {\n      call.eventListener.requestFailed(call, e)\n      trackFailure(e)\n      throw e\n    }\n  }\n\n  @Throws(IOException::class)\n  fun finishRequest() {\n    try {\n      codec.finishRequest()\n    } catch (e: IOException) {\n      call.eventListener.requestFailed(call, e)\n      trackFailure(e)\n      throw e\n    }\n  }\n\n  fun responseHeadersStart() {\n    call.eventListener.responseHeadersStart(call)\n  }\n\n  @Throws(IOException::class)\n  fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {\n    try {\n      val result = codec.readResponseHeaders(expectContinue)\n      result?.initExchange(this)\n      return result\n    } catch (e: IOException) {\n      call.eventListener.responseFailed(call, e)\n      trackFailure(e)\n      throw e\n    }\n  }\n\n  fun responseHeadersEnd(response: Response) {\n    call.eventListener.responseHeadersEnd(call, response)\n  }\n\n  @Throws(IOException::class)\n  fun openResponseBody(response: Response): ResponseBody {\n    try {\n      val contentType = response.header(\"Content-Type\")\n      val contentLength = codec.reportedContentLength(response)\n      val rawSource = codec.openResponseBodySource(response)\n      val source =\n        ResponseBodySource(\n          delegate = rawSource,\n          contentLength = contentLength,\n          isSocket = false,\n        )\n      return RealResponseBody(contentType, contentLength, source.buffer())\n    } catch (e: IOException) {\n      call.eventListener.responseFailed(call, e)\n      trackFailure(e)\n      throw e\n    }\n  }\n\n  @Throws(IOException::class)\n  fun peekTrailers(): Headers? = codec.peekTrailers()\n\n  fun upgradeToSocket(): Socket {\n    call.upgradeToSocket()\n    (codec.carrier as RealConnection).useAsSocket()\n\n    return object : Socket {\n      override fun cancel() {\n        this@Exchange.cancel()\n      }\n\n      override val sink =\n        RequestBodySink(\n          delegate = codec.socket.sink,\n          contentLength = -1L,\n          isSocket = true,\n        )\n      override val source =\n        ResponseBodySource(\n          delegate = codec.socket.source,\n          contentLength = -1L,\n          isSocket = true,\n        )\n    }\n  }\n\n  fun noNewExchangesOnConnection() {\n    codec.carrier.noNewExchanges()\n  }\n\n  fun cancel() {\n    codec.cancel()\n  }\n\n  /**\n   * Revoke this exchange's access to streams. This is necessary when a follow-up request is\n   * required but the preceding exchange hasn't completed yet.\n   */\n  fun detachWithViolence() {\n    codec.cancel()\n    call.messageDone(\n      exchange = this,\n      requestDone = true,\n      responseDone = true,\n      socketSinkDone = true,\n      socketSourceDone = true,\n      e = null,\n    )\n  }\n\n  private fun trackFailure(e: IOException) {\n    hasFailure = true\n    codec.carrier.trackFailure(call, e)\n  }\n\n  /** If [e] is non-null, this will return a non-null value. */\n  fun bodyComplete(\n    bytesRead: Long = -1L,\n    isSocket: Boolean,\n    responseDone: Boolean = false,\n    requestDone: Boolean = false,\n    e: IOException?,\n  ): IOException? {\n    if (e != null) {\n      trackFailure(e)\n    }\n    if (requestDone) {\n      if (e != null) {\n        call.eventListener.requestFailed(call, e)\n      } else {\n        call.eventListener.requestBodyEnd(call, bytesRead)\n      }\n    }\n    if (responseDone) {\n      if (e != null) {\n        call.eventListener.responseFailed(call, e)\n      } else {\n        call.eventListener.responseBodyEnd(call, bytesRead)\n      }\n    }\n    return call.messageDone(\n      exchange = this,\n      requestDone = requestDone && !isSocket,\n      responseDone = responseDone && !isSocket,\n      socketSinkDone = requestDone && isSocket,\n      socketSourceDone = responseDone && isSocket,\n      e = e,\n    )\n  }\n\n  fun noRequestBody() {\n    call.messageDone(\n      exchange = this,\n      requestDone = true,\n      e = null,\n    )\n  }\n\n  /** A request body that fires events when it completes. */\n  private inner class RequestBodySink(\n    delegate: Sink,\n    /** The exact number of bytes to be written, or -1L if that is unknown. */\n    private val contentLength: Long,\n    private val isSocket: Boolean,\n  ) : ForwardingSink(delegate) {\n    private var completed = false\n    private var bytesReceived = 0L\n    private var invokeStartEvent = isSocket\n    private var closed = false\n\n    @Throws(IOException::class)\n    override fun write(\n      source: Buffer,\n      byteCount: Long,\n    ) {\n      check(!closed) { \"closed\" }\n      if (contentLength != -1L && bytesReceived + byteCount > contentLength) {\n        throw ProtocolException(\n          \"expected $contentLength bytes but received ${bytesReceived + byteCount}\",\n        )\n      }\n      try {\n        if (invokeStartEvent) {\n          invokeStartEvent = false\n          call.eventListener.requestBodyStart(call)\n        }\n        super.write(source, byteCount)\n        this.bytesReceived += byteCount\n      } catch (e: IOException) {\n        throw complete(e)!!\n      }\n    }\n\n    @Throws(IOException::class)\n    override fun flush() {\n      try {\n        super.flush()\n      } catch (e: IOException) {\n        throw complete(e)!!\n      }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n      if (closed) return\n      closed = true\n      if (contentLength != -1L && bytesReceived != contentLength) {\n        throw ProtocolException(\"unexpected end of stream\")\n      }\n      try {\n        super.close()\n        complete(null)\n      } catch (e: IOException) {\n        throw complete(e)!!\n      }\n    }\n\n    /** If [e] is non-null, this will return a non-null value. */\n    private fun complete(e: IOException?): IOException? {\n      if (completed) return e\n      completed = true\n      return bodyComplete(\n        bytesRead = bytesReceived,\n        isSocket = isSocket,\n        requestDone = true,\n        e = e,\n      )\n    }\n  }\n\n  /** A response body that fires events when it completes. */\n  internal inner class ResponseBodySource(\n    delegate: Source,\n    private val contentLength: Long,\n    private val isSocket: Boolean,\n  ) : ForwardingSource(delegate) {\n    private var bytesReceived = 0L\n    private var invokeStartEvent = true\n    private var completed = false\n    private var closed = false\n\n    init {\n      if (contentLength == 0L) {\n        complete(null)\n      }\n    }\n\n    @Throws(IOException::class)\n    override fun read(\n      sink: Buffer,\n      byteCount: Long,\n    ): Long {\n      check(!closed) { \"closed\" }\n      try {\n        val read = delegate.read(sink, byteCount)\n\n        if (invokeStartEvent) {\n          invokeStartEvent = false\n          call.eventListener.responseBodyStart(call)\n        }\n\n        if (read == -1L) {\n          complete(null)\n          return -1L\n        }\n\n        val newBytesReceived = bytesReceived + read\n        if (contentLength != -1L && newBytesReceived > contentLength) {\n          throw ProtocolException(\"expected $contentLength bytes but received $newBytesReceived\")\n        }\n\n        bytesReceived = newBytesReceived\n        if (codec.isResponseComplete) {\n          complete(null)\n        }\n\n        return read\n      } catch (e: IOException) {\n        throw complete(e)!!\n      }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n      if (closed) return\n      closed = true\n      try {\n        super.close()\n        complete(null)\n      } catch (e: IOException) {\n        throw complete(e)!!\n      }\n    }\n\n    /** If [e] is non-null, this will return a non-null value. */\n    fun complete(e: IOException?): IOException? {\n      if (completed) return e\n      completed = true\n      // If the body is closed without reading any bytes send a responseBodyStart() now.\n      if (e == null && invokeStartEvent) {\n        invokeStartEvent = false\n        call.eventListener.responseBodyStart(call)\n      }\n      return bodyComplete(\n        bytesRead = bytesReceived,\n        isSocket = isSocket,\n        responseDone = true,\n        e = e,\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/ExchangeFinder.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\ninterface ExchangeFinder {\n  val routePlanner: RoutePlanner\n\n  fun find(): RealConnection\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/FailedPlan.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\n/**\n * Used when we were unsuccessful in the planning phase of a connection:\n *\n *  * A DNS lookup failed\n *  * The configuration is incapable of carrying the request, such as when the client is configured\n *    to use `H2_PRIOR_KNOWLEDGE` but the URL's scheme is `https:`.\n *  * Preemptive proxy authentication failed.\n *\n * Planning failures are not necessarily fatal. For example, even if we can't DNS lookup the first\n * proxy in a list, looking up a subsequent one may succeed.\n */\ninternal class FailedPlan(\n  e: Throwable,\n) : RoutePlanner.Plan {\n  val result = RoutePlanner.ConnectResult(plan = this, throwable = e)\n\n  override val isReady = false\n\n  override fun connectTcp() = result\n\n  override fun connectTlsEtc() = result\n\n  override fun handleSuccess() = error(\"unexpected call\")\n\n  override fun cancel() = error(\"unexpected cancel\")\n\n  override fun retry() = error(\"unexpected retry\")\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/FastFallbackExchangeFinder.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport java.util.concurrent.CopyOnWriteArrayList\nimport java.util.concurrent.LinkedBlockingDeque\nimport java.util.concurrent.TimeUnit\nimport okhttp3.internal.concurrent.Task\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.connection.RoutePlanner.ConnectResult\nimport okhttp3.internal.connection.RoutePlanner.Plan\nimport okhttp3.internal.okHttpName\n\n/**\n * Speculatively connects to each IP address of a target address, returning as soon as one of them\n * connects successfully. This kicks off new attempts every 250 ms until a connect succeeds.\n */\ninternal class FastFallbackExchangeFinder(\n  override val routePlanner: RoutePlanner,\n  private val taskRunner: TaskRunner,\n) : ExchangeFinder {\n  private val connectDelayNanos = TimeUnit.MILLISECONDS.toNanos(250L)\n  private var nextTcpConnectAtNanos = Long.MIN_VALUE\n\n  /**\n   * Plans currently being connected, and that will later be added to [connectResults]. This is\n   * mutated by the call thread only. If is accessed by background connect threads.\n   */\n  private val tcpConnectsInFlight = CopyOnWriteArrayList<Plan>()\n\n  /**\n   * Results are posted here as they occur. The find job is done when either one plan completes\n   * successfully or all plans fail.\n   */\n  private val connectResults = taskRunner.backend.decorate(LinkedBlockingDeque<ConnectResult>())\n\n  override fun find(): RealConnection {\n    var firstException: IOException? = null\n    try {\n      while (tcpConnectsInFlight.isNotEmpty() || routePlanner.hasNext()) {\n        if (routePlanner.isCanceled()) throw IOException(\"Canceled\")\n\n        // Launch a new connection if we're ready to.\n        val now = taskRunner.backend.nanoTime()\n        var awaitTimeoutNanos = nextTcpConnectAtNanos - now\n        var connectResult: ConnectResult? = null\n        if (tcpConnectsInFlight.isEmpty() || awaitTimeoutNanos <= 0) {\n          connectResult = launchTcpConnect()\n          nextTcpConnectAtNanos = now + connectDelayNanos\n          awaitTimeoutNanos = connectDelayNanos\n        }\n\n        // Wait for an in-flight connect to complete or fail.\n        if (connectResult == null) {\n          connectResult = awaitTcpConnect(awaitTimeoutNanos, TimeUnit.NANOSECONDS) ?: continue\n        }\n\n        if (connectResult.isSuccess) {\n          // We have a connected TCP connection. Cancel and defer the racing connects that all lost.\n          cancelInFlightConnects()\n\n          // Finish connecting. We won't have to if the winner is from the connection pool.\n          if (!connectResult.plan.isReady) {\n            connectResult = connectResult.plan.connectTlsEtc()\n          }\n\n          if (connectResult.isSuccess) {\n            return connectResult.plan.handleSuccess()\n          }\n        }\n\n        val throwable = connectResult.throwable\n        if (throwable != null) {\n          if (throwable !is IOException) throw throwable\n          if (firstException == null) {\n            firstException = throwable\n          } else {\n            firstException.addSuppressed(throwable)\n          }\n        }\n\n        val nextPlan = connectResult.nextPlan\n        if (nextPlan != null) {\n          // Try this plan's successor before deferred plans because it won the race!\n          routePlanner.deferredPlans.addFirst(nextPlan)\n        }\n      }\n    } finally {\n      cancelInFlightConnects()\n    }\n\n    throw firstException!!\n  }\n\n  /**\n   * Returns non-null if we don't need to wait for the launched result. In such cases, this result\n   * must be processed before whatever is waiting in the queue because we may have already acquired\n   * its connection.\n   */\n  private fun launchTcpConnect(): ConnectResult? {\n    val plan =\n      when {\n        routePlanner.hasNext() -> {\n          try {\n            routePlanner.plan()\n          } catch (e: Throwable) {\n            FailedPlan(e)\n          }\n        }\n\n        else -> {\n          return null\n        } // Nothing further to try.\n      }\n\n    // Already connected. Return it immediately.\n    if (plan.isReady) return ConnectResult(plan)\n\n    // Already failed? Return it immediately.\n    if (plan is FailedPlan) return plan.result\n\n    // Connect TCP asynchronously.\n    tcpConnectsInFlight += plan\n    val taskName = \"$okHttpName connect ${routePlanner.address.url.redact()}\"\n    taskRunner.newQueue().schedule(\n      object : Task(taskName) {\n        override fun runOnce(): Long {\n          val connectResult =\n            try {\n              plan.connectTcp()\n            } catch (e: Throwable) {\n              ConnectResult(plan, throwable = e)\n            }\n          // Only post a result if this hasn't since been canceled.\n          if (plan in tcpConnectsInFlight) {\n            connectResults.put(connectResult)\n          }\n          return -1L\n        }\n      },\n    )\n    return null\n  }\n\n  private fun awaitTcpConnect(\n    timeout: Long,\n    unit: TimeUnit,\n  ): ConnectResult? {\n    if (tcpConnectsInFlight.isEmpty()) return null\n\n    val result = connectResults.poll(timeout, unit) ?: return null\n\n    tcpConnectsInFlight.remove(result.plan)\n\n    return result\n  }\n\n  private fun cancelInFlightConnects() {\n    for (plan in tcpConnectsInFlight) {\n      plan.cancel()\n      val retry = plan.retry() ?: continue\n      routePlanner.deferredPlans.addLast(retry)\n    }\n    tcpConnectsInFlight.clear()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/ForceConnectRoutePlanner.kt",
    "content": "/*\n * Copyright (C) 2024 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\n/**\n * A RoutePlanner that will always establish a new connection, ignoring any connection pooling\n */\nclass ForceConnectRoutePlanner(\n  private val delegate: RealRoutePlanner,\n) : RoutePlanner by delegate {\n  override fun plan(): RoutePlanner.Plan = delegate.planConnect() // not delegate.plan()\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/InetAddressOrder.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.internal.connection\n\nimport java.net.Inet6Address\nimport java.net.InetAddress\nimport okhttp3.internal.interleave\n\n/**\n * Implementation of HappyEyeballs Sorting Addresses.\n *\n * The current implementation does not address any of:\n *  - Async DNS split by IP class\n *  - Stateful handling of connectivity results\n *  - The prioritisation of addresses\n *\n * https://datatracker.ietf.org/doc/html/rfc8305#section-4\n */\ninternal fun reorderForHappyEyeballs(addresses: List<InetAddress>): List<InetAddress> {\n  if (addresses.size < 2) {\n    return addresses\n  }\n\n  val (ipv6, ipv4) = addresses.partition { it is Inet6Address }\n\n  return if (ipv6.isEmpty() || ipv4.isEmpty()) {\n    addresses\n  } else {\n    interleave(ipv6, ipv4)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RealCall.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.lang.ref.WeakReference\nimport java.net.Socket\nimport java.util.concurrent.CopyOnWriteArrayList\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.RejectedExecutionException\nimport java.util.concurrent.TimeUnit.MILLISECONDS\nimport java.util.concurrent.atomic.AtomicBoolean\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.concurrent.atomic.AtomicReference\nimport java.util.concurrent.atomic.AtomicReferenceFieldUpdater\nimport kotlin.reflect.KClass\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.EventListener\nimport okhttp3.Interceptor\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.assertLockNotHeld\nimport okhttp3.internal.cache.CacheInterceptor\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.computeIfAbsent\nimport okhttp3.internal.concurrent.Lockable\nimport okhttp3.internal.concurrent.assertLockHeld\nimport okhttp3.internal.concurrent.assertLockNotHeld\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.http.BridgeInterceptor\nimport okhttp3.internal.http.CallServerInterceptor\nimport okhttp3.internal.http.RealInterceptorChain\nimport okhttp3.internal.http.RetryAndFollowUpInterceptor\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.threadName\nimport okio.AsyncTimeout\nimport okio.Timeout\n\n/**\n * Bridge between OkHttp's application and network layers. This class exposes high-level application\n * layer primitives: connections, requests, responses, and streams.\n *\n * This class supports [asynchronous canceling][cancel]. This is intended to have the smallest\n * blast radius possible. If an HTTP/2 stream is active, canceling will cancel that stream but not\n * the other streams sharing its connection. But if the TLS handshake is still in progress then\n * canceling may break the entire connection.\n */\nclass RealCall(\n  val client: OkHttpClient,\n  /** The application's original request unadulterated by redirects or auth headers. */\n  val originalRequest: Request,\n  val forWebSocket: Boolean,\n) : Call,\n  Cloneable,\n  Lockable {\n  private val connectionPool: RealConnectionPool = client.connectionPool.delegate\n\n  @Volatile\n  internal var eventListener: EventListener = client.eventListenerFactory.create(this)\n\n  private val timeout =\n    object : AsyncTimeout() {\n      override fun timedOut() {\n        this@RealCall.cancel()\n      }\n    }.apply {\n      timeout(client.callTimeoutMillis.toLong(), MILLISECONDS)\n    }\n\n  private val executed = AtomicBoolean()\n\n  // These properties are only accessed by the thread executing the call.\n\n  /** Initialized in [callStart]. */\n  private var callStackTrace: Any? = null\n\n  /** Finds an exchange to send the next request and receive the next response. */\n  private var exchangeFinder: ExchangeFinder? = null\n\n  var connection: RealConnection? = null\n    private set\n  private var timeoutEarlyExit = false\n\n  /**\n   * This is the same value as [exchange], but scoped to the execution of the network interceptors.\n   * The [exchange] field is assigned to null when its streams end, which may be before or after the\n   * network interceptors return.\n   */\n  internal var interceptorScopedExchange: Exchange? = null\n    private set\n\n  // These properties are guarded by `this`. They are typically only accessed by the thread executing\n  // the call, but they may be accessed by other threads for duplex requests.\n\n  private var requestBodyOpen = false\n  private var responseBodyOpen = false\n  private var socketSinkOpen = false\n  private var socketSourceOpen = false\n\n  /** True if there are more exchanges expected for this call. */\n  private var expectMoreExchanges = true\n\n  // These properties are accessed by canceling threads. Any thread can cancel a call, and once it's\n  // canceled it's canceled forever.\n\n  @Volatile private var canceled = false\n\n  @Volatile private var exchange: Exchange? = null\n  internal val plansToCancel = CopyOnWriteArrayList<RoutePlanner.Plan>()\n\n  private val tags = AtomicReference(originalRequest.tags)\n\n  override fun timeout(): Timeout = timeout\n\n  override fun addEventListener(eventListener: EventListener) {\n    // Atomically replace the current eventListener with a composite one.\n    do {\n      val previous = this.eventListener\n    } while (!eventListenerUpdater.compareAndSet(this, previous, previous + eventListener))\n  }\n\n  override fun <T : Any> tag(type: KClass<T>): T? = type.java.cast(tags.get()[type])\n\n  override fun <T> tag(type: Class<out T>): T? = tag(type.kotlin)\n\n  override fun <T : Any> tag(\n    type: KClass<T>,\n    computeIfAbsent: () -> T,\n  ): T = tags.computeIfAbsent(type, computeIfAbsent)\n\n  override fun <T : Any> tag(\n    type: Class<T>,\n    computeIfAbsent: () -> T,\n  ): T = tags.computeIfAbsent(type.kotlin, computeIfAbsent)\n\n  @SuppressWarnings(\"CloneDoesntCallSuperClone\") // We are a final type & this saves clearing state.\n  override fun clone(): Call = RealCall(client, originalRequest, forWebSocket)\n\n  override fun request(): Request = originalRequest\n\n  /**\n   * Immediately closes the socket connection if it's currently held. Use this to interrupt an\n   * in-flight request from any thread. It's the caller's responsibility to close the request body\n   * and response body streams; otherwise resources may be leaked.\n   *\n   * This method is safe to be called concurrently, but provides limited guarantees. If a transport\n   * layer connection has been established (such as a HTTP/2 stream) that is terminated. Otherwise,\n   * if a socket connection is being established, that is terminated.\n   */\n  override fun cancel() {\n    if (canceled) return // Already canceled.\n\n    canceled = true\n    exchange?.cancel()\n    for (plan in plansToCancel) {\n      plan.cancel()\n    }\n\n    eventListener.canceled(this)\n  }\n\n  override fun isCanceled(): Boolean = canceled\n\n  override fun execute(): Response {\n    check(executed.compareAndSet(false, true)) { \"Already Executed\" }\n\n    timeout.enter()\n    callStart()\n    try {\n      client.dispatcher.executed(this)\n      return getResponseWithInterceptorChain()\n    } finally {\n      client.dispatcher.finished(this)\n    }\n  }\n\n  override fun enqueue(responseCallback: Callback) {\n    check(executed.compareAndSet(false, true)) { \"Already Executed\" }\n\n    callStart()\n    client.dispatcher.enqueue(AsyncCall(responseCallback))\n  }\n\n  override fun isExecuted(): Boolean = executed.get()\n\n  private fun callStart() {\n    this.callStackTrace = Platform.get().getStackTraceForCloseable(\"response.body().close()\")\n    eventListener.callStart(this)\n  }\n\n  @Throws(IOException::class)\n  internal fun getResponseWithInterceptorChain(): Response {\n    // Build a full stack of interceptors.\n    val interceptors = mutableListOf<Interceptor>()\n    interceptors += client.interceptors\n    interceptors += RetryAndFollowUpInterceptor()\n    interceptors += BridgeInterceptor()\n    interceptors += CacheInterceptor()\n    interceptors += ConnectInterceptor\n    if (!forWebSocket) {\n      interceptors += client.networkInterceptors\n    }\n    interceptors += CallServerInterceptor\n\n    val chain =\n      RealInterceptorChain(\n        call = this,\n        interceptors = interceptors,\n        index = 0,\n        exchange = null,\n        request = originalRequest,\n      )\n\n    var calledNoMoreExchanges = false\n    try {\n      val response = chain.proceed(originalRequest)\n      if (isCanceled()) {\n        response.closeQuietly()\n        throw IOException(\"Canceled\")\n      }\n      return response\n    } catch (e: IOException) {\n      calledNoMoreExchanges = true\n      throw noMoreExchanges(e) as Throwable\n    } finally {\n      if (!calledNoMoreExchanges) {\n        noMoreExchanges(null)\n      }\n    }\n  }\n\n  /**\n   * Prepare for a potential trip through all of this call's network interceptors. This prepares to\n   * find an exchange to carry the request.\n   *\n   * Note that an exchange will not be needed if the request is satisfied by the cache.\n   *\n   * @param newRoutePlanner true if this is not a retry and new routing can be performed.\n   */\n  fun enterNetworkInterceptorExchange(\n    request: Request,\n    newRoutePlanner: Boolean,\n    chain: RealInterceptorChain,\n  ) {\n    check(interceptorScopedExchange == null)\n\n    withLock {\n      check(!responseBodyOpen) {\n        \"cannot make a new request because the previous response is still open: \" +\n          \"please call response.close()\"\n      }\n      check(!requestBodyOpen && !socketSourceOpen && !socketSinkOpen)\n    }\n\n    if (newRoutePlanner) {\n      val routePlanner =\n        RealRoutePlanner(\n          taskRunner = client.taskRunner,\n          connectionPool = chain.connectionPool.delegate,\n          readTimeoutMillis = chain.readTimeoutMillis,\n          writeTimeoutMillis = chain.writeTimeoutMillis,\n          socketConnectTimeoutMillis = chain.connectTimeoutMillis,\n          socketReadTimeoutMillis = chain.readTimeoutMillis,\n          pingIntervalMillis = client.pingIntervalMillis,\n          retryOnConnectionFailure = chain.retryOnConnectionFailure,\n          fastFallback = client.fastFallback,\n          address = chain.address(request.url),\n          routeDatabase = client.routeDatabase,\n          call = this,\n          request = request,\n        )\n      this.exchangeFinder =\n        when {\n          client.fastFallback -> FastFallbackExchangeFinder(routePlanner, client.taskRunner)\n          else -> SequentialExchangeFinder(routePlanner)\n        }\n    }\n  }\n\n  /** Finds a new or pooled connection to carry a forthcoming request and response. */\n  internal fun initExchange(chain: RealInterceptorChain): Exchange {\n    withLock {\n      check(expectMoreExchanges) { \"released\" }\n      check(!responseBodyOpen && !requestBodyOpen && !socketSourceOpen && !socketSinkOpen)\n    }\n\n    val exchangeFinder = this.exchangeFinder!!\n    val connection = exchangeFinder.find()\n    val codec = connection.newCodec(client, chain)\n    val result = Exchange(this, exchangeFinder, codec)\n    this.interceptorScopedExchange = result\n    this.exchange = result\n    withLock {\n      this.requestBodyOpen = true\n      this.responseBodyOpen = true\n    }\n\n    if (canceled) throw IOException(\"Canceled\")\n    return result\n  }\n\n  fun acquireConnectionNoEvents(connection: RealConnection) {\n    connection.assertLockHeld()\n\n    check(this.connection == null)\n    this.connection = connection\n    connection.calls.add(CallReference(this, callStackTrace))\n  }\n\n  /**\n   * Releases resources held with the request or response of [exchange]. This should be called when\n   * the request completes normally or when it fails due to an exception, in which case [e] should\n   * be non-null.\n   *\n   * If the exchange was canceled or timed out, this will wrap [e] in an exception that provides\n   * that additional context. Otherwise [e] is returned as-is.\n   */\n  internal fun messageDone(\n    exchange: Exchange,\n    requestDone: Boolean = false,\n    responseDone: Boolean = false,\n    socketSourceDone: Boolean = false,\n    socketSinkDone: Boolean = false,\n    e: IOException?,\n  ): IOException? {\n    if (exchange != this.exchange) return e // This exchange was detached violently!\n\n    var allStreamsDone = false\n    var callDone = false\n    withLock {\n      if (\n        requestDone && requestBodyOpen ||\n        responseDone && responseBodyOpen ||\n        socketSinkDone && socketSinkOpen ||\n        socketSourceDone && socketSourceOpen\n      ) {\n        if (requestDone) requestBodyOpen = false\n        if (responseDone) responseBodyOpen = false\n        if (socketSinkDone) socketSinkOpen = false\n        if (socketSourceDone) socketSourceOpen = false\n        allStreamsDone = !requestBodyOpen &&\n          !responseBodyOpen &&\n          !socketSinkOpen &&\n          !socketSourceOpen\n        callDone = allStreamsDone && !expectMoreExchanges\n      }\n    }\n\n    if (allStreamsDone) {\n      this.exchange = null\n      this.connection?.incrementSuccessCount()\n    }\n\n    if (callDone) {\n      return callDone(e)\n    }\n\n    return e\n  }\n\n  internal fun noMoreExchanges(e: IOException?): IOException? {\n    var callDone = false\n    withLock {\n      if (expectMoreExchanges) {\n        expectMoreExchanges = false\n        callDone = !requestBodyOpen && !responseBodyOpen && !socketSinkOpen && !socketSourceOpen\n      }\n    }\n\n    if (callDone) {\n      return callDone(e)\n    }\n\n    return e\n  }\n\n  /**\n   * Complete this call. This should be called once these properties are all false:\n   * [requestBodyOpen], [responseBodyOpen], [socketSinkOpen], [socketSourceOpen], and\n   * [expectMoreExchanges].\n   *\n   * This will release the connection if it is still held.\n   *\n   * It will also notify the listener that the call completed; either successfully or\n   * unsuccessfully.\n   *\n   * If the call was canceled or timed out, this will wrap [e] in an exception that provides that\n   * additional context. Otherwise [e] is returned as-is.\n   */\n  private fun callDone(e: IOException?): IOException? {\n    assertLockNotHeld()\n\n    val connection = this.connection\n    if (connection != null) {\n      connection.assertLockNotHeld()\n      val toClose: Socket? =\n        connection.withLock {\n          // Sets this.connection to null.\n          releaseConnectionNoEvents()\n        }\n      if (this.connection == null) {\n        toClose?.closeQuietly()\n        eventListener.connectionReleased(this, connection)\n        connection.connectionListener.connectionReleased(connection, this)\n        if (toClose != null) {\n          connection.connectionListener.connectionClosed(connection)\n        }\n      } else {\n        check(toClose == null) // If we still have a connection we shouldn't be closing any sockets.\n      }\n    }\n\n    val result = timeoutExit(e)\n    if (e != null) {\n      eventListener.callFailed(this, result!!)\n    } else {\n      eventListener.callEnd(this)\n    }\n    return result\n  }\n\n  /**\n   * Remove this call from the connection's list of allocations. Returns a socket that the caller\n   * should close.\n   */\n  internal fun releaseConnectionNoEvents(): Socket? {\n    val connection = this.connection!!\n    connection.assertLockHeld()\n\n    val calls = connection.calls\n    val index = calls.indexOfFirst { it.get() == this@RealCall }\n    check(index != -1)\n\n    calls.removeAt(index)\n    this.connection = null\n\n    if (calls.isEmpty()) {\n      connection.idleAtNs = System.nanoTime()\n      if (connectionPool.connectionBecameIdle(connection)) {\n        return connection.socket()\n      }\n    }\n\n    return null\n  }\n\n  private fun timeoutExit(cause: IOException?): IOException? {\n    if (timeoutEarlyExit) return cause\n    if (!timeout.exit()) return cause\n\n    val e = InterruptedIOException(\"timeout\")\n    if (cause != null) e.initCause(cause)\n    return e\n  }\n\n  /**\n   * Stops applying the timeout before the call is entirely complete. This is used for WebSockets\n   * and duplex calls where the timeout only applies to the initial setup.\n   */\n  fun timeoutEarlyExit() {\n    check(!timeoutEarlyExit)\n    timeoutEarlyExit = true\n    timeout.exit()\n  }\n\n  fun upgradeToSocket() {\n    timeoutEarlyExit()\n\n    withLock {\n      check(exchange != null)\n      check(!socketSinkOpen && !socketSourceOpen)\n      check(!requestBodyOpen)\n      check(responseBodyOpen)\n      responseBodyOpen = false\n      socketSinkOpen = true\n      socketSourceOpen = true\n    }\n  }\n\n  /**\n   * @param closeExchange true if the current exchange should be closed because it will not be used.\n   *     This is usually due to either an exception or a retry.\n   */\n  internal fun exitNetworkInterceptorExchange(closeExchange: Boolean) {\n    withLock {\n      check(expectMoreExchanges) { \"released\" }\n    }\n\n    if (closeExchange) {\n      exchange?.detachWithViolence()\n    }\n\n    interceptorScopedExchange = null\n  }\n\n  fun retryAfterFailure(): Boolean =\n    exchange?.hasFailure == true &&\n      exchangeFinder!!.routePlanner.hasNext(exchange?.connection)\n\n  /**\n   * Returns a string that describes this call. Doesn't include a full URL as that might contain\n   * sensitive information.\n   */\n  private fun toLoggableString(): String =\n    (\n      (if (isCanceled()) \"canceled \" else \"\") +\n        (if (forWebSocket) \"web socket\" else \"call\") +\n        \" to \" + redactedUrl()\n    )\n\n  internal fun redactedUrl(): String = originalRequest.url.redact()\n\n  inner class AsyncCall(\n    private val responseCallback: Callback,\n  ) : Runnable {\n    @Volatile var callsPerHost = AtomicInteger(0)\n      private set\n\n    fun reuseCallsPerHostFrom(other: AsyncCall) {\n      this.callsPerHost = other.callsPerHost\n    }\n\n    val host: String\n      get() = originalRequest.url.host\n\n    val request: Request\n      get() = originalRequest\n\n    val call: RealCall\n      get() = this@RealCall\n\n    /**\n     * Attempt to enqueue this async call on [executorService]. This will attempt to clean up\n     * if the executor has been shut down by reporting the call as failed.\n     */\n    fun executeOn(executorService: ExecutorService) {\n      client.dispatcher.assertLockNotHeld()\n\n      var success = false\n      try {\n        executorService.execute(this)\n        success = true\n      } catch (e: RejectedExecutionException) {\n        failRejected(e)\n      } finally {\n        if (!success) {\n          client.dispatcher.finished(this) // This call is no longer running!\n        }\n      }\n    }\n\n    internal fun failRejected(e: RejectedExecutionException? = null) {\n      val ioException = InterruptedIOException(\"executor rejected\")\n      ioException.initCause(e)\n      noMoreExchanges(ioException)\n      responseCallback.onFailure(this@RealCall, ioException)\n    }\n\n    override fun run() {\n      threadName(\"OkHttp ${redactedUrl()}\") {\n        var signalledCallback = false\n        timeout.enter()\n        try {\n          val response = getResponseWithInterceptorChain()\n          signalledCallback = true\n          responseCallback.onResponse(this@RealCall, response)\n        } catch (e: IOException) {\n          if (signalledCallback) {\n            // Do not signal the callback twice!\n            Platform.get().log(\"Callback failure for ${toLoggableString()}\", Platform.INFO, e)\n          } else {\n            responseCallback.onFailure(this@RealCall, e)\n          }\n        } catch (t: Throwable) {\n          cancel()\n          if (!signalledCallback) {\n            val canceledException = IOException(\"canceled due to $t\")\n            canceledException.initCause(t)\n            responseCallback.onFailure(this@RealCall, canceledException)\n          }\n          if (t is InterruptedException) {\n            Thread.currentThread().interrupt()\n          } else {\n            throw t\n          }\n        } finally {\n          client.dispatcher.finished(this)\n        }\n      }\n    }\n  }\n\n  internal class CallReference(\n    referent: RealCall,\n    /**\n     * Captures the stack trace at the time the Call is executed or enqueued. This is helpful for\n     * identifying the origin of connection leaks.\n     */\n    val callStackTrace: Any?,\n  ) : WeakReference<RealCall>(referent)\n\n  private companion object {\n    val eventListenerUpdater: AtomicReferenceFieldUpdater<RealCall, EventListener> =\n      AtomicReferenceFieldUpdater.newUpdater(\n        RealCall::class.java,\n        EventListener::class.java,\n        \"eventListener\",\n      )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RealConnection.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport java.lang.ref.Reference\nimport java.net.Proxy\nimport java.net.Socket as JavaNetSocket\nimport java.net.SocketException\nimport java.security.cert.X509Certificate\nimport java.util.concurrent.TimeUnit.MILLISECONDS\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Address\nimport okhttp3.Connection\nimport okhttp3.Handshake\nimport okhttp3.HttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Route\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.Lockable\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.assertLockHeld\nimport okhttp3.internal.concurrent.assertLockNotHeld\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.http.ExchangeCodec\nimport okhttp3.internal.http.RealInterceptorChain\nimport okhttp3.internal.http1.Http1ExchangeCodec\nimport okhttp3.internal.http2.ConnectionShutdownException\nimport okhttp3.internal.http2.ErrorCode\nimport okhttp3.internal.http2.FlowControlListener\nimport okhttp3.internal.http2.Http2Connection\nimport okhttp3.internal.http2.Http2ExchangeCodec\nimport okhttp3.internal.http2.Http2Stream\nimport okhttp3.internal.http2.Settings\nimport okhttp3.internal.http2.StreamResetException\nimport okhttp3.internal.isHealthy\nimport okhttp3.internal.tls.OkHostnameVerifier\nimport okio.Buffer\n\n/**\n * A connection to a remote web server capable of carrying 1 or more concurrent streams.\n *\n * Connections are shared in a connection pool. Accesses to the connection's state must be guarded\n * by holding a lock on the connection.\n */\nclass RealConnection internal constructor(\n  val taskRunner: TaskRunner,\n  val connectionPool: RealConnectionPool,\n  override val route: Route,\n  /** The low-level TCP socket. */\n  private val rawSocket: JavaNetSocket,\n  /**\n   * The application layer socket. Either an [SSLSocket] layered over [rawSocket], or [rawSocket]\n   * itself if this connection does not use SSL.\n   */\n  private val javaNetSocket: JavaNetSocket,\n  private val handshake: Handshake?,\n  private val protocol: Protocol,\n  private val socket: BufferedSocket,\n  private val pingIntervalMillis: Int,\n  internal val connectionListener: ConnectionListener,\n) : Http2Connection.Listener(),\n  Connection,\n  ExchangeCodec.Carrier,\n  Lockable {\n  private var http2Connection: Http2Connection? = null\n\n  // These properties are guarded by `this`.\n\n  /**\n   * If true, no new exchanges can be created on this connection. It is necessary to set this to\n   * true when removing a connection from the pool; otherwise a racing caller might get it from the\n   * pool when it shouldn't. Symmetrically, this must always be checked before returning a\n   * connection from the pool.\n   *\n   * Once true this is always true. Guarded by this.\n   */\n  var noNewExchanges = false\n\n  /**\n   * If true, this connection may not be used for coalesced requests. These are requests that could\n   * share the same connection without sharing the same hostname.\n   */\n  private var noCoalescedConnections = false\n\n  /**\n   * The number of times there was a problem establishing a stream that could be due to route\n   * chosen. Guarded by this.\n   */\n  internal var routeFailureCount = 0\n\n  private var successCount = 0\n  private var refusedStreamCount = 0\n\n  /**\n   * The maximum number of concurrent streams that can be carried by this connection. If\n   * `allocations.size() < allocationLimit` then new streams can be created on this connection.\n   */\n  internal var allocationLimit = 1\n    private set\n\n  /** Current calls carried by this connection. */\n  val calls = mutableListOf<Reference<RealCall>>()\n\n  /** Timestamp when `allocations.size()` reached zero. Also assigned upon initial connection. */\n  var idleAtNs = Long.MAX_VALUE\n\n  /**\n   * Returns true if this is an HTTP/2 connection. Such connections can be used in multiple HTTP\n   * requests simultaneously.\n   */\n  internal val isMultiplexed: Boolean\n    get() = http2Connection != null\n\n  /** Prevent further exchanges from being created on this connection. */\n  override fun noNewExchanges() {\n    withLock {\n      noNewExchanges = true\n    }\n    connectionListener.noNewExchanges(this)\n  }\n\n  /** Prevent this connection from being used for hosts other than the one in [route]. */\n  internal fun noCoalescedConnections() {\n    withLock {\n      noCoalescedConnections = true\n    }\n  }\n\n  internal fun incrementSuccessCount() {\n    withLock {\n      successCount++\n    }\n  }\n\n  @Throws(IOException::class)\n  fun start() {\n    idleAtNs = System.nanoTime()\n    if (protocol == Protocol.HTTP_2 || protocol == Protocol.H2_PRIOR_KNOWLEDGE) {\n      startHttp2()\n    }\n  }\n\n  @Throws(IOException::class)\n  private fun startHttp2() {\n    javaNetSocket.soTimeout = 0 // HTTP/2 connection timeouts are set per-stream.\n    val flowControlListener = connectionListener as? FlowControlListener ?: FlowControlListener.None\n    val http2Connection =\n      Http2Connection\n        .Builder(client = true, taskRunner)\n        .socket(socket, route.address.url.host)\n        .listener(this)\n        .pingIntervalMillis(pingIntervalMillis)\n        .flowControlListener(flowControlListener)\n        .build()\n    this.http2Connection = http2Connection\n    this.allocationLimit = Http2Connection.DEFAULT_SETTINGS.getMaxConcurrentStreams()\n    http2Connection.start()\n  }\n\n  /**\n   * Returns true if this connection can carry a stream allocation to `address`. If non-null\n   * `route` is the resolved route for a connection.\n   */\n  internal fun isEligible(\n    address: Address,\n    routes: List<Route>?,\n  ): Boolean {\n    assertLockHeld()\n\n    // If this connection is not accepting new exchanges, we're done.\n    if (calls.size >= allocationLimit || noNewExchanges) return false\n\n    // If the non-host fields of the address don't overlap, we're done.\n    if (!this.route.address.equalsNonHost(address)) return false\n\n    // If the host exactly matches, we're done: this connection can carry the address.\n    if (address.url.host ==\n      this\n        .route()\n        .address.url.host\n    ) {\n      return true // This connection is a perfect match.\n    }\n\n    // At this point we don't have a hostname match. But we still be able to carry the request if\n    // our connection coalescing requirements are met. See also:\n    // https://hpbn.co/optimizing-application-delivery/#eliminate-domain-sharding\n    // https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/\n\n    // 1. This connection must be HTTP/2.\n    if (http2Connection == null) return false\n\n    // 2. The routes must share an IP address.\n    if (routes == null || !routeMatchesAny(routes)) return false\n\n    // 3. This connection's server certificates must cover the new host.\n    if (address.hostnameVerifier !== OkHostnameVerifier) return false\n    if (!supportsUrl(address.url)) return false\n\n    // 4. Certificate pinning must match the host.\n    try {\n      address.certificatePinner!!.check(address.url.host, handshake()!!.peerCertificates)\n    } catch (_: SSLPeerUnverifiedException) {\n      return false\n    }\n\n    return true // The caller's address can be carried by this connection.\n  }\n\n  /**\n   * Returns true if this connection's route has the same address as any of [candidates]. This\n   * requires us to have a DNS address for both hosts, which only happens after route planning. We\n   * can't coalesce connections that use a proxy, since proxies don't tell us the origin server's IP\n   * address.\n   */\n  private fun routeMatchesAny(candidates: List<Route>): Boolean =\n    candidates.any {\n      it.proxy.type() == Proxy.Type.DIRECT &&\n        route.proxy.type() == Proxy.Type.DIRECT &&\n        route.socketAddress == it.socketAddress\n    }\n\n  private fun supportsUrl(url: HttpUrl): Boolean {\n    assertLockHeld()\n\n    val routeUrl = route.address.url\n\n    if (url.port != routeUrl.port) {\n      return false // Port mismatch.\n    }\n\n    if (url.host == routeUrl.host) {\n      return true // Host match. The URL is supported.\n    }\n\n    // We have a host mismatch. But if the certificate matches, we're still good.\n    return !noCoalescedConnections && handshake != null && certificateSupportHost(url, handshake)\n  }\n\n  private fun certificateSupportHost(\n    url: HttpUrl,\n    handshake: Handshake,\n  ): Boolean {\n    val peerCertificates = handshake.peerCertificates\n\n    return peerCertificates.isNotEmpty() &&\n      OkHostnameVerifier.verify(url.host, peerCertificates[0] as X509Certificate)\n  }\n\n  @Throws(SocketException::class)\n  internal fun newCodec(\n    client: OkHttpClient,\n    chain: RealInterceptorChain,\n  ): ExchangeCodec {\n    val okHttpSocket = this.socket\n    val http2Connection = this.http2Connection\n\n    return if (http2Connection != null) {\n      Http2ExchangeCodec(client, this, chain, http2Connection)\n    } else {\n      javaNetSocket.soTimeout = chain.readTimeoutMillis()\n      okHttpSocket.source.timeout().timeout(chain.readTimeoutMillis.toLong(), MILLISECONDS)\n      okHttpSocket.sink.timeout().timeout(chain.writeTimeoutMillis.toLong(), MILLISECONDS)\n      Http1ExchangeCodec(client, this, okHttpSocket)\n    }\n  }\n\n  internal fun useAsSocket() {\n    javaNetSocket.soTimeout = 0\n    noNewExchanges()\n  }\n\n  override fun route(): Route = route\n\n  override fun cancel() {\n    // Close the raw socket so we don't end up doing synchronous I/O.\n    rawSocket.closeQuietly()\n  }\n\n  override fun socket(): JavaNetSocket = javaNetSocket\n\n  /** Returns true if this connection is ready to host new streams. */\n  fun isHealthy(doExtensiveChecks: Boolean): Boolean {\n    assertLockNotHeld()\n\n    val nowNs = System.nanoTime()\n\n    if (rawSocket.isClosed ||\n      javaNetSocket.isClosed ||\n      javaNetSocket.isInputShutdown ||\n      javaNetSocket.isOutputShutdown\n    ) {\n      return false\n    }\n\n    val http2Connection = this.http2Connection\n    if (http2Connection != null) {\n      return http2Connection.isHealthy(nowNs)\n    }\n\n    val idleDurationNs = withLock { nowNs - idleAtNs }\n    if (idleDurationNs >= IDLE_CONNECTION_HEALTHY_NS && doExtensiveChecks) {\n      return javaNetSocket.isHealthy(socket.source)\n    }\n\n    return true\n  }\n\n  /** Refuse incoming streams. */\n  @Throws(IOException::class)\n  override fun onStream(stream: Http2Stream) {\n    stream.close(ErrorCode.REFUSED_STREAM, null)\n  }\n\n  /** When settings are received, adjust the allocation limit. */\n  override fun onSettings(\n    connection: Http2Connection,\n    settings: Settings,\n  ) {\n    withLock {\n      allocationLimit = settings.getMaxConcurrentStreams()\n    }\n  }\n\n  override fun handshake(): Handshake? = handshake\n\n  /** Track a bad route in the route database. Other routes will be attempted first. */\n  internal fun connectFailed(\n    client: OkHttpClient,\n    failedRoute: Route,\n    failure: IOException,\n  ) {\n    // Tell the proxy selector when we fail to connect on a fresh connection.\n    if (failedRoute.proxy.type() != Proxy.Type.DIRECT) {\n      val address = failedRoute.address\n      address.proxySelector.connectFailed(\n        address.url.toUri(),\n        failedRoute.proxy.address(),\n        failure,\n      )\n    }\n\n    client.routeDatabase.failed(failedRoute)\n  }\n\n  /**\n   * Track a failure using this connection. This may prevent both the connection and its route from\n   * being used for future exchanges.\n   */\n  override fun trackFailure(\n    call: RealCall,\n    e: IOException?,\n  ) {\n    var noNewExchangesEvent = false\n    withLock {\n      if (e is StreamResetException) {\n        when {\n          e.errorCode == ErrorCode.REFUSED_STREAM -> {\n            // Stop using this connection on the 2nd REFUSED_STREAM error.\n            refusedStreamCount++\n            if (refusedStreamCount > 1) {\n              noNewExchangesEvent = !noNewExchanges\n              noNewExchanges = true\n              routeFailureCount++\n            }\n          }\n\n          e.errorCode == ErrorCode.CANCEL && call.isCanceled() -> {\n            // Permit any number of CANCEL errors on locally-canceled calls.\n          }\n\n          else -> {\n            // Everything else wants a fresh connection.\n            noNewExchangesEvent = !noNewExchanges\n            noNewExchanges = true\n            routeFailureCount++\n          }\n        }\n      } else if (!isMultiplexed || e is ConnectionShutdownException) {\n        noNewExchangesEvent = !noNewExchanges\n        noNewExchanges = true\n\n        // If this route hasn't completed a call, avoid it for new connections.\n        if (successCount == 0) {\n          if (e != null) {\n            connectFailed(call.client, route, e)\n          }\n          routeFailureCount++\n        }\n      }\n\n      Unit\n    }\n\n    if (noNewExchangesEvent) {\n      connectionListener.noNewExchanges(this)\n    }\n  }\n\n  override fun protocol(): Protocol = protocol\n\n  override fun toString(): String =\n    \"Connection{${route.address.url.host}:${route.address.url.port},\" +\n      \" proxy=${route.proxy}\" +\n      \" hostAddress=${route.socketAddress}\" +\n      \" cipherSuite=${handshake?.cipherSuite ?: \"none\"}\" +\n      \" protocol=$protocol}\"\n\n  companion object {\n    const val IDLE_CONNECTION_HEALTHY_NS = 10_000_000_000 // 10 seconds.\n\n    fun newTestConnection(\n      taskRunner: TaskRunner,\n      connectionPool: RealConnectionPool,\n      route: Route,\n      socket: JavaNetSocket,\n      idleAtNs: Long,\n    ): RealConnection {\n      val bufferedSocket =\n        object : BufferedSocket {\n          override val sink = Buffer()\n          override val source = Buffer()\n\n          override fun cancel() {\n          }\n        }\n\n      val result =\n        RealConnection(\n          taskRunner = taskRunner,\n          connectionPool = connectionPool,\n          route = route,\n          rawSocket = JavaNetSocket(),\n          javaNetSocket = socket,\n          handshake = null,\n          protocol = Protocol.HTTP_2,\n          socket = bufferedSocket,\n          pingIntervalMillis = 0,\n          connectionListener = ConnectionListener.NONE,\n        )\n      result.idleAtNs = idleAtNs\n      return result\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RealConnectionPool.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.net.Socket\nimport java.util.concurrent.ConcurrentLinkedQueue\nimport java.util.concurrent.TimeUnit\nimport okhttp3.Address\nimport okhttp3.ConnectionPool\nimport okhttp3.Route\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.Task\nimport okhttp3.internal.concurrent.TaskQueue\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.assertLockHeld\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.connection.RealCall.CallReference\nimport okhttp3.internal.okHttpName\nimport okhttp3.internal.platform.Platform\n\nclass RealConnectionPool internal constructor(\n  taskRunner: TaskRunner,\n  /** The maximum number of idle connections across all addresses. */\n  private val maxIdleConnections: Int,\n  keepAliveDuration: Long,\n  timeUnit: TimeUnit,\n  internal val connectionListener: ConnectionListener,\n) {\n  internal val keepAliveDurationNs: Long = timeUnit.toNanos(keepAliveDuration)\n\n  private val cleanupQueue: TaskQueue = taskRunner.newQueue()\n  private val cleanupTask =\n    object : Task(\"$okHttpName ConnectionPool connection closer\") {\n      override fun runOnce(): Long = closeConnections(System.nanoTime())\n    }\n\n  /**\n   * Holding the lock of the connection being added or removed when mutating this, and check its\n   * [RealConnection.noNewExchanges] property. This defends against races where a connection is\n   * simultaneously adopted and removed.\n   */\n  private val connections = ConcurrentLinkedQueue<RealConnection>()\n\n  init {\n    // Put a floor on the keep alive duration, otherwise cleanup will spin loop.\n    require(keepAliveDuration > 0L) { \"keepAliveDuration <= 0: $keepAliveDuration\" }\n  }\n\n  fun idleConnectionCount(): Int =\n    connections.count {\n      it.withLock { it.calls.isEmpty() }\n    }\n\n  fun connectionCount(): Int = connections.size\n\n  /**\n   * Attempts to acquire a recycled connection to [address] for [call]. Returns the connection if it\n   * was acquired, or null if no connection was acquired. The acquired connection will also be\n   * given to [call] who may (for example) assign it to a [RealCall.connection].\n   *\n   * This confirms the returned connection is healthy before returning it. If this encounters any\n   * unhealthy connections in its search, this will clean them up.\n   *\n   * If [routes] is non-null these are the resolved routes (ie. IP addresses) for the connection.\n   * This is used to coalesce related domains to the same HTTP/2 connection, such as `square.com`\n   * and `square.ca`.\n   */\n  internal fun callAcquirePooledConnection(\n    doExtensiveHealthChecks: Boolean,\n    address: Address,\n    call: RealCall,\n    routes: List<Route>?,\n    requireMultiplexed: Boolean,\n  ): RealConnection? {\n    for (connection in connections) {\n      // In the first synchronized block, acquire the connection if it can satisfy this call.\n      val acquired =\n        connection.withLock {\n          when {\n            requireMultiplexed && !connection.isMultiplexed -> {\n              false\n            }\n\n            !connection.isEligible(address, routes) -> {\n              false\n            }\n\n            else -> {\n              call.acquireConnectionNoEvents(connection)\n              true\n            }\n          }\n        }\n      if (!acquired) continue\n\n      // Confirm the connection is healthy and return it.\n      if (connection.isHealthy(doExtensiveHealthChecks)) return connection\n\n      // In the second synchronized block, release the unhealthy acquired connection. We're also on\n      // the hook to close this connection if it's no longer in use.\n      val noNewExchangesEvent: Boolean\n      val toClose: Socket? =\n        connection.withLock {\n          noNewExchangesEvent = !connection.noNewExchanges\n          connection.noNewExchanges = true\n          call.releaseConnectionNoEvents()\n        }\n      if (toClose != null) {\n        toClose.closeQuietly()\n        connectionListener.connectionClosed(connection)\n      } else if (noNewExchangesEvent) {\n        connectionListener.noNewExchanges(connection)\n      }\n    }\n    return null\n  }\n\n  fun put(connection: RealConnection) {\n    connection.assertLockHeld()\n\n    connections.add(connection)\n//    connection.queueEvent { connectionListener.connectEnd(connection) }\n    scheduleCloser()\n  }\n\n  /**\n   * Notify this pool that [connection] has become idle. Returns true if the connection has been\n   * removed from the pool and should be closed.\n   */\n  fun connectionBecameIdle(connection: RealConnection): Boolean {\n    connection.assertLockHeld()\n\n    return if (connection.noNewExchanges || maxIdleConnections == 0) {\n      connection.noNewExchanges = true\n      connections.remove(connection)\n      if (connections.isEmpty()) cleanupQueue.cancelAll()\n      true\n    } else {\n      scheduleCloser()\n      false\n    }\n  }\n\n  fun evictAll() {\n    val i = connections.iterator()\n    while (i.hasNext()) {\n      val connection = i.next()\n      val socketToClose =\n        connection.withLock {\n          if (connection.calls.isEmpty()) {\n            i.remove()\n            connection.noNewExchanges = true\n            return@withLock connection.socket()\n          } else {\n            return@withLock null\n          }\n        }\n      if (socketToClose != null) {\n        socketToClose.closeQuietly()\n        connectionListener.connectionClosed(connection)\n      }\n    }\n\n    if (connections.isEmpty()) cleanupQueue.cancelAll()\n  }\n\n  /**\n   * Performs maintenance on this pool, evicting the connection that has been idle the longest if\n   * either it has exceeded the keep alive limit or the idle connections limit.\n   *\n   * Returns the duration in nanoseconds to sleep until the next scheduled call to this method.\n   * Returns -1 if no further cleanups are required.\n   */\n  fun closeConnections(now: Long): Long {\n    // Find the longest-idle connections in 2 categories:\n    //\n    //  1. OLD: Connections that have been idle for at least keepAliveDurationNs. We close these if\n    //     we find them, regardless of what the address policies need.\n    //\n    //  2. EVICTABLE: Connections not required by any address policy. This matches connections that\n    //     don't participate in any policy, plus connections whose policies won't be violated if the\n    //     connection is closed. We only close these if the idle connection limit is exceeded.\n    //\n    // Also count the evictable connections to find out if we must close an EVICTABLE connection\n    // before its keepAliveDurationNs is reached.\n    var earliestOldIdleAtNs = (now - keepAliveDurationNs) + 1\n    var earliestOldConnection: RealConnection? = null\n    var earliestEvictableIdleAtNs = Long.MAX_VALUE\n    var earliestEvictableConnection: RealConnection? = null\n    var inUseConnectionCount = 0\n    var evictableConnectionCount = 0\n    for (connection in connections) {\n      connection.withLock {\n        // If the connection is in use, keep searching.\n        if (pruneAndGetAllocationCount(connection, now) > 0) {\n          inUseConnectionCount++\n          return@withLock\n        }\n\n        val idleAtNs = connection.idleAtNs\n\n        if (idleAtNs < earliestOldIdleAtNs) {\n          earliestOldIdleAtNs = idleAtNs\n          earliestOldConnection = connection\n        }\n\n        evictableConnectionCount++\n        if (idleAtNs < earliestEvictableIdleAtNs) {\n          earliestEvictableIdleAtNs = idleAtNs\n          earliestEvictableConnection = connection\n        }\n      }\n    }\n\n    val toEvict: RealConnection?\n    val toEvictIdleAtNs: Long\n    when {\n      // We had at least one OLD connection. Close the oldest one.\n      earliestOldConnection != null -> {\n        toEvict = earliestOldConnection\n        toEvictIdleAtNs = earliestOldIdleAtNs\n      }\n\n      // We have too many EVICTABLE connections. Close the oldest one.\n      evictableConnectionCount > maxIdleConnections -> {\n        toEvict = earliestEvictableConnection\n        toEvictIdleAtNs = earliestEvictableIdleAtNs\n      }\n\n      else -> {\n        toEvict = null\n        toEvictIdleAtNs = -1L\n      }\n    }\n\n    when {\n      toEvict != null -> {\n        // We've chosen a connection to evict. Confirm it's still okay to be evicted, then close it.\n        toEvict.withLock {\n          if (toEvict.calls.isNotEmpty()) return 0L // No longer idle.\n          if (toEvict.idleAtNs != toEvictIdleAtNs) return 0L // No longer oldest.\n          toEvict.noNewExchanges = true\n          connections.remove(toEvict)\n        }\n        toEvict.socket().closeQuietly()\n        connectionListener.connectionClosed(toEvict)\n        if (connections.isEmpty()) cleanupQueue.cancelAll()\n\n        // Clean up again immediately.\n        return 0L\n      }\n\n      earliestEvictableConnection != null -> {\n        // A connection will be ready to evict soon.\n        return earliestEvictableIdleAtNs + keepAliveDurationNs - now\n      }\n\n      inUseConnectionCount > 0 -> {\n        // All connections are in use. It'll be at least the keep alive duration 'til we run again.\n        return keepAliveDurationNs\n      }\n\n      else -> {\n        // No connections, idle or in use.\n        return -1\n      }\n    }\n  }\n\n  /**\n   * Prunes any leaked calls and then returns the number of remaining live calls on [connection].\n   * Calls are leaked if the connection is tracking them but the application code has abandoned\n   * them. Leak detection is imprecise and relies on garbage collection.\n   */\n  private fun pruneAndGetAllocationCount(\n    connection: RealConnection,\n    now: Long,\n  ): Int {\n    connection.assertLockHeld()\n\n    val references = connection.calls\n    var i = 0\n    while (i < references.size) {\n      val reference = references[i]\n\n      if (reference.get() != null) {\n        i++\n        continue\n      }\n\n      // We've discovered a leaked call. This is an application bug.\n      val callReference = reference as CallReference\n      val message =\n        \"A connection to ${connection.route().address.url} was leaked. \" +\n          \"Did you forget to close a response body?\"\n      Platform.get().logCloseableLeak(message, callReference.callStackTrace)\n\n      references.removeAt(i)\n\n      // If this was the last allocation, the connection is eligible for immediate eviction.\n      if (references.isEmpty()) {\n        connection.idleAtNs = now - keepAliveDurationNs\n        return 0\n      }\n    }\n\n    return references.size\n  }\n\n  fun scheduleCloser() {\n    cleanupQueue.schedule(cleanupTask)\n  }\n\n  companion object {\n    fun get(connectionPool: ConnectionPool): RealConnectionPool = connectionPool.delegate\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RealRoutePlanner.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport java.net.HttpURLConnection\nimport java.net.Socket\nimport java.net.UnknownServiceException\nimport okhttp3.Address\nimport okhttp3.ConnectionSpec\nimport okhttp3.HttpUrl\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.Route\nimport okhttp3.internal.USER_AGENT\nimport okhttp3.internal.canReuseConnectionFor\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.connection.RoutePlanner.Plan\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.toHostHeader\n\nclass RealRoutePlanner internal constructor(\n  private val taskRunner: TaskRunner,\n  private val connectionPool: RealConnectionPool,\n  private val readTimeoutMillis: Int,\n  private val writeTimeoutMillis: Int,\n  private val socketConnectTimeoutMillis: Int,\n  private val socketReadTimeoutMillis: Int,\n  private val pingIntervalMillis: Int,\n  private val retryOnConnectionFailure: Boolean,\n  private val fastFallback: Boolean,\n  override val address: Address,\n  private val routeDatabase: RouteDatabase,\n  private val call: RealCall,\n  request: Request,\n) : RoutePlanner {\n  private val doExtensiveHealthChecks = request.method != \"GET\"\n  private var routeSelection: RouteSelector.Selection? = null\n  private var routeSelector: RouteSelector? = null\n  private var nextRouteToTry: Route? = null\n\n  override val deferredPlans = ArrayDeque<Plan>()\n\n  override fun isCanceled(): Boolean = call.isCanceled()\n\n  @Throws(IOException::class)\n  override fun plan(): Plan {\n    val reuseCallConnection = planReuseCallConnection()\n    if (reuseCallConnection != null) return reuseCallConnection\n\n    // Attempt to get a connection from the pool.\n    val pooled1 = planReusePooledConnection()\n    if (pooled1 != null) return pooled1\n\n    // Attempt a deferred plan before new routes.\n    if (deferredPlans.isNotEmpty()) return deferredPlans.removeFirst()\n\n    // Do blocking calls to plan a route for a new connection.\n    val connect = planConnect()\n\n    // Now that we have a set of IP addresses, make another attempt at getting a connection from\n    // the pool. We have a better chance of matching thanks to connection coalescing.\n    val pooled2 = planReusePooledConnection(connect, connect.routes)\n    if (pooled2 != null) return pooled2\n\n    return connect\n  }\n\n  /**\n   * Returns the connection already attached to the call if it's eligible for a new exchange.\n   *\n   * If the call's connection exists and is eligible for another exchange, it is returned. If it\n   * exists but cannot be used for another exchange, it is closed and this returns null.\n   */\n  private fun planReuseCallConnection(): ReusePlan? {\n    // This may be mutated by releaseConnectionNoEvents()!\n    val candidate = call.connection ?: return null\n\n    // Make sure this connection is healthy & eligible for new exchanges. If it's no longer needed\n    // then we're on the hook to close it.\n    val healthy = candidate.isHealthy(doExtensiveHealthChecks)\n    var noNewExchangesEvent = false\n    val toClose: Socket? =\n      candidate.withLock {\n        when {\n          !healthy -> {\n            noNewExchangesEvent = !candidate.noNewExchanges\n            candidate.noNewExchanges = true\n            call.releaseConnectionNoEvents()\n          }\n\n          candidate.noNewExchanges || !sameHostAndPort(candidate.route().address.url) -> {\n            call.releaseConnectionNoEvents()\n          }\n\n          else -> {\n            null\n          }\n        }\n      }\n\n    // If the call's connection wasn't released, reuse it. We don't call connectionAcquired() here\n    // because we already acquired it.\n    if (call.connection != null) {\n      check(toClose == null)\n      return ReusePlan(candidate)\n    }\n\n    // The call's connection was released.\n    toClose?.closeQuietly()\n    call.eventListener.connectionReleased(call, candidate)\n    candidate.connectionListener.connectionReleased(candidate, call)\n    if (toClose != null) {\n      candidate.connectionListener.connectionClosed(candidate)\n    } else if (noNewExchangesEvent) {\n      candidate.connectionListener.noNewExchanges(candidate)\n    }\n    return null\n  }\n\n  /** Plans to make a new connection by deciding which route to try next. */\n  @Throws(IOException::class)\n  internal fun planConnect(): ConnectPlan {\n    // Use a route from a preceding coalesced connection.\n    val localNextRouteToTry = nextRouteToTry\n    if (localNextRouteToTry != null) {\n      nextRouteToTry = null\n      return planConnectToRoute(localNextRouteToTry)\n    }\n\n    // Use a route from an existing route selection.\n    val existingRouteSelection = routeSelection\n    if (existingRouteSelection != null && existingRouteSelection.hasNext()) {\n      return planConnectToRoute(existingRouteSelection.next())\n    }\n\n    // Decide which proxy to use, if any. This may block in ProxySelector.select().\n    var newRouteSelector = routeSelector\n    if (newRouteSelector == null) {\n      newRouteSelector =\n        RouteSelector(\n          address = address,\n          routeDatabase = routeDatabase,\n          call = call,\n          fastFallback = fastFallback,\n        )\n      routeSelector = newRouteSelector\n    }\n\n    // List available IP addresses for the current proxy. This may block in Dns.lookup().\n    if (!newRouteSelector.hasNext()) throw IOException(\"exhausted all routes\")\n    val newRouteSelection = newRouteSelector.next()\n    routeSelection = newRouteSelection\n\n    if (isCanceled()) throw IOException(\"Canceled\")\n\n    return planConnectToRoute(newRouteSelection.next(), newRouteSelection.routes)\n  }\n\n  /**\n   * Returns a plan to reuse a pooled connection, or null if the pool doesn't have a connection for\n   * this address.\n   *\n   * If [planToReplace] is non-null, this will swap it for a pooled connection if that pooled\n   * connection uses HTTP/2. That results in fewer sockets overall and thus fewer TCP slow starts.\n   */\n  internal fun planReusePooledConnection(\n    planToReplace: ConnectPlan? = null,\n    routes: List<Route>? = null,\n  ): ReusePlan? {\n    val result =\n      connectionPool.callAcquirePooledConnection(\n        doExtensiveHealthChecks = doExtensiveHealthChecks,\n        address = address,\n        call = call,\n        routes = routes,\n        requireMultiplexed = planToReplace != null && planToReplace.isReady,\n      ) ?: return null\n\n    // If we coalesced our connection, remember the replaced connection's route. That way if the\n    // coalesced connection later fails we don't waste a valid route.\n    if (planToReplace != null) {\n      nextRouteToTry = planToReplace.route\n      planToReplace.closeQuietly()\n    }\n\n    call.eventListener.connectionAcquired(call, result)\n    result.connectionListener.connectionAcquired(result, call)\n    return ReusePlan(result)\n  }\n\n  /** Returns a plan for the first attempt at [route]. This throws if no plan is possible. */\n  @Throws(IOException::class)\n  internal fun planConnectToRoute(\n    route: Route,\n    routes: List<Route>? = null,\n  ): ConnectPlan {\n    if (route.address.sslSocketFactory == null) {\n      if (ConnectionSpec.CLEARTEXT !in route.address.connectionSpecs) {\n        throw UnknownServiceException(\"CLEARTEXT communication not enabled for client\")\n      }\n\n      val host = route.address.url.host\n      if (!Platform.get().isCleartextTrafficPermitted(host)) {\n        throw UnknownServiceException(\n          \"CLEARTEXT communication to $host not permitted by network security policy\",\n        )\n      }\n    } else {\n      if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {\n        throw UnknownServiceException(\"H2_PRIOR_KNOWLEDGE cannot be used with HTTPS\")\n      }\n    }\n\n    val tunnelRequest =\n      when {\n        route.requiresTunnel() -> createTunnelRequest(route)\n        else -> null\n      }\n\n    return ConnectPlan(\n      taskRunner = taskRunner,\n      connectionPool = connectionPool,\n      readTimeoutMillis = readTimeoutMillis,\n      writeTimeoutMillis = writeTimeoutMillis,\n      socketConnectTimeoutMillis = socketConnectTimeoutMillis,\n      socketReadTimeoutMillis = socketReadTimeoutMillis,\n      pingIntervalMillis = pingIntervalMillis,\n      retryOnConnectionFailure = retryOnConnectionFailure,\n      call = call,\n      routePlanner = this,\n      route = route,\n      routes = routes,\n      attempt = 0,\n      tunnelRequest = tunnelRequest,\n      connectionSpecIndex = -1,\n      isTlsFallback = false,\n    )\n  }\n\n  /**\n   * Returns a request that creates a TLS tunnel via an HTTP proxy. Everything in the tunnel request\n   * is sent unencrypted to the proxy server, so tunnels include only the minimum set of headers.\n   * This avoids sending potentially sensitive data like HTTP cookies to the proxy unencrypted.\n   *\n   * In order to support preemptive authentication we pass a fake \"Auth Failed\" response to the\n   * authenticator. This gives the authenticator the option to customize the CONNECT request. It can\n   * decline to do so by returning null, in which case OkHttp will use it as-is.\n   */\n  @Throws(IOException::class)\n  private fun createTunnelRequest(route: Route): Request {\n    val proxyConnectRequest =\n      Request\n        .Builder()\n        .url(route.address.url)\n        .method(\"CONNECT\", null)\n        .header(\"Host\", route.address.url.toHostHeader(includeDefaultPort = true))\n        .header(\"Proxy-Connection\", \"Keep-Alive\") // For HTTP/1.0 proxies like Squid.\n        .header(\"User-Agent\", USER_AGENT)\n        .build()\n\n    val fakeAuthChallengeResponse =\n      Response\n        .Builder()\n        .request(proxyConnectRequest)\n        .protocol(Protocol.HTTP_1_1)\n        .code(HttpURLConnection.HTTP_PROXY_AUTH)\n        .message(\"Preemptive Authenticate\")\n        .sentRequestAtMillis(-1L)\n        .receivedResponseAtMillis(-1L)\n        .header(\"Proxy-Authenticate\", \"OkHttp-Preemptive\")\n        .build()\n\n    val authenticatedRequest =\n      route.address.proxyAuthenticator\n        .authenticate(route, fakeAuthChallengeResponse)\n\n    return authenticatedRequest ?: proxyConnectRequest\n  }\n\n  override fun hasNext(failedConnection: RealConnection?): Boolean {\n    if (deferredPlans.isNotEmpty()) {\n      return true\n    }\n\n    if (nextRouteToTry != null) {\n      return true\n    }\n\n    if (failedConnection != null) {\n      val retryRoute = retryRoute(failedConnection)\n      if (retryRoute != null) {\n        // Lock in the route because retryRoute() is racy and we don't want to call it twice.\n        nextRouteToTry = retryRoute\n        return true\n      }\n    }\n\n    // If we have a routes left, use 'em.\n    if (routeSelection?.hasNext() == true) return true\n\n    // If we haven't initialized the route selector yet, assume it'll have at least one route.\n    val localRouteSelector = routeSelector ?: return true\n\n    // If we do have a route selector, use its routes.\n    return localRouteSelector.hasNext()\n  }\n\n  /**\n   * Return the route from [connection] if it should be retried, even if the connection itself is\n   * unhealthy. The biggest gotcha here is that we shouldn't reuse routes from coalesced\n   * connections.\n   */\n  private fun retryRoute(connection: RealConnection): Route? =\n    connection.withLock {\n      when {\n        connection.routeFailureCount != 0 -> null\n\n        // This route is still in use.\n        !connection.noNewExchanges -> null\n\n        !connection\n          .route()\n          .address.url\n          .canReuseConnectionFor(address.url) -> null\n\n        else -> connection.route()\n      }\n    }\n\n  override fun sameHostAndPort(url: HttpUrl): Boolean {\n    val routeUrl = address.url\n    return url.port == routeUrl.port && url.host == routeUrl.host\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RetryTlsHandshake.kt",
    "content": "/*\n * Copyright (C) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.InterruptedIOException\nimport java.net.ProtocolException\nimport java.security.cert.CertificateException\nimport javax.net.ssl.SSLException\nimport javax.net.ssl.SSLHandshakeException\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport okio.IOException\n\n/** Returns true if a TLS connection should be retried after [e]. */\nfun retryTlsHandshake(e: IOException): Boolean =\n  when {\n    // If there was a protocol problem, don't recover.\n    e is ProtocolException -> false\n\n    // If there was an interruption or timeout (SocketTimeoutException), don't recover.\n    // For the socket connect timeout case we do not try the same host with a different\n    // ConnectionSpec: we assume it is unreachable.\n    e is InterruptedIOException -> false\n\n    // If the problem was a CertificateException from the X509TrustManager, do not retry.\n    e is SSLHandshakeException && e.cause is CertificateException -> false\n\n    // e.g. a certificate pinning error.\n    e is SSLPeerUnverifiedException -> false\n\n    // Retry for all other SSL failures.\n    e is SSLException -> true\n\n    else -> false\n  }\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/ReusePlan.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\n/** Reuse a connection from the pool. */\ninternal class ReusePlan(\n  val connection: RealConnection,\n) : RoutePlanner.Plan {\n  override val isReady = true\n\n  override fun connectTcp() = error(\"already connected\")\n\n  override fun connectTlsEtc() = error(\"already connected\")\n\n  override fun handleSuccess() = connection\n\n  override fun cancel() = error(\"unexpected cancel\")\n\n  override fun retry() = error(\"unexpected retry\")\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RouteDatabase.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport okhttp3.Route\n\n/**\n * A denylist of failed routes to avoid when creating a new connection to a target address. This is\n * used so that OkHttp can learn from its mistakes: if there was a failure attempting to connect to\n * a specific IP address or proxy server, that failure is remembered and alternate routes are\n * preferred.\n */\nclass RouteDatabase {\n  private val _failedRoutes = mutableSetOf<Route>()\n\n  val failedRoutes: Set<Route>\n    @Synchronized get() = _failedRoutes.toSet()\n\n  /** Records a failure connecting to [failedRoute]. */\n  @Synchronized fun failed(failedRoute: Route) {\n    _failedRoutes.add(failedRoute)\n  }\n\n  /** Records success connecting to [route]. */\n  @Synchronized fun connected(route: Route) {\n    _failedRoutes.remove(route)\n  }\n\n  /** Returns true if [route] has failed recently and should be avoided. */\n  @Synchronized fun shouldPostpone(route: Route): Boolean = route in _failedRoutes\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RoutePlanner.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport okhttp3.Address\nimport okhttp3.HttpUrl\n\n/**\n * Policy on choosing which connection to use for an exchange and any retries that follow. This uses\n * the following strategies:\n *\n *  1. If the current call already has a connection that can satisfy the request it is used. Using\n *     the same connection for an initial exchange and its follow-ups may improve locality.\n *\n *  2. If there is a connection in the pool that can satisfy the request it is used. Note that it is\n *     possible for shared exchanges to make requests to different host names! See\n *     [RealConnection.isEligible] for details.\n *\n *  3. Attempt plans from prior connect attempts for this call. These occur as either follow-ups to\n *     failed connect attempts (such as trying the next [ConnectionSpec]), or as attempts that lost\n *     a race in fast follow-up.\n *\n *  4. If there's no existing connection, make a list of routes (which may require blocking DNS\n *     lookups) and attempt new connections to them. When failures occur, retries iterate the\n *     list of available routes.\n *\n * If the pool gains an eligible connection while DNS, TCP, or TLS work is in flight, this finder\n * will prefer pooled connections. Only pooled HTTP/2 connections are used for such de-duplication.\n *\n * It is possible to cancel the finding process by canceling its call.\n *\n * Implementations of this interface are not thread-safe. Each instance is thread-confined to the\n * thread executing the call.\n */\ninterface RoutePlanner {\n  val address: Address\n\n  /** Follow-ups for failed plans and plans that lost a race. */\n  val deferredPlans: ArrayDeque<Plan>\n\n  fun isCanceled(): Boolean\n\n  /** Returns a plan to attempt. */\n  @Throws(IOException::class)\n  fun plan(): Plan\n\n  /**\n   * Returns true if there's more route plans to try.\n   *\n   * @param failedConnection an optional connection that was resulted in a failure. If the failure\n   *     is recoverable, the connection's route may be recovered for the retry.\n   */\n  fun hasNext(failedConnection: RealConnection? = null): Boolean\n\n  /**\n   * Returns true if the host and port are unchanged from when this was created. This is used to\n   * detect if followups need to do a full connection-finding process including DNS resolution, and\n   * certificate pin checks.\n   */\n  fun sameHostAndPort(url: HttpUrl): Boolean\n\n  /**\n   * A plan holds either an immediately-usable connection, or one that must be connected first.\n   * These steps are split so callers can call [connectTcp] on a background thread if attempting\n   * multiple plans concurrently.\n   */\n  interface Plan {\n    val isReady: Boolean\n\n    fun connectTcp(): ConnectResult\n\n    fun connectTlsEtc(): ConnectResult\n\n    fun handleSuccess(): RealConnection\n\n    fun cancel()\n\n    /**\n     * Returns a plan to attempt if canceling this plan was a mistake! The returned plan is not\n     * canceled, even if this plan is canceled.\n     */\n    fun retry(): Plan?\n  }\n\n  /**\n   * What to do once a plan has executed.\n   *\n   * If [nextPlan] is not-null, another attempt should be made by following it. If [throwable] is\n   * non-null, it should be reported to the user should all further attempts fail.\n   *\n   * The two values are independent: results can contain both (recoverable error), neither\n   * (success), just an exception (permanent failure), or just a plan (non-exceptional retry).\n   */\n  data class ConnectResult(\n    val plan: Plan,\n    val nextPlan: Plan? = null,\n    val throwable: Throwable? = null,\n  ) {\n    val isSuccess: Boolean\n      get() = nextPlan == null && throwable == null\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RouteSelector.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.SocketException\nimport java.net.UnknownHostException\nimport okhttp3.Address\nimport okhttp3.HttpUrl\nimport okhttp3.Route\nimport okhttp3.internal.canParseAsIpAddress\nimport okhttp3.internal.immutableListOf\nimport okhttp3.internal.toImmutableList\n\n/**\n * Selects routes to connect to an origin server. Each connection requires a choice of proxy server,\n * IP address, and TLS mode. Connections may also be recycled.\n */\nclass RouteSelector internal constructor(\n  private val address: Address,\n  private val routeDatabase: RouteDatabase,\n  private val call: RealCall,\n  private val fastFallback: Boolean,\n) {\n  // State for negotiating the next proxy to use.\n  private var proxies = emptyList<Proxy>()\n  private var nextProxyIndex: Int = 0\n\n  // State for negotiating the next socket address to use.\n  private var inetSocketAddresses = emptyList<InetSocketAddress>()\n\n  // State for negotiating failed routes\n  private val postponedRoutes = mutableListOf<Route>()\n\n  init {\n    resetNextProxy(address.url, address.proxy)\n  }\n\n  /**\n   * Returns true if there's another set of routes to attempt. Every address has at least one route.\n   */\n  operator fun hasNext(): Boolean = hasNextProxy() || postponedRoutes.isNotEmpty()\n\n  @Throws(IOException::class)\n  operator fun next(): Selection {\n    if (!hasNext()) throw NoSuchElementException()\n\n    // Compute the next set of routes to attempt.\n    val routes = mutableListOf<Route>()\n    while (hasNextProxy()) {\n      // Postponed routes are always tried last. For example, if we have 2 proxies and all the\n      // routes for proxy1 should be postponed, we'll move to proxy2. Only after we've exhausted\n      // all the good routes will we attempt the postponed routes.\n      val proxy = nextProxy()\n      for (inetSocketAddress in inetSocketAddresses) {\n        val route = Route(address, proxy, inetSocketAddress)\n        if (routeDatabase.shouldPostpone(route)) {\n          postponedRoutes += route\n        } else {\n          routes += route\n        }\n      }\n\n      if (routes.isNotEmpty()) {\n        break\n      }\n    }\n\n    if (routes.isEmpty()) {\n      // We've exhausted all Proxies so fallback to the postponed routes.\n      routes += postponedRoutes\n      postponedRoutes.clear()\n    }\n\n    return Selection(routes)\n  }\n\n  /** Prepares the proxy servers to try. */\n  private fun resetNextProxy(\n    url: HttpUrl,\n    proxy: Proxy?,\n  ) {\n    fun selectProxies(): List<Proxy> {\n      // If the user specifies a proxy, try that and only that.\n      if (proxy != null) return listOf(proxy)\n\n      // If the URI lacks a host (as in \"http://</\"), don't call the ProxySelector.\n      val uri = url.toUri()\n      if (uri.host == null) return immutableListOf(Proxy.NO_PROXY)\n\n      // Try each of the ProxySelector choices until one connection succeeds.\n      val proxiesOrNull = address.proxySelector.select(uri)\n      if (proxiesOrNull.isNullOrEmpty()) return immutableListOf(Proxy.NO_PROXY)\n\n      return proxiesOrNull.toImmutableList()\n    }\n\n    call.eventListener.proxySelectStart(call, url)\n    proxies = selectProxies()\n    nextProxyIndex = 0\n    call.eventListener.proxySelectEnd(call, url, proxies)\n  }\n\n  /** Returns true if there's another proxy to try. */\n  private fun hasNextProxy(): Boolean = nextProxyIndex < proxies.size\n\n  /** Returns the next proxy to try. May be PROXY.NO_PROXY but never null. */\n  @Throws(IOException::class)\n  private fun nextProxy(): Proxy {\n    if (!hasNextProxy()) {\n      throw SocketException(\n        \"No route to ${address.url.host}; exhausted proxy configurations: $proxies\",\n      )\n    }\n    val result = proxies[nextProxyIndex++]\n    resetNextInetSocketAddress(result)\n    return result\n  }\n\n  /** Prepares the socket addresses to attempt for the current proxy or host. */\n  @Throws(IOException::class)\n  private fun resetNextInetSocketAddress(proxy: Proxy) {\n    // Clear the addresses. Necessary if getAllByName() below throws!\n    val mutableInetSocketAddresses = mutableListOf<InetSocketAddress>()\n    inetSocketAddresses = mutableInetSocketAddresses\n\n    val socketHost: String\n    val socketPort: Int\n    if (proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.SOCKS) {\n      socketHost = address.url.host\n      socketPort = address.url.port\n    } else {\n      val proxyAddress = proxy.address()\n      require(proxyAddress is InetSocketAddress) {\n        \"Proxy.address() is not an InetSocketAddress: ${proxyAddress.javaClass}\"\n      }\n      socketHost = proxyAddress.socketHost\n      socketPort = proxyAddress.port\n    }\n\n    if (socketPort !in 1..65535) {\n      throw SocketException(\"No route to $socketHost:$socketPort; port is out of range\")\n    }\n\n    if (proxy.type() == Proxy.Type.SOCKS) {\n      mutableInetSocketAddresses += InetSocketAddress.createUnresolved(socketHost, socketPort)\n    } else {\n      val addresses =\n        if (socketHost.canParseAsIpAddress()) {\n          listOf(InetAddress.getByName(socketHost))\n        } else {\n          call.eventListener.dnsStart(call, socketHost)\n\n          val result = address.dns.lookup(socketHost)\n          if (result.isEmpty()) {\n            throw UnknownHostException(\"${address.dns} returned no addresses for $socketHost\")\n          }\n\n          call.eventListener.dnsEnd(call, socketHost, result)\n          result\n        }\n\n      // Try each address for best behavior in mixed IPv4/IPv6 environments.\n      val orderedAddresses =\n        when {\n          fastFallback -> reorderForHappyEyeballs(addresses)\n          else -> addresses\n        }\n\n      for (inetAddress in orderedAddresses) {\n        mutableInetSocketAddresses += InetSocketAddress(inetAddress, socketPort)\n      }\n    }\n  }\n\n  /** A set of selected Routes. */\n  class Selection(\n    val routes: List<Route>,\n  ) {\n    private var nextRouteIndex = 0\n\n    operator fun hasNext(): Boolean = nextRouteIndex < routes.size\n\n    operator fun next(): Route {\n      if (!hasNext()) throw NoSuchElementException()\n      return routes[nextRouteIndex++]\n    }\n  }\n\n  companion object {\n    /** Obtain a host string containing either an actual host name or a numeric IP address. */\n    val InetSocketAddress.socketHost: String get() {\n      // The InetSocketAddress was specified with a string (either a numeric IP or a host name). If\n      // it is a name, all IPs for that name should be tried. If it is an IP address, only that IP\n      // address should be tried.\n      val address = address ?: return hostName\n\n      // The InetSocketAddress has a specific address: we should only try that address. Therefore we\n      // return the address and ignore any host name that may be available.\n      return address.hostAddress\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/SequentialExchangeFinder.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport java.io.IOException\n\n/** Attempt routes one at a time until one connects. */\ninternal class SequentialExchangeFinder(\n  override val routePlanner: RoutePlanner,\n) : ExchangeFinder {\n  override fun find(): RealConnection {\n    var firstException: IOException? = null\n    while (true) {\n      if (routePlanner.isCanceled()) throw IOException(\"Canceled\")\n\n      try {\n        val plan = routePlanner.plan()\n\n        if (!plan.isReady) {\n          val tcpConnectResult = plan.connectTcp()\n          val connectResult =\n            when {\n              tcpConnectResult.isSuccess -> plan.connectTlsEtc()\n              else -> tcpConnectResult\n            }\n\n          val (_, nextPlan, failure) = connectResult\n\n          if (failure != null) throw failure\n          if (nextPlan != null) {\n            routePlanner.deferredPlans.addFirst(nextPlan)\n            continue\n          }\n        }\n        return plan.handleSuccess()\n      } catch (e: IOException) {\n        if (firstException == null) {\n          firstException = e\n        } else {\n          firstException.addSuppressed(e)\n        }\n        if (!routePlanner.hasNext()) {\n          throw firstException\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/BridgeInterceptor.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport java.io.IOException\nimport okhttp3.Cookie\nimport okhttp3.CookieJar\nimport okhttp3.Interceptor\nimport okhttp3.Response\nimport okhttp3.internal.USER_AGENT\nimport okhttp3.internal.toHostHeader\nimport okio.GzipSource\nimport okio.buffer\n\n/**\n * Bridges from application code to network code. First it builds a network request from a user\n * request. Then it proceeds to call the network. Finally it builds a user response from the network\n * response.\n */\nclass BridgeInterceptor : Interceptor {\n  @Throws(IOException::class)\n  override fun intercept(chain: Interceptor.Chain): Response {\n    val userRequest = chain.request()\n    val requestBuilder = userRequest.newBuilder()\n\n    val body = userRequest.body\n    if (body != null) {\n      val contentType = body.contentType()\n      if (contentType != null) {\n        requestBuilder.header(\"Content-Type\", contentType.toString())\n      }\n\n      val contentLength = body.contentLength()\n      if (contentLength != -1L) {\n        requestBuilder.header(\"Content-Length\", contentLength.toString())\n        requestBuilder.removeHeader(\"Transfer-Encoding\")\n      } else {\n        requestBuilder.header(\"Transfer-Encoding\", \"chunked\")\n        requestBuilder.removeHeader(\"Content-Length\")\n      }\n    }\n\n    if (userRequest.header(\"Host\") == null) {\n      requestBuilder.header(\"Host\", userRequest.url.toHostHeader())\n    }\n\n    if (userRequest.header(\"Connection\") == null) {\n      requestBuilder.header(\"Connection\", \"Keep-Alive\")\n    }\n\n    // If we add an \"Accept-Encoding: gzip\" header field we're responsible for also decompressing\n    // the transfer stream.\n    var transparentGzip = false\n    if (userRequest.header(\"Accept-Encoding\") == null && userRequest.header(\"Range\") == null) {\n      transparentGzip = true\n      requestBuilder.header(\"Accept-Encoding\", \"gzip\")\n    }\n\n    val cookies = chain.cookieJar.loadForRequest(userRequest.url)\n    if (cookies.isNotEmpty()) {\n      requestBuilder.header(\"Cookie\", cookieHeader(cookies))\n    }\n\n    if (userRequest.header(\"User-Agent\") == null) {\n      requestBuilder.header(\"User-Agent\", USER_AGENT)\n    }\n\n    val networkRequest = requestBuilder.build()\n    val networkResponse = chain.proceed(networkRequest)\n\n    chain.cookieJar.receiveHeaders(networkRequest.url, networkResponse.headers)\n\n    val responseBuilder =\n      networkResponse\n        .newBuilder()\n        .request(networkRequest)\n\n    if (transparentGzip &&\n      \"gzip\".equals(networkResponse.header(\"Content-Encoding\"), ignoreCase = true) &&\n      networkResponse.promisesBody()\n    ) {\n      val responseBody = networkResponse.body\n      val gzipSource = GzipSource(responseBody.source())\n      val strippedHeaders =\n        networkResponse.headers\n          .newBuilder()\n          .removeAll(\"Content-Encoding\")\n          .removeAll(\"Content-Length\")\n          .build()\n      responseBuilder.headers(strippedHeaders)\n      val contentType = networkResponse.header(\"Content-Type\")\n      responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))\n    }\n\n    return responseBuilder.build()\n  }\n\n  /** Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`. */\n  private fun cookieHeader(cookies: List<Cookie>): String =\n    buildString {\n      cookies.forEachIndexed { index, cookie ->\n        if (index > 0) append(\"; \")\n        append(cookie.name).append('=').append(cookie.value)\n      }\n    }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/CallServerInterceptor.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport java.io.IOException\nimport java.net.ProtocolException\nimport okhttp3.Headers\nimport okhttp3.Interceptor\nimport okhttp3.Response\nimport okhttp3.TrailersSource\nimport okhttp3.internal.UnreadableResponseBody\nimport okhttp3.internal.http2.ConnectionShutdownException\nimport okhttp3.internal.skipAll\nimport okio.buffer\n\n/** This is the last interceptor in the chain. It makes a network call to the server. */\nobject CallServerInterceptor : Interceptor {\n  @Throws(IOException::class)\n  override fun intercept(chain: Interceptor.Chain): Response {\n    val realChain = chain as RealInterceptorChain\n    val exchange = realChain.exchange!!\n    val request = realChain.request\n    val requestBody = request.body\n    val sentRequestMillis = System.currentTimeMillis()\n\n    var invokeStartEvent = true\n    var responseBuilder: Response.Builder? = null\n    var sendRequestException: IOException? = null\n    val hasRequestBody = HttpMethod.permitsRequestBody(request.method) && requestBody != null\n    val isUpgradeRequest = \"upgrade\".equals(request.header(\"Connection\"), ignoreCase = true)\n    try {\n      exchange.writeRequestHeaders(request)\n\n      if (hasRequestBody) {\n        // If there's a \"Expect: 100-continue\" header on the request, wait for a \"HTTP/1.1 100\n        // Continue\" response before transmitting the request body. If we don't get that, return\n        // what we did get (such as a 4xx response) without ever transmitting the request body.\n        if (\"100-continue\".equals(request.header(\"Expect\"), ignoreCase = true)) {\n          exchange.flushRequest()\n          responseBuilder = exchange.readResponseHeaders(expectContinue = true)\n          exchange.responseHeadersStart()\n          invokeStartEvent = false\n        }\n        if (responseBuilder == null) {\n          if (requestBody.isDuplex()) {\n            // Prepare a duplex body so that the application can send a request body later.\n            exchange.flushRequest()\n            val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()\n            requestBody.writeTo(bufferedRequestBody)\n          } else {\n            // Write the request body if the \"Expect: 100-continue\" expectation was met.\n            val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()\n            requestBody.writeTo(bufferedRequestBody)\n            bufferedRequestBody.close()\n          }\n        } else {\n          exchange.noRequestBody()\n          if (!exchange.connection.isMultiplexed) {\n            // If the \"Expect: 100-continue\" expectation wasn't met, prevent the HTTP/1 connection\n            // from being reused. Otherwise we're still obligated to transmit the request body to\n            // leave the connection in a consistent state.\n            exchange.noNewExchangesOnConnection()\n          }\n        }\n      } else {\n        exchange.noRequestBody()\n      }\n\n      if (requestBody == null || !requestBody.isDuplex()) {\n        exchange.finishRequest()\n      }\n    } catch (e: IOException) {\n      if (e is ConnectionShutdownException) {\n        throw e // No request was sent so there's no response to read.\n      }\n      if (!exchange.hasFailure) {\n        throw e // Don't attempt to read the response; we failed to send the request.\n      }\n      sendRequestException = e\n    }\n\n    try {\n      if (responseBuilder == null) {\n        responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!\n        if (invokeStartEvent) {\n          exchange.responseHeadersStart()\n          invokeStartEvent = false\n        }\n      }\n      var response =\n        responseBuilder\n          .request(request)\n          .handshake(exchange.connection.handshake())\n          .sentRequestAtMillis(sentRequestMillis)\n          .receivedResponseAtMillis(System.currentTimeMillis())\n          .build()\n      var code = response.code\n\n      while (shouldIgnoreAndWaitForRealResponse(code)) {\n        responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!\n        if (invokeStartEvent) {\n          exchange.responseHeadersStart()\n        }\n        response =\n          responseBuilder\n            .request(request)\n            .handshake(exchange.connection.handshake())\n            .sentRequestAtMillis(sentRequestMillis)\n            .receivedResponseAtMillis(System.currentTimeMillis())\n            .build()\n        code = response.code\n      }\n\n      exchange.responseHeadersEnd(response)\n\n      val isUpgradeCode = code == HTTP_SWITCHING_PROTOCOLS\n      if (isUpgradeCode && exchange.connection.isMultiplexed) {\n        throw ProtocolException(\"Unexpected $HTTP_SWITCHING_PROTOCOLS code on HTTP/2 connection\")\n      }\n\n      val isUpgradeResponse =\n        isUpgradeCode &&\n          \"upgrade\".equals(response.header(\"Connection\"), ignoreCase = true)\n\n      response =\n        when {\n          // This is an HTTP/1 upgrade. (This case includes web socket upgrades.)\n          isUpgradeRequest && isUpgradeResponse -> {\n            response\n              .newBuilder()\n              .body(\n                UnreadableResponseBody(\n                  response.body.contentType(),\n                  response.body.contentLength(),\n                ),\n              ).socket(exchange.upgradeToSocket())\n              .build()\n          }\n\n          // This is not an upgrade response.\n          else -> {\n            val responseBody = exchange.openResponseBody(response)\n            response\n              .newBuilder()\n              .body(responseBody)\n              .trailers(\n                object : TrailersSource {\n                  override fun peek() = exchange.peekTrailers()\n\n                  override fun get(): Headers {\n                    val source = responseBody.source()\n                    if (source.isOpen) {\n                      source.skipAll()\n                    }\n                    return peek() ?: error(\"null trailers after exhausting response body?!\")\n                  }\n                },\n              ).build()\n          }\n        }\n      if (\"close\".equals(response.request.header(\"Connection\"), ignoreCase = true) ||\n        \"close\".equals(response.header(\"Connection\"), ignoreCase = true)\n      ) {\n        exchange.noNewExchangesOnConnection()\n      }\n      if ((code == 204 || code == 205) && response.body.contentLength() > 0L) {\n        throw ProtocolException(\n          \"HTTP $code had non-zero Content-Length: ${response.body.contentLength()}\",\n        )\n      }\n      return response\n    } catch (e: IOException) {\n      if (sendRequestException != null) {\n        sendRequestException.addSuppressed(e)\n        throw sendRequestException\n      }\n      throw e\n    }\n  }\n\n  private fun shouldIgnoreAndWaitForRealResponse(code: Int): Boolean =\n    when {\n      // Server sent a 100-continue even though we did not request one. Try again to read the\n      // actual response status.\n      code == 100 -> true\n\n      // Handle Processing (102) & Early Hints (103) and any new codes without failing\n      // 100 and 101 are the exceptions with different meanings\n      // But Early Hints not currently exposed\n      code in (102 until 200) -> true\n\n      else -> false\n    }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/DateFormatting.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport java.text.DateFormat\nimport java.text.ParsePosition\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.Locale\nimport okhttp3.internal.UTC\n\n/** The last four-digit year: \"Fri, 31 Dec 9999 23:59:59 GMT\". */\ninternal const val MAX_DATE = 253402300799999L\n\n/**\n * Most websites serve cookies in the blessed format. Eagerly create the parser to ensure such\n * cookies are on the fast path.\n */\nprivate val STANDARD_DATE_FORMAT =\n  object : ThreadLocal<DateFormat>() {\n    override fun initialValue(): DateFormat {\n      // Date format specified by RFC 7231 section 7.1.1.1.\n      return SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss 'GMT'\", Locale.US).apply {\n        isLenient = false\n        timeZone = UTC\n      }\n    }\n  }\n\n/** If we fail to parse a date in a non-standard format, try each of these formats in sequence. */\nprivate val BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS =\n  arrayOf(\n    // HTTP formats required by RFC2616 but with any timezone:\n    // RFC 822, updated by RFC 1123 with any TZ.\n    \"EEE, dd MMM yyyy HH:mm:ss zzz\",\n    // RFC 850, obsoleted by RFC 1036 with any TZ.\n    \"EEEE, dd-MMM-yy HH:mm:ss zzz\",\n    // ANSI C's asctime() format\n    \"EEE MMM d HH:mm:ss yyyy\",\n    // Alternative formats:\n    \"EEE, dd-MMM-yyyy HH:mm:ss z\",\n    \"EEE, dd-MMM-yyyy HH-mm-ss z\",\n    \"EEE, dd MMM yy HH:mm:ss z\",\n    \"EEE dd-MMM-yyyy HH:mm:ss z\",\n    \"EEE dd MMM yyyy HH:mm:ss z\",\n    \"EEE dd-MMM-yyyy HH-mm-ss z\",\n    \"EEE dd-MMM-yy HH:mm:ss z\",\n    \"EEE dd MMM yy HH:mm:ss z\",\n    \"EEE,dd-MMM-yy HH:mm:ss z\",\n    \"EEE,dd-MMM-yyyy HH:mm:ss z\",\n    \"EEE, dd-MM-yyyy HH:mm:ss z\",\n    // RI bug 6641315 claims a cookie of this format was once served by www.yahoo.com:\n    \"EEE MMM d yyyy HH:mm:ss z\",\n  )\n\nprivate val BROWSER_COMPATIBLE_DATE_FORMATS =\n  arrayOfNulls<DateFormat>(BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.size)\n\n/** Returns the date for this string, or null if the value couldn't be parsed. */\nfun String.toHttpDateOrNull(): Date? {\n  if (isEmpty()) return null\n\n  val position = ParsePosition(0)\n  var result = STANDARD_DATE_FORMAT.get().parse(this, position)\n  if (position.index == length) {\n    // STANDARD_DATE_FORMAT must match exactly; all text must be consumed, e.g. no ignored\n    // non-standard trailing \"+01:00\". Those cases are covered below.\n    return result\n  }\n  synchronized(BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS) {\n    for (i in 0 until BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.size) {\n      var format: DateFormat? = BROWSER_COMPATIBLE_DATE_FORMATS[i]\n      if (format == null) {\n        format =\n          SimpleDateFormat(BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS[i], Locale.US).apply {\n            // Set the timezone to use when interpreting formats that don't have a timezone. GMT is\n            // specified by RFC 7231.\n            timeZone = UTC\n          }\n        BROWSER_COMPATIBLE_DATE_FORMATS[i] = format\n      }\n      position.index = 0\n      result = format.parse(this, position)\n      if (position.index != 0) {\n        // Something was parsed. It's possible the entire string was not consumed but we ignore\n        // that. If any of the BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS ended in \"'GMT'\" we'd have\n        // to also check that position.getIndex() == value.length() otherwise parsing might have\n        // terminated early, ignoring things like \"+01:00\". Leaving this as != 0 means that any\n        // trailing junk is ignored.\n        return result\n      }\n    }\n  }\n  return null\n}\n\n/** Returns the string for this date. */\nfun Date.toHttpDateString(): String = STANDARD_DATE_FORMAT.get().format(this)\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/ExchangeCodec.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport java.io.IOException\nimport okhttp3.Headers\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.Route\nimport okhttp3.internal.connection.RealCall\nimport okio.Sink\nimport okio.Socket\nimport okio.Source\n\n/** Encodes HTTP requests and decodes HTTP responses. */\ninterface ExchangeCodec {\n  /** The connection or CONNECT tunnel that owns this codec. */\n  val carrier: Carrier\n\n  /** Returns true if the response body and (possibly empty) trailers have been received. */\n  val isResponseComplete: Boolean\n\n  /** The socket that carries this exchange. */\n  val socket: Socket\n\n  /** Returns an output stream where the request body can be streamed. */\n  @Throws(IOException::class)\n  fun createRequestBody(\n    request: Request,\n    contentLength: Long,\n  ): Sink\n\n  /** This should update the HTTP engine's sentRequestMillis field. */\n  @Throws(IOException::class)\n  fun writeRequestHeaders(request: Request)\n\n  /** Flush the request to the underlying socket. */\n  @Throws(IOException::class)\n  fun flushRequest()\n\n  /** Flush the request to the underlying socket and signal no more bytes will be transmitted. */\n  @Throws(IOException::class)\n  fun finishRequest()\n\n  /**\n   * Parses bytes of a response header from an HTTP transport.\n   *\n   * @param expectContinue true to return null if this is an intermediate response with a \"100\"\n   * response code. Otherwise this method never returns null.\n   */\n  @Throws(IOException::class)\n  fun readResponseHeaders(expectContinue: Boolean): Response.Builder?\n\n  @Throws(IOException::class)\n  fun reportedContentLength(response: Response): Long\n\n  @Throws(IOException::class)\n  fun openResponseBodySource(response: Response): Source\n\n  /** Returns the trailers after the HTTP response if they're ready. May be empty. */\n  @Throws(IOException::class)\n  fun peekTrailers(): Headers?\n\n  /**\n   * Cancel this stream. Resources held by this stream will be cleaned up, though not synchronously.\n   * That may happen later by the connection pool thread.\n   */\n  fun cancel()\n\n  /**\n   * Carries an exchange. This is usually a connection, but it could also be a connect plan for\n   * CONNECT tunnels. Note that CONNECT tunnels are significantly less capable than connections.\n   */\n  interface Carrier {\n    val route: Route\n\n    fun trackFailure(\n      call: RealCall,\n      e: IOException?,\n    )\n\n    fun noNewExchanges()\n\n    fun cancel()\n  }\n\n  companion object {\n    /**\n     * The timeout to use while discarding a stream of input data. Since this is used for connection\n     * reuse, this timeout should be significantly less than the time it takes to establish a new\n     * connection.\n     */\n    const val DISCARD_STREAM_TIMEOUT_MILLIS = 100\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/GzipRequestBody.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport okhttp3.RequestBody\nimport okio.BufferedSink\nimport okio.GzipSink\nimport okio.buffer\n\ninternal class GzipRequestBody(\n  val delegate: RequestBody,\n) : RequestBody() {\n  override fun contentType() = delegate.contentType()\n\n  // We don't know the compressed length in advance!\n  override fun contentLength() = -1L\n\n  override fun writeTo(sink: BufferedSink) {\n    GzipSink(sink).buffer().use(delegate::writeTo)\n  }\n\n  override fun isOneShot() = delegate.isOneShot()\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/HttpHeaders.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:JvmName(\"HttpHeaders\")\n\npackage okhttp3.internal.http\n\nimport java.io.EOFException\nimport java.net.HttpURLConnection.HTTP_NOT_MODIFIED\nimport java.net.HttpURLConnection.HTTP_NO_CONTENT\nimport java.util.Collections\nimport okhttp3.Challenge\nimport okhttp3.Cookie\nimport okhttp3.CookieJar\nimport okhttp3.Headers\nimport okhttp3.HttpUrl\nimport okhttp3.Response\nimport okhttp3.internal.headersContentLength\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.skipAll\nimport okio.Buffer\nimport okio.ByteString.Companion.encodeUtf8\n\nprivate val QUOTED_STRING_DELIMITERS = \"\\\"\\\\\".encodeUtf8()\nprivate val TOKEN_DELIMITERS = \"\\t ,=\".encodeUtf8()\n\n/**\n * Parse RFC 7235 challenges. This is awkward because we need to look ahead to know how to\n * interpret a token.\n *\n * For example, the first line has a parameter name/value pair and the second line has a single\n * token68:\n *\n * ```\n * WWW-Authenticate: Digest foo=bar\n * WWW-Authenticate: Digest foo=\n * ```\n *\n * Similarly, the first line has one challenge and the second line has two challenges:\n *\n * ```\n * WWW-Authenticate: Digest ,foo=bar\n * WWW-Authenticate: Digest ,foo\n * ```\n */\nfun Headers.parseChallenges(headerName: String): List<Challenge> {\n  val result = mutableListOf<Challenge>()\n  for (h in 0 until size) {\n    if (headerName.equals(name(h), ignoreCase = true)) {\n      val header = Buffer().writeUtf8(value(h))\n      try {\n        header.readChallengeHeader(result)\n      } catch (e: EOFException) {\n        Platform.get().log(\"Unable to parse challenge\", Platform.WARN, e)\n      }\n    }\n  }\n  return result\n}\n\n@Throws(EOFException::class)\nprivate fun Buffer.readChallengeHeader(result: MutableList<Challenge>) {\n  var peek: String? = null\n\n  while (true) {\n    // Read a scheme name for this challenge if we don't have one already.\n    if (peek == null) {\n      skipCommasAndWhitespace()\n      peek = readToken()\n      if (peek == null) return\n    }\n\n    val schemeName = peek\n\n    // Read a token68, a sequence of parameters, or nothing.\n    val commaPrefixed = skipCommasAndWhitespace()\n    peek = readToken()\n    if (peek == null) {\n      if (!exhausted()) return // Expected a token; got something else.\n      result.add(Challenge(schemeName, emptyMap()))\n      return\n    }\n\n    var eqCount = skipAll('='.code.toByte())\n    val commaSuffixed = skipCommasAndWhitespace()\n\n    // It's a token68 because there isn't a value after it.\n    if (!commaPrefixed && (commaSuffixed || exhausted())) {\n      result.add(\n        Challenge(\n          schemeName,\n          Collections.singletonMap<String?, String>(null, peek + \"=\".repeat(eqCount)),\n        ),\n      )\n      peek = null\n      continue\n    }\n\n    // It's a series of parameter names and values.\n    val parameters = mutableMapOf<String?, String>()\n    eqCount += skipAll('='.code.toByte())\n    while (true) {\n      if (peek == null) {\n        peek = readToken()\n        if (skipCommasAndWhitespace()) break // We peeked a scheme name followed by ','.\n        eqCount = skipAll('='.code.toByte())\n      }\n      if (eqCount == 0) break // We peeked a scheme name.\n      if (eqCount > 1) return // Unexpected '=' characters.\n      if (skipCommasAndWhitespace()) return // Unexpected ','.\n\n      val parameterValue =\n        when {\n          startsWith('\"'.code.toByte()) -> readQuotedString()\n          else -> readToken()\n        } ?: return // Expected a value.\n\n      val replaced = parameters.put(peek, parameterValue)\n      peek = null\n      if (replaced != null) return // Unexpected duplicate parameter.\n      if (!skipCommasAndWhitespace() && !exhausted()) return // Expected ',' or EOF.\n    }\n    result.add(Challenge(schemeName, parameters))\n  }\n}\n\n/** Returns true if any commas were skipped. */\nprivate fun Buffer.skipCommasAndWhitespace(): Boolean {\n  var commaFound = false\n  loop@ while (!exhausted()) {\n    when (this[0]) {\n      ','.code.toByte() -> {\n        // Consume ','.\n        readByte()\n        commaFound = true\n      }\n\n      ' '.code.toByte(), '\\t'.code.toByte() -> {\n        readByte()\n        // Consume space or tab.\n      }\n\n      else -> {\n        break@loop\n      }\n    }\n  }\n  return commaFound\n}\n\nprivate fun Buffer.startsWith(prefix: Byte): Boolean = !exhausted() && this[0] == prefix\n\n/**\n * Reads a double-quoted string, unescaping quoted pairs like `\\\"` to the 2nd character in each\n * sequence. Returns the unescaped string, or null if the buffer isn't prefixed with a\n * double-quoted string.\n */\n@Throws(EOFException::class)\nprivate fun Buffer.readQuotedString(): String? {\n  require(readByte() == '\\\"'.code.toByte())\n  val result = Buffer()\n  while (true) {\n    val i = indexOfElement(QUOTED_STRING_DELIMITERS)\n    if (i == -1L) return null // Unterminated quoted string.\n\n    if (this[i] == '\"'.code.toByte()) {\n      result.write(this, i)\n      // Consume '\"'.\n      readByte()\n      return result.readUtf8()\n    }\n\n    if (size == i + 1L) return null // Dangling escape.\n    result.write(this, i)\n    // Consume '\\'.\n    readByte()\n    result.write(this, 1L) // The escaped character.\n  }\n}\n\n/**\n * Consumes and returns a non-empty token, terminating at special characters in\n * [TOKEN_DELIMITERS]. Returns null if the buffer is empty or prefixed with a delimiter.\n */\nprivate fun Buffer.readToken(): String? {\n  var tokenSize = indexOfElement(TOKEN_DELIMITERS)\n  if (tokenSize == -1L) tokenSize = size\n\n  return when {\n    tokenSize != 0L -> readUtf8(tokenSize)\n    else -> null\n  }\n}\n\nfun CookieJar.receiveHeaders(\n  url: HttpUrl,\n  headers: Headers,\n) {\n  if (this === CookieJar.NO_COOKIES) return\n\n  val cookies = Cookie.parseAll(url, headers)\n  if (cookies.isEmpty()) return\n\n  saveFromResponse(url, cookies)\n}\n\n/**\n * Returns true if the response headers and status indicate that this response has a (possibly\n * 0-length) body. See RFC 7231.\n */\nfun Response.promisesBody(): Boolean {\n  // HEAD requests never yield a body regardless of the response headers.\n  if (request.method == \"HEAD\") {\n    return false\n  }\n\n  val responseCode = code\n  if ((responseCode < HTTP_CONTINUE || responseCode >= 200) &&\n    responseCode != HTTP_NO_CONTENT &&\n    responseCode != HTTP_NOT_MODIFIED\n  ) {\n    return true\n  }\n\n  // If the Content-Length or Transfer-Encoding headers disagree with the response code, the\n  // response is malformed. For best compatibility, we honor the headers.\n  if (headersContentLength() != -1L ||\n    \"chunked\".equals(header(\"Transfer-Encoding\"), ignoreCase = true)\n  ) {\n    return true\n  }\n\n  return false\n}\n\n@Deprecated(\n  message = \"No longer supported\",\n  level = DeprecationLevel.ERROR,\n  replaceWith = ReplaceWith(expression = \"response.promisesBody()\"),\n)\nfun hasBody(response: Response): Boolean = response.promisesBody()\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/HttpMethod.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport kotlin.jvm.JvmStatic\n\nobject HttpMethod {\n  @JvmStatic // Despite being 'internal', this method is called by popular 3rd party SDKs.\n  fun invalidatesCache(method: String): Boolean =\n    (\n      method == \"POST\" ||\n        method == \"PATCH\" ||\n        method == \"PUT\" ||\n        method == \"DELETE\" ||\n        method == \"MOVE\"\n    )\n\n  @JvmStatic // Despite being 'internal', this method is called by popular 3rd party SDKs.\n  fun requiresRequestBody(method: String): Boolean =\n    (\n      method == \"POST\" ||\n        method == \"PUT\" ||\n        method == \"PATCH\" ||\n        method == \"PROPPATCH\" ||\n        method == \"QUERY\" ||\n        // WebDAV\n        method == \"REPORT\"\n    )\n\n  @JvmStatic // Despite being 'internal', this method is called by popular 3rd party SDKs.\n  fun permitsRequestBody(method: String): Boolean = !(method == \"GET\" || method == \"HEAD\")\n\n  fun redirectsWithBody(method: String): Boolean = method == \"PROPFIND\"\n\n  fun redirectsToGet(method: String): Boolean = method != \"PROPFIND\"\n\n  fun isCacheable(requestMethod: String): Boolean = requestMethod == \"GET\" || requestMethod == \"QUERY\"\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/HttpStatusCodes.kt",
    "content": "/*\n * ====================================================================\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n * ====================================================================\n *\n * This software consists of voluntary contributions made by many\n * individuals on behalf of the Apache Software Foundation.  For more\n * information on the Apache Software Foundation, please see\n * <http://www.apache.org/>.\n */\n\npackage okhttp3.internal.http\n\n// HTTP Status Codes not offered by HttpUrlConnection.\n//\n// https://datatracker.ietf.org/doc/html/rfc7231#page-47\n//\n// From https://github.com/apache/httpcomponents-core/blob/master/httpcore5/src/main/java/org/apache/hc/core5/http/HttpStatus.java\n\n/** `100 Continue` (HTTP/1.1 - RFC 7231)  */\nconst val HTTP_CONTINUE = 100\n\n/** `101 Switching Protocols` (HTTP/1.1 - RFC 9110)  */\nconst val HTTP_SWITCHING_PROTOCOLS = 101\n\n/** `102 Processing` (WebDAV - RFC 2518)  */\nconst val HTTP_PROCESSING = 102\n\n/** `103 Early Hints (Early Hints - RFC 8297)` */\nconst val HTTP_EARLY_HINTS = 103\n\n/** `307 Temporary Redirect` (HTTP/1.1 - RFC 7231)  */\nconst val HTTP_TEMP_REDIRECT = 307\n\n/** `308 Permanent Redirect` (HTTP/1.1 - RFC 7538)  */\nconst val HTTP_PERM_REDIRECT = 308\n\n/** `421 Misdirected Request` (HTTP/2 - RFC 7540)  */\nconst val HTTP_MISDIRECTED_REQUEST = 421\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/RealInterceptorChain.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport java.io.IOException\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.util.concurrent.TimeUnit\nimport javax.net.SocketFactory\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Address\nimport okhttp3.Authenticator\nimport okhttp3.Cache\nimport okhttp3.Call\nimport okhttp3.CertificatePinner\nimport okhttp3.Connection\nimport okhttp3.ConnectionPool\nimport okhttp3.CookieJar\nimport okhttp3.Dns\nimport okhttp3.EventListener\nimport okhttp3.HttpUrl\nimport okhttp3.Interceptor\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.checkDuration\nimport okhttp3.internal.connection.Exchange\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.tls.CertificateChainCleaner\n\n/**\n * A concrete interceptor chain that carries the entire interceptor chain: all application\n * interceptors, the OkHttp core, all network interceptors, and finally the network caller.\n *\n * If the chain is for an application interceptor then [exchange] must be null. Otherwise it is for\n * a network interceptor and [exchange] must be non-null.\n */\nclass RealInterceptorChain(\n  internal val call: RealCall,\n  private val interceptors: List<Interceptor>,\n  private val index: Int,\n  internal val exchange: Exchange?,\n  internal val request: Request,\n  internal val connectTimeoutMillis: Int,\n  internal val readTimeoutMillis: Int,\n  internal val writeTimeoutMillis: Int,\n  override val authenticator: Authenticator,\n  override val cache: Cache?,\n  override val certificatePinner: CertificatePinner,\n  override val connectionPool: ConnectionPool,\n  override val cookieJar: CookieJar,\n  override val dns: Dns,\n  override val hostnameVerifier: HostnameVerifier,\n  override val proxy: Proxy?,\n  override val proxyAuthenticator: Authenticator,\n  override val proxySelector: ProxySelector,\n  override val retryOnConnectionFailure: Boolean,\n  override val socketFactory: SocketFactory,\n  override val sslSocketFactoryOrNull: SSLSocketFactory?,\n  override val x509TrustManagerOrNull: X509TrustManager?,\n  val certificateChainCleaner: CertificateChainCleaner?,\n) : Interceptor.Chain {\n  internal constructor(\n    call: RealCall,\n    interceptors: List<Interceptor>,\n    index: Int,\n    exchange: Nothing?,\n    request: Request,\n    client: OkHttpClient = call.client,\n  ) : this(\n    call = call,\n    interceptors = interceptors,\n    index = index,\n    exchange = exchange,\n    request = request,\n    connectTimeoutMillis = client.connectTimeoutMillis,\n    readTimeoutMillis = client.readTimeoutMillis,\n    writeTimeoutMillis = client.writeTimeoutMillis,\n    authenticator = client.authenticator,\n    cache = client.cache,\n    certificatePinner = client.certificatePinner,\n    connectionPool = client.connectionPool,\n    cookieJar = client.cookieJar,\n    dns = client.dns,\n    hostnameVerifier = client.hostnameVerifier,\n    proxy = client.proxy,\n    proxyAuthenticator = client.proxyAuthenticator,\n    proxySelector = client.proxySelector,\n    retryOnConnectionFailure = client.retryOnConnectionFailure,\n    socketFactory = client.socketFactory,\n    sslSocketFactoryOrNull = client.sslSocketFactoryOrNull,\n    x509TrustManagerOrNull = client.x509TrustManager,\n    certificateChainCleaner = client.certificateChainCleaner,\n  )\n\n  private var calls: Int = 0\n\n  internal fun copy(\n    index: Int = this.index,\n    exchange: Exchange? = this.exchange,\n    request: Request = this.request,\n    connectTimeoutMillis: Int = this.connectTimeoutMillis,\n    readTimeoutMillis: Int = this.readTimeoutMillis,\n    writeTimeoutMillis: Int = this.writeTimeoutMillis,\n    authenticator: Authenticator = this.authenticator,\n    cache: Cache? = this.cache,\n    certificatePinner: CertificatePinner = this.certificatePinner,\n    connectionPool: ConnectionPool = this.connectionPool,\n    cookieJar: CookieJar = this.cookieJar,\n    dns: Dns = this.dns,\n    hostnameVerifier: HostnameVerifier = this.hostnameVerifier,\n    proxy: Proxy? = this.proxy,\n    proxyAuthenticator: Authenticator = this.proxyAuthenticator,\n    proxySelector: ProxySelector = this.proxySelector,\n    retryOnConnectionFailure: Boolean = this.retryOnConnectionFailure,\n    socketFactory: SocketFactory = this.socketFactory,\n    sslSocketFactory: SSLSocketFactory? = this.sslSocketFactoryOrNull,\n    x509TrustManager: X509TrustManager? = this.x509TrustManagerOrNull,\n    certificateChainCleaner: CertificateChainCleaner? = this.certificateChainCleaner,\n  ) = RealInterceptorChain(\n    call = call,\n    interceptors = interceptors,\n    index = index,\n    exchange = exchange,\n    request = request,\n    connectTimeoutMillis = connectTimeoutMillis,\n    readTimeoutMillis = readTimeoutMillis,\n    writeTimeoutMillis = writeTimeoutMillis,\n    authenticator = authenticator,\n    cache = cache,\n    certificatePinner = certificatePinner,\n    connectionPool = connectionPool,\n    cookieJar = cookieJar,\n    dns = dns,\n    hostnameVerifier = hostnameVerifier,\n    proxy = proxy,\n    proxyAuthenticator = proxyAuthenticator,\n    proxySelector = proxySelector,\n    retryOnConnectionFailure = retryOnConnectionFailure,\n    socketFactory = socketFactory,\n    sslSocketFactoryOrNull = sslSocketFactory,\n    x509TrustManagerOrNull = x509TrustManager,\n    certificateChainCleaner = certificateChainCleaner,\n  )\n\n  override val eventListener: EventListener\n    get() = call.eventListener\n\n  override val followSslRedirects: Boolean\n    get() = call.client.followSslRedirects\n\n  override val followRedirects: Boolean\n    get() = call.client.followRedirects\n\n  override fun connection(): Connection? = exchange?.connection\n\n  override fun connectTimeoutMillis(): Int = connectTimeoutMillis\n\n  override fun withConnectTimeout(\n    timeout: Int,\n    unit: TimeUnit,\n  ): Interceptor.Chain {\n    check(exchange == null) { \"Timeouts can't be adjusted in a network interceptor\" }\n\n    return copy(connectTimeoutMillis = checkDuration(\"connectTimeout\", timeout.toLong(), unit))\n  }\n\n  override fun readTimeoutMillis(): Int = readTimeoutMillis\n\n  override fun withReadTimeout(\n    timeout: Int,\n    unit: TimeUnit,\n  ): Interceptor.Chain {\n    check(exchange == null) { \"Timeouts can't be adjusted in a network interceptor\" }\n\n    return copy(readTimeoutMillis = checkDuration(\"readTimeout\", timeout.toLong(), unit))\n  }\n\n  override fun writeTimeoutMillis(): Int = writeTimeoutMillis\n\n  override fun withWriteTimeout(\n    timeout: Int,\n    unit: TimeUnit,\n  ): Interceptor.Chain {\n    check(exchange == null) { \"Timeouts can't be adjusted in a network interceptor\" }\n\n    return copy(writeTimeoutMillis = checkDuration(\"writeTimeout\", timeout.toLong(), unit))\n  }\n\n  override fun withDns(dns: Dns): Interceptor.Chain {\n    check(exchange == null) { \"dns can't be adjusted in a network interceptor\" }\n\n    return copy(dns = dns)\n  }\n\n  override fun withSocketFactory(socketFactory: SocketFactory): Interceptor.Chain {\n    check(exchange == null) { \"socketFactory can't be adjusted in a network interceptor\" }\n\n    return copy(socketFactory = socketFactory)\n  }\n\n  override fun withRetryOnConnectionFailure(retryOnConnectionFailure: Boolean): Interceptor.Chain {\n    check(exchange == null) { \"retryOnConnectionFailure can't be adjusted in a network interceptor\" }\n\n    return copy(retryOnConnectionFailure = retryOnConnectionFailure)\n  }\n\n  override fun withAuthenticator(authenticator: Authenticator): Interceptor.Chain {\n    check(exchange == null) { \"authenticator can't be adjusted in a network interceptor\" }\n\n    return copy(authenticator = authenticator)\n  }\n\n  override fun withCookieJar(cookieJar: CookieJar): Interceptor.Chain {\n    check(exchange == null) { \"cookieJar can't be adjusted in a network interceptor\" }\n\n    return copy(cookieJar = cookieJar)\n  }\n\n  override fun withCache(cache: Cache?): Interceptor.Chain {\n    check(exchange == null) { \"cache can't be adjusted in a network interceptor\" }\n\n    return copy(cache = cache)\n  }\n\n  override fun withProxy(proxy: Proxy?): Interceptor.Chain {\n    check(exchange == null) { \"proxy can't be adjusted in a network interceptor\" }\n\n    return copy(proxy = proxy)\n  }\n\n  override fun withProxySelector(proxySelector: ProxySelector): Interceptor.Chain {\n    check(exchange == null) { \"proxySelector can't be adjusted in a network interceptor\" }\n\n    return copy(proxySelector = proxySelector)\n  }\n\n  override fun withProxyAuthenticator(proxyAuthenticator: Authenticator): Interceptor.Chain {\n    check(exchange == null) { \"proxyAuthenticator can't be adjusted in a network interceptor\" }\n\n    return copy(proxyAuthenticator = proxyAuthenticator)\n  }\n\n  override fun withSslSocketFactory(\n    sslSocketFactory: SSLSocketFactory?,\n    x509TrustManager: X509TrustManager?,\n  ): Interceptor.Chain {\n    check(exchange == null) { \"sslSocketFactory can't be adjusted in a network interceptor\" }\n\n    if (sslSocketFactory != null && x509TrustManager != null) {\n      val newCertificateChainCleaner = CertificateChainCleaner.get(x509TrustManager)\n      return copy(\n        sslSocketFactory = sslSocketFactory,\n        x509TrustManager = x509TrustManager,\n        certificateChainCleaner = newCertificateChainCleaner,\n        certificatePinner = certificatePinner.withCertificateChainCleaner(newCertificateChainCleaner),\n      )\n    } else {\n      return copy(\n        sslSocketFactory = null,\n        x509TrustManager = null,\n        certificateChainCleaner = null,\n      )\n    }\n  }\n\n  override fun withHostnameVerifier(hostnameVerifier: HostnameVerifier): Interceptor.Chain {\n    check(exchange == null) { \"hostnameVerifier can't be adjusted in a network interceptor\" }\n\n    return copy(hostnameVerifier = hostnameVerifier)\n  }\n\n  override fun withCertificatePinner(certificatePinner: CertificatePinner): Interceptor.Chain {\n    check(exchange == null) { \"certificatePinner can't be adjusted in a network interceptor\" }\n\n    val newCertificatePinner =\n      if (certificateChainCleaner != null) {\n        certificatePinner.withCertificateChainCleaner(certificateChainCleaner)\n      } else {\n        certificatePinner\n      }\n\n    return copy(certificatePinner = newCertificatePinner)\n  }\n\n  override fun withConnectionPool(connectionPool: ConnectionPool): Interceptor.Chain {\n    check(exchange == null) { \"connectionPool can't be adjusted in a network interceptor\" }\n\n    return copy(connectionPool = connectionPool)\n  }\n\n  override fun call(): Call = call\n\n  override fun request(): Request = request\n\n  @Throws(IOException::class)\n  override fun proceed(request: Request): Response {\n    check(index < interceptors.size)\n\n    calls++\n\n    if (exchange != null) {\n      check(exchange.finder.routePlanner.sameHostAndPort(request.url)) {\n        \"network interceptor ${interceptors[index - 1]} must retain the same host and port\"\n      }\n      check(calls == 1) {\n        \"network interceptor ${interceptors[index - 1]} must call proceed() exactly once\"\n      }\n    }\n\n    // Call the next interceptor in the chain.\n    val next = copy(index = index + 1, request = request)\n    val interceptor = interceptors[index]\n\n    @Suppress(\"USELESS_ELVIS\")\n    val response =\n      interceptor.intercept(next) ?: throw NullPointerException(\n        \"interceptor $interceptor returned null\",\n      )\n\n    if (exchange != null) {\n      check(index + 1 >= interceptors.size || next.calls == 1) {\n        \"network interceptor $interceptor must call proceed() exactly once\"\n      }\n    }\n\n    return response\n  }\n\n  /**\n   * Creates an [Address] of out of the provided [HttpUrl]\n   * that uses this client’s DNS, TLS, and proxy configuration.\n   */\n  fun address(url: HttpUrl): Address {\n    var useSslSocketFactory: SSLSocketFactory? = null\n    var useHostnameVerifier: HostnameVerifier? = null\n    var useCertificatePinner: CertificatePinner? = null\n    if (url.isHttps) {\n      useSslSocketFactory = this.sslSocketFactoryOrNull\n      useHostnameVerifier = this.hostnameVerifier\n      useCertificatePinner = this.certificatePinner\n    }\n\n    return Address(\n      uriHost = url.host,\n      uriPort = url.port,\n      dns = dns,\n      socketFactory = socketFactory,\n      sslSocketFactory = useSslSocketFactory,\n      hostnameVerifier = useHostnameVerifier,\n      certificatePinner = useCertificatePinner,\n      proxyAuthenticator = proxyAuthenticator,\n      proxy = proxy,\n      protocols = call.client.protocols,\n      connectionSpecs = call.client.connectionSpecs,\n      proxySelector = proxySelector,\n    )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/RealResponseBody.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport okhttp3.MediaType\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.ResponseBody\nimport okio.BufferedSource\n\nclass RealResponseBody(\n  /**\n   * Use a string to avoid parsing the content type until needed. This also defers problems caused\n   * by malformed content types.\n   */\n  private val contentTypeString: String?,\n  private val contentLength: Long,\n  private val source: BufferedSource,\n) : ResponseBody() {\n  override fun contentLength(): Long = contentLength\n\n  override fun contentType(): MediaType? = contentTypeString?.toMediaTypeOrNull()\n\n  override fun source(): BufferedSource = source\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/RequestLine.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport java.net.Proxy\nimport okhttp3.HttpUrl\nimport okhttp3.Request\n\nobject RequestLine {\n  /**\n   * Returns the request status line, like \"GET / HTTP/1.1\". This is exposed to the application by\n   * [HttpURLConnection.getHeaderFields], so it needs to be set even if the transport is\n   * HTTP/2.\n   */\n  fun get(\n    request: Request,\n    proxyType: Proxy.Type,\n  ): String =\n    buildString {\n      append(request.method)\n      append(' ')\n      if (includeAuthorityInRequestLine(request, proxyType)) {\n        append(request.url)\n      } else {\n        append(requestPath(request.url))\n      }\n      append(\" HTTP/1.1\")\n    }\n\n  /**\n   * Returns true if the request line should contain the full URL with host and port (like \"GET\n   * http://android.com/foo HTTP/1.1\") or only the path (like \"GET /foo HTTP/1.1\").\n   */\n  private fun includeAuthorityInRequestLine(\n    request: Request,\n    proxyType: Proxy.Type,\n  ): Boolean = !request.isHttps && proxyType == Proxy.Type.HTTP\n\n  /**\n   * Returns the path to request, like the '/' in 'GET / HTTP/1.1'. Never empty, even if the request\n   * URL is. Includes the query component if it exists.\n   */\n  fun requestPath(url: HttpUrl): String {\n    val path = url.encodedPath\n    val query = url.encodedQuery\n    return if (query != null) \"$path?$query\" else path\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/RetryAndFollowUpInterceptor.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport java.io.FileNotFoundException\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT\nimport java.net.HttpURLConnection.HTTP_MOVED_PERM\nimport java.net.HttpURLConnection.HTTP_MOVED_TEMP\nimport java.net.HttpURLConnection.HTTP_MULT_CHOICE\nimport java.net.HttpURLConnection.HTTP_PROXY_AUTH\nimport java.net.HttpURLConnection.HTTP_SEE_OTHER\nimport java.net.HttpURLConnection.HTTP_UNAUTHORIZED\nimport java.net.HttpURLConnection.HTTP_UNAVAILABLE\nimport java.net.ProtocolException\nimport java.net.Proxy\nimport java.net.SocketTimeoutException\nimport java.security.cert.CertificateException\nimport javax.net.ssl.SSLHandshakeException\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport okhttp3.Interceptor\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.canReuseConnectionFor\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.connection.Exchange\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.http2.ConnectionShutdownException\nimport okhttp3.internal.stripBody\nimport okhttp3.internal.withSuppressed\n\n/**\n * This interceptor recovers from failures and follows redirects as necessary. It may throw an\n * [IOException] if the call was canceled.\n */\nclass RetryAndFollowUpInterceptor : Interceptor {\n  @Throws(IOException::class)\n  override fun intercept(chain: Interceptor.Chain): Response {\n    val realChain = chain as RealInterceptorChain\n    var request = chain.request\n    val call = realChain.call\n    var followUpCount = 0\n    var priorResponse: Response? = null\n    var newRoutePlanner = true\n    var recoveredFailures = listOf<IOException>()\n    while (true) {\n      call.enterNetworkInterceptorExchange(request, newRoutePlanner, chain)\n\n      var response: Response\n      var closeActiveExchange = true\n      try {\n        if (call.isCanceled()) {\n          throw IOException(\"Canceled\")\n        }\n\n        try {\n          response = realChain.proceed(request)\n          newRoutePlanner = true\n        } catch (e: IOException) {\n          // An attempt to communicate with a server failed. The request may have been sent.\n          val isRecoverable = recover(e, call, chain, request)\n          call.eventListener.retryDecision(call, e, isRecoverable)\n          if (!isRecoverable) throw e.withSuppressed(recoveredFailures)\n          recoveredFailures += e\n          newRoutePlanner = false\n          continue\n        }\n\n        // Clear out downstream interceptor's additional request headers, cookies, etc.\n        response =\n          response\n            .newBuilder()\n            .request(request)\n            .priorResponse(priorResponse?.stripBody())\n            .build()\n\n        val exchange = call.interceptorScopedExchange\n        val followUp = followUpRequest(response, exchange, chain)\n\n        if (followUp == null) {\n          if (exchange != null && exchange.isDuplex) {\n            call.timeoutEarlyExit()\n          }\n          closeActiveExchange = false\n          call.eventListener.followUpDecision(call, response, null)\n          return response\n        }\n\n        val followUpBody = followUp.body\n        if (followUpBody != null && followUpBody.isOneShot()) {\n          closeActiveExchange = false\n          call.eventListener.followUpDecision(call, response, null)\n          return response\n        }\n\n        response.body.closeQuietly()\n\n        if (++followUpCount > MAX_FOLLOW_UPS) {\n          call.eventListener.followUpDecision(call, response, null)\n          throw ProtocolException(\"Too many follow-up requests: $followUpCount\")\n        }\n\n        call.eventListener.followUpDecision(call, response, followUp)\n        request = followUp\n        priorResponse = response\n      } finally {\n        call.exitNetworkInterceptorExchange(closeActiveExchange)\n      }\n    }\n  }\n\n  /**\n   * Report and attempt to recover from a failure to communicate with a server. Returns true if\n   * `e` is recoverable, or false if the failure is permanent. Requests with a body can only\n   * be recovered if the body is buffered or if the failure occurred before the request has been\n   * sent.\n   */\n  private fun recover(\n    e: IOException,\n    call: RealCall,\n    chain: Interceptor.Chain,\n    userRequest: Request,\n  ): Boolean {\n    val requestSendStarted = e !is ConnectionShutdownException\n\n    // The application layer has forbidden retries.\n    if (!chain.retryOnConnectionFailure) return false\n\n    // We can't send the request body again.\n    if (requestSendStarted && requestIsOneShot(e, userRequest)) return false\n\n    // This exception is fatal.\n    if (!isRecoverable(e, requestSendStarted)) return false\n\n    // No more routes to attempt.\n    if (!call.retryAfterFailure()) return false\n\n    // For failure recovery, use the same route selector with a new connection.\n    return true\n  }\n\n  private fun requestIsOneShot(\n    e: IOException,\n    userRequest: Request,\n  ): Boolean {\n    val requestBody = userRequest.body\n    return (requestBody != null && requestBody.isOneShot()) ||\n      e is FileNotFoundException\n  }\n\n  private fun isRecoverable(\n    e: IOException,\n    requestSendStarted: Boolean,\n  ): Boolean {\n    // If there was a protocol problem, don't recover.\n    if (e is ProtocolException) {\n      return false\n    }\n\n    // If there was an interruption don't recover, but if there was a timeout connecting to a route\n    // we should try the next route (if there is one).\n    if (e is InterruptedIOException) {\n      return e is SocketTimeoutException && !requestSendStarted\n    }\n\n    // Look for known client-side or negotiation errors that are unlikely to be fixed by trying\n    // again with a different route.\n    if (e is SSLHandshakeException) {\n      // If the problem was a CertificateException from the X509TrustManager,\n      // do not retry.\n      if (e.cause is CertificateException) {\n        return false\n      }\n    }\n    if (e is SSLPeerUnverifiedException) {\n      // e.g. a certificate pinning error.\n      return false\n    }\n    // An example of one we might want to retry with a different route is a problem connecting to a\n    // proxy and would manifest as a standard IOException. Unless it is one we know we should not\n    // retry, we return true and try a new route.\n    return true\n  }\n\n  /**\n   * Figures out the HTTP request to make in response to receiving [userResponse]. This will\n   * either add authentication headers, follow redirects or handle a client request timeout. If a\n   * follow-up is either unnecessary or not applicable, this returns null.\n   */\n  @Throws(IOException::class)\n  private fun followUpRequest(\n    userResponse: Response,\n    exchange: Exchange?,\n    chain: Interceptor.Chain,\n  ): Request? {\n    val route = exchange?.connection?.route()\n    val responseCode = userResponse.code\n\n    val method = userResponse.request.method\n    when (responseCode) {\n      HTTP_PROXY_AUTH -> {\n        val selectedProxy = route!!.proxy\n        if (selectedProxy.type() != Proxy.Type.HTTP) {\n          throw ProtocolException(\"Received HTTP_PROXY_AUTH (407) code while not using proxy\")\n        }\n        return chain.proxyAuthenticator.authenticate(route, userResponse)\n      }\n\n      HTTP_UNAUTHORIZED -> {\n        return chain.authenticator.authenticate(route, userResponse)\n      }\n\n      HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {\n        return buildRedirectRequest(userResponse, method, chain)\n      }\n\n      HTTP_CLIENT_TIMEOUT -> {\n        // 408's are rare in practice, but some servers like HAProxy use this response code. The\n        // spec says that we may repeat the request without modifications. Modern browsers also\n        // repeat the request (even non-idempotent ones.)\n        if (!chain.retryOnConnectionFailure) {\n          // The application layer has directed us not to retry the request.\n          return null\n        }\n\n        val requestBody = userResponse.request.body\n        if (requestBody != null && requestBody.isOneShot()) {\n          return null\n        }\n        val priorResponse = userResponse.priorResponse\n        if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {\n          // We attempted to retry and got another timeout. Give up.\n          return null\n        }\n\n        if (retryAfter(userResponse, 0) > 0) {\n          return null\n        }\n\n        return userResponse.request\n      }\n\n      HTTP_UNAVAILABLE -> {\n        val priorResponse = userResponse.priorResponse\n        if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {\n          // We attempted to retry and got another timeout. Give up.\n          return null\n        }\n\n        if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {\n          // specifically received an instruction to retry without delay.\n          return userResponse.request\n        }\n\n        return null\n      }\n\n      HTTP_MISDIRECTED_REQUEST -> {\n        // OkHttp can coalesce HTTP/2 connections even if the domain names are different. See\n        // RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then\n        // we can retry on a different connection.\n        val requestBody = userResponse.request.body\n        if (requestBody != null && requestBody.isOneShot()) {\n          return null\n        }\n\n        if (exchange == null || !exchange.isCoalescedConnection) {\n          return null\n        }\n\n        exchange.connection.noCoalescedConnections()\n        return userResponse.request\n      }\n\n      else -> {\n        return null\n      }\n    }\n  }\n\n  private fun buildRedirectRequest(\n    userResponse: Response,\n    method: String,\n    chain: Interceptor.Chain,\n  ): Request? {\n    // Does the client allow redirects?\n    if (!chain.followRedirects) return null\n\n    val location = userResponse.header(\"Location\") ?: return null\n    // Don't follow redirects to unsupported protocols.\n    val url = userResponse.request.url.resolve(location) ?: return null\n\n    // If configured, don't follow redirects between SSL and non-SSL.\n    val sameScheme = url.scheme == userResponse.request.url.scheme\n    if (!sameScheme && !chain.followSslRedirects) return null\n\n    // Most redirects don't include a request body.\n    val requestBuilder = userResponse.request.newBuilder()\n    if (HttpMethod.permitsRequestBody(method)) {\n      val responseCode = userResponse.code\n      val maintainBody =\n        HttpMethod.redirectsWithBody(method) ||\n          responseCode == HTTP_PERM_REDIRECT ||\n          responseCode == HTTP_TEMP_REDIRECT\n      if (HttpMethod.redirectsToGet(method) && responseCode != HTTP_PERM_REDIRECT && responseCode != HTTP_TEMP_REDIRECT) {\n        requestBuilder.method(\"GET\", null)\n      } else {\n        val requestBody = if (maintainBody) userResponse.request.body else null\n        requestBuilder.method(method, requestBody)\n      }\n      if (!maintainBody) {\n        requestBuilder.removeHeader(\"Transfer-Encoding\")\n        requestBuilder.removeHeader(\"Content-Length\")\n        requestBuilder.removeHeader(\"Content-Type\")\n      }\n    }\n\n    // When redirecting across hosts, drop all authentication headers. This\n    // is potentially annoying to the application layer since they have no\n    // way to retain them.\n    if (!userResponse.request.url.canReuseConnectionFor(url)) {\n      requestBuilder.removeHeader(\"Authorization\")\n    }\n\n    return requestBuilder.url(url).build()\n  }\n\n  private fun retryAfter(\n    userResponse: Response,\n    defaultDelay: Int,\n  ): Int {\n    val header = userResponse.header(\"Retry-After\") ?: return defaultDelay\n\n    // https://tools.ietf.org/html/rfc7231#section-7.1.3\n    // currently ignores a HTTP-date, and assumes any non int 0 is a delay\n    if (header.matches(\"\\\\d+\".toRegex())) {\n      return Integer.valueOf(header)\n    }\n    return Integer.MAX_VALUE\n  }\n\n  companion object {\n    /**\n     * How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox,\n     * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.\n     */\n    private const val MAX_FOLLOW_UPS = 20\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/StatusLine.kt",
    "content": "/*\n * Copyright (c) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage okhttp3.internal.http\n\nimport java.net.ProtocolException\nimport okhttp3.Protocol\nimport okhttp3.Response\nimport okio.IOException\n\n/** An HTTP response status line like \"HTTP/1.1 200 OK\". */\nclass StatusLine(\n  @JvmField val protocol: Protocol,\n  @JvmField val code: Int,\n  @JvmField val message: String,\n) {\n  override fun toString(): String =\n    buildString {\n      if (protocol == Protocol.HTTP_1_0) {\n        append(\"HTTP/1.0\")\n      } else {\n        append(\"HTTP/1.1\")\n      }\n      append(' ').append(code)\n      append(' ').append(message)\n    }\n\n  companion object {\n    fun get(response: Response): StatusLine = StatusLine(response.protocol, response.code, response.message)\n\n    @Throws(IOException::class)\n    fun parse(statusLine: String): StatusLine {\n      // H T T P / 1 . 1   2 0 0   T e m p o r a r y   R e d i r e c t\n      // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0\n\n      // Parse protocol like \"HTTP/1.1\" followed by a space.\n      val codeStart: Int\n      val protocol: Protocol\n      if (statusLine.startsWith(\"HTTP/1.\")) {\n        if (statusLine.length < 9 || statusLine[8] != ' ') {\n          throw ProtocolException(\"Unexpected status line: $statusLine\")\n        }\n        val httpMinorVersion = statusLine[7] - '0'\n        codeStart = 9\n        protocol =\n          when (httpMinorVersion) {\n            0 -> Protocol.HTTP_1_0\n            1 -> Protocol.HTTP_1_1\n            else -> throw ProtocolException(\"Unexpected status line: $statusLine\")\n          }\n      } else if (statusLine.startsWith(\"ICY \")) {\n        // Shoutcast uses ICY instead of \"HTTP/1.0\".\n        protocol = Protocol.HTTP_1_0\n        codeStart = 4\n      } else if (statusLine.startsWith(\"SOURCETABLE \")) {\n        // NTRIP r1 uses SOURCETABLE instead of HTTP/1.1\n        protocol = Protocol.HTTP_1_1\n        codeStart = 12\n      } else {\n        throw ProtocolException(\"Unexpected status line: $statusLine\")\n      }\n\n      // Parse response code like \"200\". Always 3 digits.\n      if (statusLine.length < codeStart + 3) {\n        throw ProtocolException(\"Unexpected status line: $statusLine\")\n      }\n      val code =\n        statusLine.substring(codeStart, codeStart + 3).toIntOrNull()\n          ?: throw ProtocolException(\n            \"Unexpected status line: $statusLine\",\n          )\n\n      // Parse an optional response message like \"OK\" or \"Not Modified\". If it\n      // exists, it is separated from the response code by a space.\n      var message = \"\"\n      if (statusLine.length > codeStart + 3) {\n        if (statusLine[codeStart + 3] != ' ') {\n          throw ProtocolException(\"Unexpected status line: $statusLine\")\n        }\n        message = statusLine.substring(codeStart + 4)\n      }\n\n      return StatusLine(protocol, code, message)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http1/HeadersReader.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http1\n\nimport okhttp3.Headers\nimport okhttp3.internal.HEADER_LIMIT\nimport okio.BufferedSource\n\n/**\n * Parse all headers delimited by \"\\r\\n\" until an empty line. This throws if headers exceed 256 KiB.\n */\nclass HeadersReader(\n  val source: BufferedSource,\n) {\n  private var headerLimit = HEADER_LIMIT\n\n  /** Read a single line counted against the header size limit. */\n  fun readLine(): String {\n    val line = source.readUtf8LineStrict(headerLimit)\n    headerLimit -= line.length.toLong()\n    return line\n  }\n\n  /** Reads headers or trailers. */\n  fun readHeaders(): Headers {\n    val result = Headers.Builder()\n    while (true) {\n      val line = readLine()\n      if (line.isEmpty()) break\n      result.addLenient(line)\n    }\n    return result.build()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http1/Http1ExchangeCodec.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http1\n\nimport java.io.EOFException\nimport java.io.IOException\nimport java.net.ProtocolException\nimport java.util.concurrent.TimeUnit.MILLISECONDS\nimport okhttp3.Headers\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.checkOffsetAndCount\nimport okhttp3.internal.connection.BufferedSocket\nimport okhttp3.internal.discard\nimport okhttp3.internal.headersContentLength\nimport okhttp3.internal.http.ExchangeCodec\nimport okhttp3.internal.http.HTTP_CONTINUE\nimport okhttp3.internal.http.RequestLine\nimport okhttp3.internal.http.StatusLine\nimport okhttp3.internal.http.promisesBody\nimport okhttp3.internal.http.receiveHeaders\nimport okhttp3.internal.http1.Http1ExchangeCodec.Companion.TRAILERS_RESPONSE_BODY_TRUNCATED\nimport okhttp3.internal.skipAll\nimport okio.Buffer\nimport okio.ForwardingTimeout\nimport okio.Sink\nimport okio.Source\nimport okio.Timeout\n\n/**\n * A socket connection that can be used to send HTTP/1.1 messages. This class strictly enforces the\n * following lifecycle:\n *\n *  1. [Send request headers][writeRequest].\n *  2. Open a sink to write the request body. Either [known][newKnownLengthSink] or\n *     [chunked][newChunkedSink].\n *  3. Write to and then close that sink.\n *  4. [Read response headers][readResponseHeaders].\n *  5. Open a source to read the response body. Either [fixed-length][newFixedLengthSource],\n *     [chunked][newChunkedSource] or [unknown][newUnknownLengthSource].\n *  6. Read from and close that source.\n *\n * Exchanges that do not have a request body may skip creating and closing the request body.\n * Exchanges that do not have a response body can call\n * [newFixedLengthSource(0)][newFixedLengthSource] and may skip reading and closing that source.\n */\nclass Http1ExchangeCodec(\n  /** The client that configures this stream. May be null for HTTPS proxy tunnels. */\n  private val client: OkHttpClient?,\n  override val carrier: ExchangeCodec.Carrier,\n  override val socket: BufferedSocket,\n) : ExchangeCodec {\n  private var state = STATE_IDLE\n  private val headersReader = HeadersReader(socket.source)\n\n  private val Response.isChunked: Boolean\n    get() = \"chunked\".equals(header(\"Transfer-Encoding\"), ignoreCase = true)\n\n  private val Request.isChunked: Boolean\n    get() = \"chunked\".equals(header(\"Transfer-Encoding\"), ignoreCase = true)\n\n  /**\n   * Trailers received when the response body became exhausted.\n   *\n   * If the response body was successfully read until the end, this is the headers that followed,\n   * or empty headers if there were none that followed.\n   *\n   * If the response body was closed prematurely or failed with an error, this will be the sentinel\n   * value [TRAILERS_RESPONSE_BODY_TRUNCATED]. In that case attempts to read the trailers should not\n   * return the value but instead throw an exception.\n   */\n  private var trailers: Headers? = null\n\n  override val isResponseComplete: Boolean\n    get() = state == STATE_CLOSED\n\n  override fun createRequestBody(\n    request: Request,\n    contentLength: Long,\n  ): Sink =\n    when {\n      request.body?.isDuplex() == true -> {\n        throw ProtocolException(\n          \"Duplex connections are not supported for HTTP/1\",\n        )\n      }\n\n      request.isChunked -> {\n        newChunkedSink()\n      }\n\n      // Stream a request body of unknown length.\n      contentLength != -1L -> {\n        newKnownLengthSink()\n      }\n\n      // Stream a request body of a known length.\n      else -> {\n        // Stream a request body of a known length.\n        throw IllegalStateException(\n          \"Cannot stream a request body without chunked encoding or a known content length!\",\n        )\n      }\n    }\n\n  override fun cancel() {\n    carrier.cancel()\n  }\n\n  /**\n   * Prepares the HTTP headers and sends them to the server.\n   *\n   * For streaming requests with a body, headers must be prepared **before** the output stream has\n   * been written to. Otherwise the body would need to be buffered!\n   *\n   * For non-streaming requests with a body, headers must be prepared **after** the output stream\n   * has been written to and closed. This ensures that the `Content-Length` header field receives\n   * the proper value.\n   */\n  override fun writeRequestHeaders(request: Request) {\n    val requestLine = RequestLine.get(request, carrier.route.proxy.type())\n    writeRequest(request.headers, requestLine)\n  }\n\n  override fun reportedContentLength(response: Response): Long =\n    when {\n      !response.promisesBody() -> 0L\n      response.isChunked -> -1L\n      else -> response.headersContentLength()\n    }\n\n  override fun openResponseBodySource(response: Response): Source =\n    when {\n      !response.promisesBody() -> {\n        newFixedLengthSource(response.request.url, 0)\n      }\n\n      response.isChunked -> {\n        newChunkedSource(response.request.url)\n      }\n\n      else -> {\n        val contentLength = response.headersContentLength()\n        if (contentLength != -1L) {\n          newFixedLengthSource(response.request.url, contentLength)\n        } else {\n          newUnknownLengthSource(response.request.url)\n        }\n      }\n    }\n\n  override fun peekTrailers(): Headers? {\n    if (trailers === TRAILERS_RESPONSE_BODY_TRUNCATED) {\n      throw IOException(\"Trailers cannot be read because the response body was truncated\")\n    }\n    check(state == STATE_READING_RESPONSE_BODY || state == STATE_CLOSED) {\n      \"Trailers cannot be read because the state is $state\"\n    }\n    return trailers\n  }\n\n  override fun flushRequest() {\n    socket.sink.flush()\n  }\n\n  override fun finishRequest() {\n    socket.sink.flush()\n  }\n\n  /** Returns bytes of a request header for sending on an HTTP transport. */\n  fun writeRequest(\n    headers: Headers,\n    requestLine: String,\n  ) {\n    check(state == STATE_IDLE) { \"state: $state\" }\n    socket.sink.writeUtf8(requestLine).writeUtf8(\"\\r\\n\")\n    for (i in 0 until headers.size) {\n      socket.sink\n        .writeUtf8(headers.name(i))\n        .writeUtf8(\": \")\n        .writeUtf8(headers.value(i))\n        .writeUtf8(\"\\r\\n\")\n    }\n    socket.sink.writeUtf8(\"\\r\\n\")\n    state = STATE_OPEN_REQUEST_BODY\n  }\n\n  override fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {\n    check(\n      state == STATE_IDLE ||\n        state == STATE_OPEN_REQUEST_BODY ||\n        state == STATE_WRITING_REQUEST_BODY ||\n        state == STATE_READ_RESPONSE_HEADERS,\n    ) {\n      \"state: $state\"\n    }\n\n    try {\n      val statusLine = StatusLine.parse(headersReader.readLine())\n\n      val responseBuilder =\n        Response\n          .Builder()\n          .protocol(statusLine.protocol)\n          .code(statusLine.code)\n          .message(statusLine.message)\n          .headers(headersReader.readHeaders())\n\n      return when {\n        expectContinue && statusLine.code == HTTP_CONTINUE -> {\n          null\n        }\n\n        statusLine.code == HTTP_CONTINUE -> {\n          state = STATE_READ_RESPONSE_HEADERS\n          responseBuilder\n        }\n\n        statusLine.code in (102 until 200) -> {\n          // Processing and Early Hints will mean a second headers are coming.\n          // Treat others the same for now\n          state = STATE_READ_RESPONSE_HEADERS\n          responseBuilder\n        }\n\n        else -> {\n          state = STATE_OPEN_RESPONSE_BODY\n          responseBuilder\n        }\n      }\n    } catch (e: EOFException) {\n      // Provide more context if the server ends the stream before sending a response.\n      val address =\n        carrier.route.address.url\n          .redact()\n      throw IOException(\"unexpected end of stream on $address\", e)\n    }\n  }\n\n  private fun newChunkedSink(): Sink {\n    check(state == STATE_OPEN_REQUEST_BODY) { \"state: $state\" }\n    state = STATE_WRITING_REQUEST_BODY\n    return ChunkedSink()\n  }\n\n  private fun newKnownLengthSink(): Sink {\n    check(state == STATE_OPEN_REQUEST_BODY) { \"state: $state\" }\n    state = STATE_WRITING_REQUEST_BODY\n    return KnownLengthSink()\n  }\n\n  private fun newFixedLengthSource(\n    url: HttpUrl,\n    length: Long,\n  ): Source {\n    check(state == STATE_OPEN_RESPONSE_BODY) { \"state: $state\" }\n    state = STATE_READING_RESPONSE_BODY\n    return FixedLengthSource(url, length)\n  }\n\n  private fun newChunkedSource(url: HttpUrl): Source {\n    check(state == STATE_OPEN_RESPONSE_BODY) { \"state: $state\" }\n    state = STATE_READING_RESPONSE_BODY\n    return ChunkedSource(url)\n  }\n\n  private fun newUnknownLengthSource(url: HttpUrl): Source {\n    check(state == STATE_OPEN_RESPONSE_BODY) { \"state: $state\" }\n    state = STATE_READING_RESPONSE_BODY\n    carrier.noNewExchanges()\n    return UnknownLengthSource(url)\n  }\n\n  /**\n   * Sets the delegate of `timeout` to [Timeout.NONE] and resets its underlying timeout\n   * to the default configuration. Use this to avoid unexpected sharing of timeouts between pooled\n   * connections.\n   */\n  private fun detachTimeout(timeout: ForwardingTimeout) {\n    val oldDelegate = timeout.delegate\n    timeout.setDelegate(Timeout.NONE)\n    oldDelegate.clearDeadline()\n    oldDelegate.clearTimeout()\n  }\n\n  /**\n   * The response body from a CONNECT should be empty, but if it is not then we should consume it\n   * before proceeding.\n   */\n  fun skipConnectBody(response: Response) {\n    val contentLength = response.headersContentLength()\n    if (contentLength == -1L) return\n    val body = newFixedLengthSource(response.request.url, contentLength)\n    body.skipAll(Int.MAX_VALUE, MILLISECONDS)\n    body.close()\n  }\n\n  /** An HTTP request body. */\n  private inner class KnownLengthSink : Sink {\n    private val timeout = ForwardingTimeout(socket.sink.timeout())\n    private var closed: Boolean = false\n\n    override fun timeout(): Timeout = timeout\n\n    override fun write(\n      source: Buffer,\n      byteCount: Long,\n    ) {\n      check(!closed) { \"closed\" }\n      checkOffsetAndCount(source.size, 0, byteCount)\n      socket.sink.write(source, byteCount)\n    }\n\n    override fun flush() {\n      if (closed) return // Don't throw; this stream might have been closed on the caller's behalf.\n      socket.sink.flush()\n    }\n\n    override fun close() {\n      if (closed) return\n      closed = true\n      detachTimeout(timeout)\n      state = STATE_READ_RESPONSE_HEADERS\n    }\n  }\n\n  /**\n   * An HTTP body with alternating chunk sizes and chunk bodies. It is the caller's responsibility\n   * to buffer chunks; typically by using a buffered sink with this sink.\n   */\n  private inner class ChunkedSink : Sink {\n    private val timeout = ForwardingTimeout(socket.sink.timeout())\n    private var closed: Boolean = false\n\n    override fun timeout(): Timeout = timeout\n\n    override fun write(\n      source: Buffer,\n      byteCount: Long,\n    ) {\n      check(!closed) { \"closed\" }\n      if (byteCount == 0L) return\n\n      with(socket.sink) {\n        writeHexadecimalUnsignedLong(byteCount)\n        writeUtf8(\"\\r\\n\")\n        write(source, byteCount)\n        writeUtf8(\"\\r\\n\")\n      }\n    }\n\n    @Synchronized\n    override fun flush() {\n      if (closed) return // Don't throw; this stream might have been closed on the caller's behalf.\n      socket.sink.flush()\n    }\n\n    @Synchronized\n    override fun close() {\n      if (closed) return\n      closed = true\n      socket.sink.writeUtf8(\"0\\r\\n\\r\\n\")\n      detachTimeout(timeout)\n      state = STATE_READ_RESPONSE_HEADERS\n    }\n  }\n\n  private abstract inner class AbstractSource(\n    val url: HttpUrl,\n  ) : Source {\n    protected val timeout = ForwardingTimeout(socket.source.timeout())\n    protected var closed: Boolean = false\n\n    override fun timeout(): Timeout = timeout\n\n    override fun read(\n      sink: Buffer,\n      byteCount: Long,\n    ): Long =\n      try {\n        socket.source.read(sink, byteCount)\n      } catch (e: IOException) {\n        carrier.noNewExchanges()\n        responseBodyComplete(TRAILERS_RESPONSE_BODY_TRUNCATED)\n        throw e\n      }\n\n    /**\n     * Closes the cache entry and makes the socket available for reuse. This should be invoked when\n     * the end of the body has been reached.\n     */\n    fun responseBodyComplete(trailers: Headers) {\n      if (state == STATE_CLOSED) return\n      if (state != STATE_READING_RESPONSE_BODY) throw IllegalStateException(\"state: $state\")\n\n      detachTimeout(timeout)\n\n      this@Http1ExchangeCodec.trailers = trailers\n      state = STATE_CLOSED\n      if (trailers.size > 0) {\n        client?.cookieJar?.receiveHeaders(url, trailers)\n      }\n    }\n  }\n\n  /** An HTTP body with a fixed length specified in advance. */\n  private inner class FixedLengthSource(\n    url: HttpUrl,\n    private var bytesRemaining: Long,\n  ) : AbstractSource(url) {\n    init {\n      if (bytesRemaining == 0L) {\n        responseBodyComplete(trailers = Headers.EMPTY)\n      }\n    }\n\n    override fun read(\n      sink: Buffer,\n      byteCount: Long,\n    ): Long {\n      require(byteCount >= 0L) { \"byteCount < 0: $byteCount\" }\n      check(!closed) { \"closed\" }\n      if (bytesRemaining == 0L) return -1\n\n      val read = super.read(sink, minOf(bytesRemaining, byteCount))\n      if (read == -1L) {\n        carrier.noNewExchanges() // The server didn't supply the promised content length.\n        val e = ProtocolException(\"unexpected end of stream\")\n        responseBodyComplete(TRAILERS_RESPONSE_BODY_TRUNCATED)\n        throw e\n      }\n\n      bytesRemaining -= read\n      if (bytesRemaining == 0L) {\n        responseBodyComplete(trailers = Headers.EMPTY)\n      }\n      return read\n    }\n\n    override fun close() {\n      if (closed) return\n\n      if (bytesRemaining != 0L &&\n        !discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)\n      ) {\n        carrier.noNewExchanges() // Unread bytes remain on the stream.\n        responseBodyComplete(TRAILERS_RESPONSE_BODY_TRUNCATED)\n      }\n\n      closed = true\n    }\n  }\n\n  /** An HTTP body with alternating chunk sizes and chunk bodies. */\n  private inner class ChunkedSource(\n    url: HttpUrl,\n  ) : AbstractSource(url) {\n    private var bytesRemainingInChunk = NO_CHUNK_YET\n    private var hasMoreChunks = true\n\n    override fun read(\n      sink: Buffer,\n      byteCount: Long,\n    ): Long {\n      require(byteCount >= 0L) { \"byteCount < 0: $byteCount\" }\n      check(!closed) { \"closed\" }\n      if (!hasMoreChunks) return -1\n\n      if (bytesRemainingInChunk == 0L || bytesRemainingInChunk == NO_CHUNK_YET) {\n        readChunkSize()\n        if (!hasMoreChunks) return -1\n      }\n\n      val read = super.read(sink, minOf(byteCount, bytesRemainingInChunk))\n      if (read == -1L) {\n        carrier.noNewExchanges() // The server didn't supply the promised chunk length.\n        val e = ProtocolException(\"unexpected end of stream\")\n        responseBodyComplete(TRAILERS_RESPONSE_BODY_TRUNCATED)\n        throw e\n      }\n      bytesRemainingInChunk -= read\n      return read\n    }\n\n    private fun readChunkSize() {\n      // Read the suffix of the previous chunk.\n      if (bytesRemainingInChunk != NO_CHUNK_YET) {\n        socket.source.readUtf8LineStrict()\n      }\n      try {\n        bytesRemainingInChunk = socket.source.readHexadecimalUnsignedLong()\n        val extensions = socket.source.readUtf8LineStrict().trim()\n        if ((bytesRemainingInChunk < 0L) || (extensions.isNotEmpty() && !extensions.startsWith(\";\"))) {\n          throw ProtocolException(\n            \"expected chunk size and optional extensions\" +\n              \" but was \\\"$bytesRemainingInChunk$extensions\\\"\",\n          )\n        }\n      } catch (e: NumberFormatException) {\n        throw ProtocolException(e.message)\n      }\n\n      if (bytesRemainingInChunk == 0L) {\n        hasMoreChunks = false\n        val trailers = headersReader.readHeaders()\n        responseBodyComplete(trailers)\n      }\n    }\n\n    override fun close() {\n      if (closed) return\n      if (hasMoreChunks &&\n        !discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)\n      ) {\n        carrier.noNewExchanges() // Unread bytes remain on the stream.\n        responseBodyComplete(TRAILERS_RESPONSE_BODY_TRUNCATED)\n      }\n      closed = true\n    }\n  }\n\n  /** An HTTP message body terminated by the end of the underlying stream. */\n  private inner class UnknownLengthSource(\n    url: HttpUrl,\n  ) : AbstractSource(url) {\n    private var inputExhausted: Boolean = false\n\n    override fun read(\n      sink: Buffer,\n      byteCount: Long,\n    ): Long {\n      require(byteCount >= 0L) { \"byteCount < 0: $byteCount\" }\n      check(!closed) { \"closed\" }\n      if (inputExhausted) return -1\n\n      val read = super.read(sink, byteCount)\n      if (read == -1L) {\n        inputExhausted = true\n        responseBodyComplete(trailers = Headers.EMPTY)\n        return -1\n      }\n      return read\n    }\n\n    override fun close() {\n      if (closed) return\n      if (!inputExhausted) {\n        responseBodyComplete(TRAILERS_RESPONSE_BODY_TRUNCATED)\n      }\n      closed = true\n    }\n  }\n\n  companion object {\n    private const val NO_CHUNK_YET = -1L\n\n    private const val STATE_IDLE = 0 // Idle connections are ready to write request headers.\n    private const val STATE_OPEN_REQUEST_BODY = 1\n    private const val STATE_WRITING_REQUEST_BODY = 2\n    private const val STATE_READ_RESPONSE_HEADERS = 3\n    private const val STATE_OPEN_RESPONSE_BODY = 4\n    private const val STATE_READING_RESPONSE_BODY = 5\n    private const val STATE_CLOSED = 6\n\n    private val TRAILERS_RESPONSE_BODY_TRUNCATED = headersOf(\"OkHttp-Response-Body\", \"Truncated\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/ConnectionShutdownException.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.IOException\n\n/**\n * Thrown when an HTTP/2 connection is shutdown (either explicitly or if the peer has sent a GOAWAY\n * frame) and an attempt is made to use the connection.\n */\nclass ConnectionShutdownException : IOException()\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/ErrorCode.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\n/** http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-7 */\nenum class ErrorCode constructor(\n  val httpCode: Int,\n) {\n  /** Not an error!  */\n  NO_ERROR(0),\n\n  PROTOCOL_ERROR(1),\n\n  INTERNAL_ERROR(2),\n\n  FLOW_CONTROL_ERROR(3),\n\n  SETTINGS_TIMEOUT(4),\n\n  STREAM_CLOSED(5),\n\n  FRAME_SIZE_ERROR(6),\n\n  REFUSED_STREAM(7),\n\n  CANCEL(8),\n\n  COMPRESSION_ERROR(9),\n\n  CONNECT_ERROR(0xa),\n\n  ENHANCE_YOUR_CALM(0xb),\n\n  INADEQUATE_SECURITY(0xc),\n\n  HTTP_1_1_REQUIRED(0xd),\n  ;\n\n  companion object {\n    fun fromHttp2(code: Int): ErrorCode? = values().find { it.httpCode == code }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/FlowControlListener.kt",
    "content": "/*\n * Copyright (C) 2023 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport okhttp3.internal.http2.flowcontrol.WindowCounter\n\ninterface FlowControlListener {\n  /**\n   * Notification that the receiving stream flow control window has changed.\n   * [WindowCounter] generally carries the client view of total and acked bytes.\n   */\n  fun receivingStreamWindowChanged(\n    streamId: Int,\n    windowCounter: WindowCounter,\n    bufferSize: Long,\n  )\n\n  /**\n   * Notification that the receiving connection flow control window has changed.\n   * [WindowCounter] generally carries the client view of total and acked bytes.\n   */\n  fun receivingConnectionWindowChanged(windowCounter: WindowCounter)\n\n  /** Noop implementation */\n  object None : FlowControlListener {\n    override fun receivingStreamWindowChanged(\n      streamId: Int,\n      windowCounter: WindowCounter,\n      bufferSize: Long,\n    ) {\n    }\n\n    override fun receivingConnectionWindowChanged(windowCounter: WindowCounter) {\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Header.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\n\n/** HTTP header: the name is an ASCII string, but the value can be UTF-8. */\ndata class Header(\n  /** Name in case-insensitive ASCII encoding. */\n  @JvmField val name: ByteString,\n  /** Value in UTF-8 encoding. */\n  @JvmField val value: ByteString,\n) {\n  @JvmField val hpackSize = 32 + name.size + value.size\n\n  // TODO: search for toLowerCase and consider moving logic here.\n  constructor(name: String, value: String) : this(name.encodeUtf8(), value.encodeUtf8())\n\n  constructor(name: ByteString, value: String) : this(name, value.encodeUtf8())\n\n  override fun toString(): String = \"${name.utf8()}: ${value.utf8()}\"\n\n  companion object {\n    // Special header names defined in HTTP/2 spec.\n    @JvmField val PSEUDO_PREFIX: ByteString = \":\".encodeUtf8()\n\n    const val RESPONSE_STATUS_UTF8 = \":status\"\n    const val TARGET_METHOD_UTF8 = \":method\"\n    const val TARGET_PATH_UTF8 = \":path\"\n    const val TARGET_SCHEME_UTF8 = \":scheme\"\n    const val TARGET_AUTHORITY_UTF8 = \":authority\"\n\n    @JvmField val RESPONSE_STATUS: ByteString = RESPONSE_STATUS_UTF8.encodeUtf8()\n\n    @JvmField val TARGET_METHOD: ByteString = TARGET_METHOD_UTF8.encodeUtf8()\n\n    @JvmField val TARGET_PATH: ByteString = TARGET_PATH_UTF8.encodeUtf8()\n\n    @JvmField val TARGET_SCHEME: ByteString = TARGET_SCHEME_UTF8.encodeUtf8()\n\n    @JvmField val TARGET_AUTHORITY: ByteString = TARGET_AUTHORITY_UTF8.encodeUtf8()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Hpack.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.IOException\nimport java.util.Arrays\nimport okhttp3.internal.HEADER_LIMIT\nimport okhttp3.internal.and\nimport okhttp3.internal.http2.Header.Companion.RESPONSE_STATUS\nimport okhttp3.internal.http2.Header.Companion.TARGET_AUTHORITY\nimport okhttp3.internal.http2.Header.Companion.TARGET_METHOD\nimport okhttp3.internal.http2.Header.Companion.TARGET_PATH\nimport okhttp3.internal.http2.Header.Companion.TARGET_SCHEME\nimport okhttp3.internal.unmodifiable\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.Source\nimport okio.buffer\n\n/**\n * Read and write HPACK v10.\n *\n * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12\n *\n * This implementation uses an array for the dynamic table and a list for indexed entries. Dynamic\n * entries are added to the array, starting in the last position moving forward. When the array\n * fills, it is doubled.\n */\n@Suppress(\"NAME_SHADOWING\")\nobject Hpack {\n  private const val PREFIX_4_BITS = 0x0f\n  private const val PREFIX_5_BITS = 0x1f\n  private const val PREFIX_6_BITS = 0x3f\n  private const val PREFIX_7_BITS = 0x7f\n\n  private const val SETTINGS_HEADER_TABLE_SIZE = 4_096\n\n  /**\n   * The decoder has ultimate control of the maximum size of the dynamic table but we can choose\n   * to use less. We'll put a cap at 16K. This is arbitrary but should be enough for most purposes.\n   */\n  private const val SETTINGS_HEADER_TABLE_SIZE_LIMIT = 16_384\n\n  val STATIC_HEADER_TABLE =\n    arrayOf(\n      Header(TARGET_AUTHORITY, \"\"),\n      Header(TARGET_METHOD, \"GET\"),\n      Header(TARGET_METHOD, \"POST\"),\n      Header(TARGET_PATH, \"/\"),\n      Header(TARGET_PATH, \"/index.html\"),\n      Header(TARGET_SCHEME, \"http\"),\n      Header(TARGET_SCHEME, \"https\"),\n      Header(RESPONSE_STATUS, \"200\"),\n      Header(RESPONSE_STATUS, \"204\"),\n      Header(RESPONSE_STATUS, \"206\"),\n      Header(RESPONSE_STATUS, \"304\"),\n      Header(RESPONSE_STATUS, \"400\"),\n      Header(RESPONSE_STATUS, \"404\"),\n      Header(RESPONSE_STATUS, \"500\"),\n      Header(\"accept-charset\", \"\"),\n      Header(\"accept-encoding\", \"gzip, deflate\"),\n      Header(\"accept-language\", \"\"),\n      Header(\"accept-ranges\", \"\"),\n      Header(\"accept\", \"\"),\n      Header(\"access-control-allow-origin\", \"\"),\n      Header(\"age\", \"\"),\n      Header(\"allow\", \"\"),\n      Header(\"authorization\", \"\"),\n      Header(\"cache-control\", \"\"),\n      Header(\"content-disposition\", \"\"),\n      Header(\"content-encoding\", \"\"),\n      Header(\"content-language\", \"\"),\n      Header(\"content-length\", \"\"),\n      Header(\"content-location\", \"\"),\n      Header(\"content-range\", \"\"),\n      Header(\"content-type\", \"\"),\n      Header(\"cookie\", \"\"),\n      Header(\"date\", \"\"),\n      Header(\"etag\", \"\"),\n      Header(\"expect\", \"\"),\n      Header(\"expires\", \"\"),\n      Header(\"from\", \"\"),\n      Header(\"host\", \"\"),\n      Header(\"if-match\", \"\"),\n      Header(\"if-modified-since\", \"\"),\n      Header(\"if-none-match\", \"\"),\n      Header(\"if-range\", \"\"),\n      Header(\"if-unmodified-since\", \"\"),\n      Header(\"last-modified\", \"\"),\n      Header(\"link\", \"\"),\n      Header(\"location\", \"\"),\n      Header(\"max-forwards\", \"\"),\n      Header(\"proxy-authenticate\", \"\"),\n      Header(\"proxy-authorization\", \"\"),\n      Header(\"range\", \"\"),\n      Header(\"referer\", \"\"),\n      Header(\"refresh\", \"\"),\n      Header(\"retry-after\", \"\"),\n      Header(\"server\", \"\"),\n      Header(\"set-cookie\", \"\"),\n      Header(\"strict-transport-security\", \"\"),\n      Header(\"transfer-encoding\", \"\"),\n      Header(\"user-agent\", \"\"),\n      Header(\"vary\", \"\"),\n      Header(\"via\", \"\"),\n      Header(\"www-authenticate\", \"\"),\n    )\n\n  val NAME_TO_FIRST_INDEX = nameToFirstIndex()\n\n  // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-3.1\n  class Reader\n    @JvmOverloads\n    constructor(\n      source: Source,\n      private val headerTableSizeSetting: Int,\n      private var maxDynamicTableByteCount: Int = headerTableSizeSetting,\n    ) {\n      private val headerList = mutableListOf<Header>()\n      private var headerListByteCount = 0L\n      private val source: BufferedSource = source.buffer()\n\n      // Visible for testing.\n      @JvmField var dynamicTable = arrayOfNulls<Header>(8)\n\n      // Array is populated back to front, so new entries always have lowest index.\n      private var nextHeaderIndex = dynamicTable.size - 1\n\n      @JvmField var headerCount = 0\n\n      @JvmField var dynamicTableByteCount = 0\n\n      fun getAndResetHeaderList(): List<Header> {\n        val result = headerList.toList()\n        headerList.clear()\n        headerListByteCount = 0L\n        return result\n      }\n\n      fun maxDynamicTableByteCount(): Int = maxDynamicTableByteCount\n\n      private fun adjustDynamicTableByteCount() {\n        if (maxDynamicTableByteCount < dynamicTableByteCount) {\n          if (maxDynamicTableByteCount == 0) {\n            clearDynamicTable()\n          } else {\n            evictToRecoverBytes(dynamicTableByteCount - maxDynamicTableByteCount)\n          }\n        }\n      }\n\n      private fun clearDynamicTable() {\n        dynamicTable.fill(null)\n        nextHeaderIndex = dynamicTable.size - 1\n        headerCount = 0\n        dynamicTableByteCount = 0\n      }\n\n      /** Returns the count of entries evicted. */\n      private fun evictToRecoverBytes(bytesToRecover: Int): Int {\n        var bytesToRecover = bytesToRecover\n        var entriesToEvict = 0\n        if (bytesToRecover > 0) {\n          // determine how many headers need to be evicted.\n          var j = dynamicTable.size - 1\n          while (j >= nextHeaderIndex && bytesToRecover > 0) {\n            val toEvict = dynamicTable[j]!!\n            bytesToRecover -= toEvict.hpackSize\n            dynamicTableByteCount -= toEvict.hpackSize\n            headerCount--\n            entriesToEvict++\n            j--\n          }\n          System.arraycopy(\n            dynamicTable,\n            nextHeaderIndex + 1,\n            dynamicTable,\n            nextHeaderIndex + 1 + entriesToEvict,\n            headerCount,\n          )\n          nextHeaderIndex += entriesToEvict\n        }\n        return entriesToEvict\n      }\n\n      /**\n       * Read `byteCount` bytes of headers from the source stream. This implementation does not\n       * propagate the never indexed flag of a header.\n       */\n      @Throws(IOException::class)\n      fun readHeaders() {\n        while (!source.exhausted()) {\n          val b = source.readByte() and 0xff\n          when {\n            b == 0x80 -> {\n              // 10000000\n              throw IOException(\"index == 0\")\n            }\n\n            b and 0x80 == 0x80 -> {\n              // 1NNNNNNN\n              val index = readInt(b, PREFIX_7_BITS)\n              readIndexedHeader(index - 1)\n            }\n\n            b == 0x40 -> {\n              // 01000000\n              readLiteralHeaderWithIncrementalIndexingNewName()\n            }\n\n            b and 0x40 == 0x40 -> {\n              // 01NNNNNN\n              val index = readInt(b, PREFIX_6_BITS)\n              readLiteralHeaderWithIncrementalIndexingIndexedName(index - 1)\n            }\n\n            b and 0x20 == 0x20 -> {\n              // 001NNNNN\n              maxDynamicTableByteCount = readInt(b, PREFIX_5_BITS)\n              if (maxDynamicTableByteCount < 0 || maxDynamicTableByteCount > headerTableSizeSetting) {\n                throw IOException(\"Invalid dynamic table size update $maxDynamicTableByteCount\")\n              }\n              adjustDynamicTableByteCount()\n            }\n\n            b == 0x10 || b == 0 -> {\n              // 000?0000 - Ignore never indexed bit.\n              readLiteralHeaderWithoutIndexingNewName()\n            }\n\n            else -> {\n              // 000?NNNN - Ignore never indexed bit.\n              val index = readInt(b, PREFIX_4_BITS)\n              readLiteralHeaderWithoutIndexingIndexedName(index - 1)\n            }\n          }\n        }\n      }\n\n      @Throws(IOException::class)\n      private fun readIndexedHeader(index: Int) {\n        if (isStaticHeader(index)) {\n          val staticEntry = STATIC_HEADER_TABLE[index]\n          addHeader(staticEntry)\n        } else {\n          val dynamicTableIndex = dynamicTableIndex(index - STATIC_HEADER_TABLE.size)\n          if (dynamicTableIndex < 0 || dynamicTableIndex >= dynamicTable.size) {\n            throw IOException(\"Header index too large ${index + 1}\")\n          }\n          addHeader(dynamicTable[dynamicTableIndex]!!)\n        }\n      }\n\n      // referencedHeaders is relative to nextHeaderIndex + 1.\n      private fun dynamicTableIndex(index: Int): Int = nextHeaderIndex + 1 + index\n\n      @Throws(IOException::class)\n      private fun readLiteralHeaderWithoutIndexingIndexedName(index: Int) {\n        val name = getName(index)\n        val value = readByteString()\n        addHeader(Header(name, value))\n      }\n\n      @Throws(IOException::class)\n      private fun readLiteralHeaderWithoutIndexingNewName() {\n        val name = checkLowercase(readByteString())\n        val value = readByteString()\n        addHeader(Header(name, value))\n      }\n\n      @Throws(IOException::class)\n      private fun readLiteralHeaderWithIncrementalIndexingIndexedName(nameIndex: Int) {\n        val name = getName(nameIndex)\n        val value = readByteString()\n        insertIntoDynamicTable(-1, Header(name, value))\n      }\n\n      @Throws(IOException::class)\n      private fun readLiteralHeaderWithIncrementalIndexingNewName() {\n        val name = checkLowercase(readByteString())\n        val value = readByteString()\n        insertIntoDynamicTable(-1, Header(name, value))\n      }\n\n      @Throws(IOException::class)\n      private fun getName(index: Int): ByteString =\n        if (isStaticHeader(index)) {\n          STATIC_HEADER_TABLE[index].name\n        } else {\n          val dynamicTableIndex = dynamicTableIndex(index - STATIC_HEADER_TABLE.size)\n          if (dynamicTableIndex < 0 || dynamicTableIndex >= dynamicTable.size) {\n            throw IOException(\"Header index too large ${index + 1}\")\n          }\n\n          dynamicTable[dynamicTableIndex]!!.name\n        }\n\n      private fun isStaticHeader(index: Int): Boolean = index >= 0 && index <= STATIC_HEADER_TABLE.size - 1\n\n      /** index == -1 when new. */\n      private fun insertIntoDynamicTable(\n        index: Int,\n        entry: Header,\n      ) {\n        var index = index\n        addHeader(entry)\n\n        var delta = entry.hpackSize\n        if (index != -1) { // Index -1 == new header.\n          delta -= dynamicTable[dynamicTableIndex(index)]!!.hpackSize\n        }\n\n        // if the new or replacement header is too big, drop all entries.\n        if (delta > maxDynamicTableByteCount) {\n          clearDynamicTable()\n          return\n        }\n\n        // Evict headers to the required length.\n        val bytesToRecover = dynamicTableByteCount + delta - maxDynamicTableByteCount\n        val entriesEvicted = evictToRecoverBytes(bytesToRecover)\n\n        if (index == -1) { // Adding a value to the dynamic table.\n          if (headerCount + 1 > dynamicTable.size) { // Need to grow the dynamic table.\n            val doubled = arrayOfNulls<Header>(dynamicTable.size * 2)\n            System.arraycopy(dynamicTable, 0, doubled, dynamicTable.size, dynamicTable.size)\n            nextHeaderIndex = dynamicTable.size - 1\n            dynamicTable = doubled\n          }\n          index = nextHeaderIndex--\n          dynamicTable[index] = entry\n          headerCount++\n        } else { // Replace value at same position.\n          index += dynamicTableIndex(index) + entriesEvicted\n          dynamicTable[index] = entry\n        }\n        dynamicTableByteCount += delta\n      }\n\n      @Throws(IOException::class)\n      private fun readByte(): Int = source.readByte() and 0xff\n\n      @Throws(IOException::class)\n      fun readInt(\n        firstByte: Int,\n        prefixMask: Int,\n      ): Int {\n        val prefix = firstByte and prefixMask\n        if (prefix < prefixMask) {\n          return prefix // This was a single byte value.\n        }\n\n        // This is a multibyte value. Read 7 bits at a time.\n        var result = prefixMask\n        var shift = 0\n        while (true) {\n          val b = readByte()\n          if (b and 0x80 != 0) { // Equivalent to (b >= 128) since b is in [0..255].\n            result += b and 0x7f shl shift\n            shift += 7\n          } else {\n            result += b shl shift // Last byte.\n            break\n          }\n        }\n        return result\n      }\n\n      /** Reads a potentially Huffman encoded byte string. */\n      @Throws(IOException::class)\n      fun readByteString(): ByteString {\n        val firstByte = readByte()\n        val huffmanDecode = firstByte and 0x80 == 0x80 // 1NNNNNNN\n        val length = readInt(firstByte, PREFIX_7_BITS).toLong()\n\n        // If the compressed or decompressed length exceeds the limit, don't even bother.\n        if (headerListByteCount + length > HEADER_LIMIT) {\n          throw IOException(\"header byte count limit of $HEADER_LIMIT exceeded\")\n        }\n\n        return if (huffmanDecode) {\n          val decodeBuffer = Buffer()\n          Huffman.decode(source, length, decodeBuffer)\n          decodeBuffer.readByteString()\n        } else {\n          source.readByteString(length)\n        }\n      }\n\n      @Throws(IOException::class)\n      private fun addHeader(header: Header) {\n        headerList.add(header)\n\n        val headerSize = header.name.size + header.value.size\n        val newHeaderListSize = headerListByteCount + headerSize\n        headerListByteCount = newHeaderListSize\n        if (newHeaderListSize > HEADER_LIMIT) {\n          throw IOException(\"header byte count limit of $HEADER_LIMIT exceeded\")\n        }\n      }\n    }\n\n  private fun nameToFirstIndex(): Map<ByteString, Int> {\n    val result = LinkedHashMap<ByteString, Int>(STATIC_HEADER_TABLE.size, 1.0F)\n    for (i in STATIC_HEADER_TABLE.indices) {\n      if (!result.containsKey(STATIC_HEADER_TABLE[i].name)) {\n        result[STATIC_HEADER_TABLE[i].name] = i\n      }\n    }\n    return result.unmodifiable()\n  }\n\n  class Writer\n    @JvmOverloads\n    constructor(\n      @JvmField var headerTableSizeSetting: Int = SETTINGS_HEADER_TABLE_SIZE,\n      private val useCompression: Boolean = true,\n      private val out: Buffer,\n    ) {\n      /**\n       * In the scenario where the dynamic table size changes multiple times between transmission of\n       * header blocks, we need to keep track of the smallest value in that interval.\n       */\n      private var smallestHeaderTableSizeSetting = Integer.MAX_VALUE\n      private var emitDynamicTableSizeUpdate: Boolean = false\n\n      @JvmField var maxDynamicTableByteCount: Int = headerTableSizeSetting\n\n      // Visible for testing.\n      @JvmField var dynamicTable = arrayOfNulls<Header>(8)\n\n      // Array is populated back to front, so new entries always have lowest index.\n      private var nextHeaderIndex = dynamicTable.size - 1\n\n      @JvmField var headerCount = 0\n\n      @JvmField var dynamicTableByteCount = 0\n\n      private fun clearDynamicTable() {\n        dynamicTable.fill(null)\n        nextHeaderIndex = dynamicTable.size - 1\n        headerCount = 0\n        dynamicTableByteCount = 0\n      }\n\n      /** Returns the count of entries evicted. */\n      private fun evictToRecoverBytes(bytesToRecover: Int): Int {\n        var bytesToRecover = bytesToRecover\n        var entriesToEvict = 0\n        if (bytesToRecover > 0) {\n          // determine how many headers need to be evicted.\n          var j = dynamicTable.size - 1\n          while (j >= nextHeaderIndex && bytesToRecover > 0) {\n            bytesToRecover -= dynamicTable[j]!!.hpackSize\n            dynamicTableByteCount -= dynamicTable[j]!!.hpackSize\n            headerCount--\n            entriesToEvict++\n            j--\n          }\n          System.arraycopy(\n            dynamicTable,\n            nextHeaderIndex + 1,\n            dynamicTable,\n            nextHeaderIndex + 1 + entriesToEvict,\n            headerCount,\n          )\n          Arrays.fill(dynamicTable, nextHeaderIndex + 1, nextHeaderIndex + 1 + entriesToEvict, null)\n          nextHeaderIndex += entriesToEvict\n        }\n        return entriesToEvict\n      }\n\n      private fun insertIntoDynamicTable(entry: Header) {\n        val delta = entry.hpackSize\n\n        // if the new or replacement header is too big, drop all entries.\n        if (delta > maxDynamicTableByteCount) {\n          clearDynamicTable()\n          return\n        }\n\n        // Evict headers to the required length.\n        val bytesToRecover = dynamicTableByteCount + delta - maxDynamicTableByteCount\n        evictToRecoverBytes(bytesToRecover)\n\n        if (headerCount + 1 > dynamicTable.size) { // Need to grow the dynamic table.\n          val doubled = arrayOfNulls<Header>(dynamicTable.size * 2)\n          System.arraycopy(dynamicTable, 0, doubled, dynamicTable.size, dynamicTable.size)\n          nextHeaderIndex = dynamicTable.size - 1\n          dynamicTable = doubled\n        }\n        val index = nextHeaderIndex--\n        dynamicTable[index] = entry\n        headerCount++\n        dynamicTableByteCount += delta\n      }\n\n      /**\n       * This does not use \"never indexed\" semantics for sensitive headers.\n       *\n       * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-6.2.3\n       */\n      @Throws(IOException::class)\n      fun writeHeaders(headerBlock: List<Header>) {\n        if (emitDynamicTableSizeUpdate) {\n          if (smallestHeaderTableSizeSetting < maxDynamicTableByteCount) {\n            // Multiple dynamic table size updates!\n            writeInt(smallestHeaderTableSizeSetting, PREFIX_5_BITS, 0x20)\n          }\n          emitDynamicTableSizeUpdate = false\n          smallestHeaderTableSizeSetting = Integer.MAX_VALUE\n          writeInt(maxDynamicTableByteCount, PREFIX_5_BITS, 0x20)\n        }\n\n        for (i in 0 until headerBlock.size) {\n          val header = headerBlock[i]\n          val name = header.name.toAsciiLowercase()\n          val value = header.value\n          var headerIndex = -1\n          var headerNameIndex = -1\n\n          val staticIndex = NAME_TO_FIRST_INDEX[name]\n          if (staticIndex != null) {\n            headerNameIndex = staticIndex + 1\n            if (headerNameIndex in 2..7) {\n              // Only search a subset of the static header table. Most entries have an empty value, so\n              // it's unnecessary to waste cycles looking at them. This check is built on the\n              // observation that the header entries we care about are in adjacent pairs, and we\n              // always know the first index of the pair.\n              if (STATIC_HEADER_TABLE[headerNameIndex - 1].value == value) {\n                headerIndex = headerNameIndex\n              } else if (STATIC_HEADER_TABLE[headerNameIndex].value == value) {\n                headerIndex = headerNameIndex + 1\n              }\n            }\n          }\n\n          if (headerIndex == -1) {\n            for (j in nextHeaderIndex + 1 until dynamicTable.size) {\n              if (dynamicTable[j]!!.name == name) {\n                if (dynamicTable[j]!!.value == value) {\n                  headerIndex = j - nextHeaderIndex + STATIC_HEADER_TABLE.size\n                  break\n                } else if (headerNameIndex == -1) {\n                  headerNameIndex = j - nextHeaderIndex + STATIC_HEADER_TABLE.size\n                }\n              }\n            }\n          }\n\n          when {\n            headerIndex != -1 -> {\n              // Indexed Header Field.\n              writeInt(headerIndex, PREFIX_7_BITS, 0x80)\n            }\n\n            headerNameIndex == -1 -> {\n              // Literal Header Field with Incremental Indexing - New Name.\n              out.writeByte(0x40)\n              writeByteString(name)\n              writeByteString(value)\n              insertIntoDynamicTable(header)\n            }\n\n            name.startsWith(Header.PSEUDO_PREFIX) && TARGET_AUTHORITY != name -> {\n              // Follow Chromes lead - only include the :authority pseudo header, but exclude all other\n              // pseudo headers. Literal Header Field without Indexing - Indexed Name.\n              writeInt(headerNameIndex, PREFIX_4_BITS, 0)\n              writeByteString(value)\n            }\n\n            else -> {\n              // Literal Header Field with Incremental Indexing - Indexed Name.\n              writeInt(headerNameIndex, PREFIX_6_BITS, 0x40)\n              writeByteString(value)\n              insertIntoDynamicTable(header)\n            }\n          }\n        }\n      }\n\n      // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-4.1.1\n      fun writeInt(\n        value: Int,\n        prefixMask: Int,\n        bits: Int,\n      ) {\n        var value = value\n        // Write the raw value for a single byte value.\n        if (value < prefixMask) {\n          out.writeByte(bits or value)\n          return\n        }\n\n        // Write the mask to start a multibyte value.\n        out.writeByte(bits or prefixMask)\n        value -= prefixMask\n\n        // Write 7 bits at a time 'til we're done.\n        while (value >= 0x80) {\n          val b = value and 0x7f\n          out.writeByte(b or 0x80)\n          value = value ushr 7\n        }\n        out.writeByte(value)\n      }\n\n      @Throws(IOException::class)\n      fun writeByteString(data: ByteString) {\n        if (useCompression && Huffman.encodedLength(data) < data.size) {\n          val huffmanBuffer = Buffer()\n          Huffman.encode(data, huffmanBuffer)\n          val huffmanBytes = huffmanBuffer.readByteString()\n          writeInt(huffmanBytes.size, PREFIX_7_BITS, 0x80)\n          out.write(huffmanBytes)\n        } else {\n          writeInt(data.size, PREFIX_7_BITS, 0)\n          out.write(data)\n        }\n      }\n\n      fun resizeHeaderTable(headerTableSizeSetting: Int) {\n        this.headerTableSizeSetting = headerTableSizeSetting\n        val effectiveHeaderTableSize = minOf(headerTableSizeSetting, SETTINGS_HEADER_TABLE_SIZE_LIMIT)\n\n        if (maxDynamicTableByteCount == effectiveHeaderTableSize) return // No change.\n\n        if (effectiveHeaderTableSize < maxDynamicTableByteCount) {\n          smallestHeaderTableSizeSetting =\n            minOf(smallestHeaderTableSizeSetting, effectiveHeaderTableSize)\n        }\n        emitDynamicTableSizeUpdate = true\n        maxDynamicTableByteCount = effectiveHeaderTableSize\n        adjustDynamicTableByteCount()\n      }\n\n      private fun adjustDynamicTableByteCount() {\n        if (maxDynamicTableByteCount < dynamicTableByteCount) {\n          if (maxDynamicTableByteCount == 0) {\n            clearDynamicTable()\n          } else {\n            evictToRecoverBytes(dynamicTableByteCount - maxDynamicTableByteCount)\n          }\n        }\n      }\n    }\n\n  /**\n   * An HTTP/2 response cannot contain uppercase header characters and must be treated as\n   * malformed.\n   */\n  @Throws(IOException::class)\n  fun checkLowercase(name: ByteString): ByteString {\n    for (i in 0 until name.size) {\n      if (name[i] in 'A'.code.toByte()..'Z'.code.toByte()) {\n        throw IOException(\"PROTOCOL_ERROR response malformed: mixed case name: ${name.utf8()}\")\n      }\n    }\n    return name\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport okhttp3.internal.format\nimport okio.ByteString.Companion.encodeUtf8\n\nobject Http2 {\n  @JvmField\n  val CONNECTION_PREFACE = \"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n\".encodeUtf8()\n\n  /** The initial max frame size, applied independently writing to, or reading from the peer. */\n  const val INITIAL_MAX_FRAME_SIZE = 0x4000 // 16384\n\n  const val TYPE_DATA = 0x0\n  const val TYPE_HEADERS = 0x1\n  const val TYPE_PRIORITY = 0x2\n  const val TYPE_RST_STREAM = 0x3\n  const val TYPE_SETTINGS = 0x4\n  const val TYPE_PUSH_PROMISE = 0x5\n  const val TYPE_PING = 0x6\n  const val TYPE_GOAWAY = 0x7\n  const val TYPE_WINDOW_UPDATE = 0x8\n  const val TYPE_CONTINUATION = 0x9\n\n  const val FLAG_NONE = 0x0\n  const val FLAG_ACK = 0x1 // Used for settings and ping.\n  const val FLAG_END_STREAM = 0x1 // Used for headers and data.\n  const val FLAG_END_HEADERS = 0x4 // Used for headers and continuation.\n  const val FLAG_END_PUSH_PROMISE = 0x4\n  const val FLAG_PADDED = 0x8 // Used for headers and data.\n  const val FLAG_PRIORITY = 0x20 // Used for headers.\n  const val FLAG_COMPRESSED = 0x20 // Used for data.\n\n  /** Lookup table for valid frame types. */\n  private val FRAME_NAMES =\n    arrayOf(\n      \"DATA\",\n      \"HEADERS\",\n      \"PRIORITY\",\n      \"RST_STREAM\",\n      \"SETTINGS\",\n      \"PUSH_PROMISE\",\n      \"PING\",\n      \"GOAWAY\",\n      \"WINDOW_UPDATE\",\n      \"CONTINUATION\",\n    )\n\n  /**\n   * Lookup table for valid flags for DATA, HEADERS, CONTINUATION. Invalid combinations are\n   * represented in binary.\n   */\n  private val FLAGS = arrayOfNulls<String>(0x40) // Highest bit flag is 0x20.\n  private val BINARY =\n    Array(256) {\n      format(\"%8s\", Integer.toBinaryString(it)).replace(' ', '0')\n    }\n\n  init {\n    FLAGS[FLAG_NONE] = \"\"\n    FLAGS[FLAG_END_STREAM] = \"END_STREAM\"\n\n    val prefixFlags = intArrayOf(FLAG_END_STREAM)\n\n    FLAGS[FLAG_PADDED] = \"PADDED\"\n    for (prefixFlag in prefixFlags) {\n      FLAGS[prefixFlag or FLAG_PADDED] = FLAGS[prefixFlag] + \"|PADDED\"\n    }\n\n    FLAGS[FLAG_END_HEADERS] = \"END_HEADERS\" // Same as END_PUSH_PROMISE.\n    FLAGS[FLAG_PRIORITY] = \"PRIORITY\" // Same as FLAG_COMPRESSED.\n    FLAGS[FLAG_END_HEADERS or FLAG_PRIORITY] = \"END_HEADERS|PRIORITY\" // Only valid on HEADERS.\n    val frameFlags = intArrayOf(FLAG_END_HEADERS, FLAG_PRIORITY, FLAG_END_HEADERS or FLAG_PRIORITY)\n\n    for (frameFlag in frameFlags) {\n      for (prefixFlag in prefixFlags) {\n        FLAGS[prefixFlag or frameFlag] = FLAGS[prefixFlag] + '|'.toString() + FLAGS[frameFlag]\n        FLAGS[prefixFlag or frameFlag or FLAG_PADDED] =\n          FLAGS[prefixFlag] + '|'.toString() + FLAGS[frameFlag] + \"|PADDED\"\n      }\n    }\n\n    for (i in FLAGS.indices) { // Fill in holes with binary representation.\n      if (FLAGS[i] == null) FLAGS[i] = BINARY[i]\n    }\n  }\n\n  /**\n   * Returns a human-readable representation of HTTP/2 frame headers.\n   *\n   * The format is:\n   *\n   * ```\n   * direction streamID length type flags\n   * ```\n   *\n   * Where direction is `<<` for inbound and `>>` for outbound.\n   *\n   * For example, the following would indicate a HEAD request sent from the client.\n   * ```\n   * `<< 0x0000000f    12 HEADERS       END_HEADERS|END_STREAM\n   * ```\n   */\n  fun frameLog(\n    inbound: Boolean,\n    streamId: Int,\n    length: Int,\n    type: Int,\n    flags: Int,\n  ): String {\n    val formattedType = formattedType(type)\n    val formattedFlags = formatFlags(type, flags)\n    val direction = if (inbound) \"<<\" else \">>\"\n    return format(\n      \"%s 0x%08x %5d %-13s %s\",\n      direction,\n      streamId,\n      length,\n      formattedType,\n      formattedFlags,\n    )\n  }\n\n  /**\n   * Returns a human-readable representation of a `WINDOW_UPDATE` frame. This frame includes the\n   * window size increment instead of flags.\n   */\n  fun frameLogWindowUpdate(\n    inbound: Boolean,\n    streamId: Int,\n    length: Int,\n    windowSizeIncrement: Long,\n  ): String {\n    val formattedType = formattedType(TYPE_WINDOW_UPDATE)\n    val direction = if (inbound) \"<<\" else \">>\"\n    return format(\n      \"%s 0x%08x %5d %-13s %d\",\n      direction,\n      streamId,\n      length,\n      formattedType,\n      windowSizeIncrement,\n    )\n  }\n\n  internal fun formattedType(type: Int): String = if (type < FRAME_NAMES.size) FRAME_NAMES[type] else format(\"0x%02x\", type)\n\n  /**\n   * Looks up valid string representing flags from the table. Invalid combinations are represented\n   * in binary.\n   */\n  fun formatFlags(\n    type: Int,\n    flags: Int,\n  ): String {\n    if (flags == 0) return \"\"\n    when (type) {\n      // Special case types that have 0 or 1 flag.\n      TYPE_SETTINGS, TYPE_PING -> return if (flags == FLAG_ACK) \"ACK\" else BINARY[flags]\n\n      TYPE_PRIORITY, TYPE_RST_STREAM, TYPE_GOAWAY, TYPE_WINDOW_UPDATE -> return BINARY[flags]\n    }\n    val result = if (flags < FLAGS.size) FLAGS[flags]!! else BINARY[flags]\n    // Special case types that have overlap flag values.\n    return when {\n      type == TYPE_PUSH_PROMISE && flags and FLAG_END_PUSH_PROMISE != 0 -> {\n        result.replace(\"HEADERS\", \"PUSH_PROMISE\") // TODO: Avoid allocation.\n      }\n\n      type == TYPE_DATA && flags and FLAG_COMPRESSED != 0 -> {\n        result.replace(\"PRIORITY\", \"COMPRESSED\") // TODO: Avoid allocation.\n      }\n\n      else -> {\n        result\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2Connection.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.util.concurrent.TimeUnit\nimport okhttp3.Headers\nimport okhttp3.internal.EMPTY_BYTE_ARRAY\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.Lockable\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.assertLockNotHeld\nimport okhttp3.internal.concurrent.notifyAll\nimport okhttp3.internal.concurrent.wait\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.connection.BufferedSocket\nimport okhttp3.internal.http2.ErrorCode.REFUSED_STREAM\nimport okhttp3.internal.http2.Settings.Companion.DEFAULT_INITIAL_WINDOW_SIZE\nimport okhttp3.internal.http2.flowcontrol.WindowCounter\nimport okhttp3.internal.ignoreIoExceptions\nimport okhttp3.internal.okHttpName\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.Platform.Companion.INFO\nimport okhttp3.internal.toHeaders\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString\n\n/**\n * A socket connection to a remote peer. A connection hosts streams which can send and receive\n * data.\n *\n * Many methods in this API are **synchronous:** the call is completed before the method returns.\n * This is typical for Java but atypical for HTTP/2. This is motivated by exception transparency:\n * an [IOException] that was triggered by a certain caller can be caught and handled by that caller.\n */\n@Suppress(\"NAME_SHADOWING\")\nclass Http2Connection internal constructor(\n  builder: Builder,\n) : Closeable,\n  Lockable {\n  // Internal state of this connection is guarded by 'lock'. No blocking operations may be\n  // performed while holding this lock!\n  //\n  // Socket writes are guarded by frameWriter.\n  //\n  // Socket reads are unguarded but are only made by the reader thread.\n  //\n  // Certain operations (like SYN_STREAM) need to synchronize on both the frameWriter (to do\n  // blocking I/O) and this (to create streams). Such operations must synchronize on 'this' last.\n  // This ensures that we never wait for a blocking operation while holding 'this'.\n\n  /** True if this peer initiated the connection. */\n  internal val client: Boolean = builder.client\n\n  /** User code to run in response to incoming streams or settings. */\n  internal val listener: Listener = builder.listener\n  internal val streams = mutableMapOf<Int, Http2Stream>()\n  internal val connectionName: String = builder.connectionName\n  internal var lastGoodStreamId = 0\n\n  /** http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-5.1.1 */\n  internal var nextStreamId = if (builder.client) 3 else 2\n\n  private var isShutdown = false\n\n  /** For scheduling everything asynchronous. */\n  private val taskRunner = builder.taskRunner\n\n  /** Asynchronously writes frames to the outgoing socket. */\n  private val writerQueue = taskRunner.newQueue()\n\n  /** Ensures push promise callbacks events are sent in order per stream. */\n  private val pushQueue = taskRunner.newQueue()\n\n  /** Notifies the listener of settings changes. */\n  private val settingsListenerQueue = taskRunner.newQueue()\n\n  /** User code to run in response to push promise events. */\n  private val pushObserver: PushObserver = builder.pushObserver\n\n  // Total number of pings send and received of the corresponding types. All guarded by this.\n  private var intervalPingsSent = 0L\n  private var intervalPongsReceived = 0L\n  private var degradedPingsSent = 0L\n  private var degradedPongsReceived = 0L\n  private var awaitPingsSent = 0L\n  private var awaitPongsReceived = 0L\n\n  /** Consider this connection to be unhealthy if a degraded pong isn't received by this time. */\n  private var degradedPongDeadlineNs = 0L\n\n  internal val flowControlListener: FlowControlListener = builder.flowControlListener\n\n  /** Settings we communicate to the peer. */\n  val okHttpSettings =\n    Settings().apply {\n      // Flow control was designed more for servers, or proxies than edge clients. If we are a client,\n      // set the flow control window to 16MiB.  This avoids thrashing window updates every 64KiB, yet\n      // small enough to avoid blowing up the heap.\n      if (builder.client) {\n        set(Settings.INITIAL_WINDOW_SIZE, OKHTTP_CLIENT_WINDOW_SIZE)\n      }\n    }\n\n  /**\n   * Settings we receive from the peer. Changes to the field are guarded by this. The instance is\n   * never mutated once it has been assigned.\n   */\n  var peerSettings = DEFAULT_SETTINGS\n\n  /** The bytes consumed and acknowledged by the application. */\n  val readBytes: WindowCounter = WindowCounter(streamId = 0)\n\n  /** The total number of bytes produced by the application. */\n  var writeBytesTotal = 0L\n    private set\n\n  /** The total number of bytes permitted to be produced according to `WINDOW_UPDATE` frames. */\n  var writeBytesMaximum: Long = peerSettings.initialWindowSize.toLong()\n    private set\n\n  internal val socket: BufferedSocket = builder.socket\n  val writer = Http2Writer(socket.sink, client)\n\n  // Visible for testing\n  val readerRunnable = ReaderRunnable(Http2Reader(socket.source, client))\n\n  // Guarded by this.\n  private val currentPushRequests = mutableSetOf<Int>()\n\n  init {\n    if (builder.pingIntervalMillis != 0) {\n      val pingIntervalNanos = TimeUnit.MILLISECONDS.toNanos(builder.pingIntervalMillis.toLong())\n      writerQueue.schedule(\"$connectionName ping\", pingIntervalNanos) {\n        val failDueToMissingPong =\n          withLock {\n            if (intervalPongsReceived < intervalPingsSent) {\n              return@withLock true\n            } else {\n              intervalPingsSent++\n              return@withLock false\n            }\n          }\n        if (failDueToMissingPong) {\n          failConnection(null)\n          return@schedule -1L\n        } else {\n          writePing(false, INTERVAL_PING, 0)\n          return@schedule pingIntervalNanos\n        }\n      }\n    }\n  }\n\n  /**\n   * Returns the number of [open streams][Http2Stream.isOpen] on this connection.\n   */\n  fun openStreamCount(): Int = withLock { streams.size }\n\n  fun getStream(id: Int): Http2Stream? = withLock { streams[id] }\n\n  internal fun removeStream(streamId: Int): Http2Stream? {\n    withLock {\n      val stream = streams.remove(streamId)\n\n      // The removed stream may be blocked on a connection-wide window update.\n      notifyAll()\n\n      return stream\n    }\n  }\n\n  internal fun updateConnectionFlowControl(read: Long) {\n    withLock {\n      readBytes.update(total = read)\n      val readBytesToAcknowledge = readBytes.unacknowledged\n      if (readBytesToAcknowledge >= okHttpSettings.initialWindowSize / 2) {\n        writeWindowUpdateLater(0, readBytesToAcknowledge)\n        readBytes.update(acknowledged = readBytesToAcknowledge)\n      }\n      flowControlListener.receivingConnectionWindowChanged(readBytes)\n    }\n  }\n\n  /**\n   * Returns a new server-initiated stream.\n   *\n   * @param associatedStreamId the stream that triggered the sender to create this stream.\n   * @param out true to create an output stream that we can use to send data to the remote peer.\n   *     Corresponds to `FLAG_FIN`.\n   */\n  @Throws(IOException::class)\n  fun pushStream(\n    associatedStreamId: Int,\n    requestHeaders: List<Header>,\n    out: Boolean,\n  ): Http2Stream {\n    check(!client) { \"Client cannot push requests.\" }\n    return newStream(associatedStreamId, requestHeaders, out)\n  }\n\n  /**\n   * Returns a new locally-initiated stream.\n   *\n   * @param out true to create an output stream that we can use to send data to the remote peer.\n   *     Corresponds to `FLAG_FIN`.\n   */\n  @Throws(IOException::class)\n  fun newStream(\n    requestHeaders: List<Header>,\n    out: Boolean,\n  ): Http2Stream = newStream(0, requestHeaders, out)\n\n  @Throws(IOException::class)\n  private fun newStream(\n    associatedStreamId: Int,\n    requestHeaders: List<Header>,\n    out: Boolean,\n  ): Http2Stream {\n    val outFinished = !out\n    val inFinished = false\n    val flushHeaders: Boolean\n    val stream: Http2Stream\n    val streamId: Int\n\n    writer.withLock {\n      withLock {\n        if (nextStreamId > Int.MAX_VALUE / 2) {\n          shutdown(REFUSED_STREAM)\n        }\n        if (isShutdown) {\n          throw ConnectionShutdownException()\n        }\n        streamId = nextStreamId\n        nextStreamId += 2\n        stream = Http2Stream(streamId, this, outFinished, inFinished, null)\n        flushHeaders = !out ||\n          writeBytesTotal >= writeBytesMaximum ||\n          stream.writeBytesTotal >= stream.writeBytesMaximum\n        if (stream.isOpen) {\n          streams[streamId] = stream\n        }\n      }\n      if (associatedStreamId == 0) {\n        writer.headers(outFinished, streamId, requestHeaders)\n      } else {\n        require(!client) { \"client streams shouldn't have associated stream IDs\" }\n        // HTTP/2 has a PUSH_PROMISE frame.\n        writer.pushPromise(associatedStreamId, streamId, requestHeaders)\n      }\n    }\n\n    if (flushHeaders) {\n      writer.flush()\n    }\n\n    return stream\n  }\n\n  @Throws(IOException::class)\n  internal fun writeHeaders(\n    streamId: Int,\n    outFinished: Boolean,\n    alternating: List<Header>,\n  ) {\n    writer.headers(outFinished, streamId, alternating)\n  }\n\n  /**\n   * Callers of this method are not thread safe, and sometimes on application threads. Most often,\n   * this method will be called to send a buffer worth of data to the peer.\n   *\n   * Writes are subject to the write window of the stream and the connection. Until there is a\n   * window sufficient to send [byteCount], the caller will block. For example, a user of\n   * `HttpURLConnection` who flushes more bytes to the output stream than the connection's write\n   * window will block.\n   *\n   * Zero [byteCount] writes are not subject to flow control and will not block. The only use case\n   * for zero [byteCount] is closing a flushed output stream.\n   */\n  @Throws(IOException::class)\n  fun writeData(\n    streamId: Int,\n    outFinished: Boolean,\n    buffer: Buffer?,\n    byteCount: Long,\n  ) {\n    // Empty data frames are not flow-controlled.\n    if (byteCount == 0L) {\n      writer.data(outFinished, streamId, buffer, 0)\n      return\n    }\n\n    var byteCount = byteCount\n    while (byteCount > 0L) {\n      var toWrite: Int\n      withLock {\n        try {\n          while (writeBytesTotal >= writeBytesMaximum) {\n            // Before blocking, confirm that the stream we're writing is still open. It's possible\n            // that the stream has since been closed (such as if this write timed out.)\n            if (!streams.containsKey(streamId)) {\n              throw IOException(\"stream closed\")\n            }\n            wait() // Wait until we receive a WINDOW_UPDATE.\n          }\n        } catch (e: InterruptedException) {\n          Thread.currentThread().interrupt() // Retain interrupted status.\n          throw InterruptedIOException()\n        }\n\n        toWrite = minOf(byteCount, writeBytesMaximum - writeBytesTotal).toInt()\n        toWrite = minOf(toWrite, writer.maxDataLength())\n        writeBytesTotal += toWrite.toLong()\n      }\n\n      byteCount -= toWrite.toLong()\n      writer.data(outFinished && byteCount == 0L, streamId, buffer, toWrite)\n    }\n  }\n\n  internal fun writeSynResetLater(\n    streamId: Int,\n    errorCode: ErrorCode,\n  ) {\n    writerQueue.execute(\"$connectionName[$streamId] writeSynReset\") {\n      try {\n        writeSynReset(streamId, errorCode)\n      } catch (e: IOException) {\n        failConnection(e)\n      }\n    }\n  }\n\n  @Throws(IOException::class)\n  internal fun writeSynReset(\n    streamId: Int,\n    statusCode: ErrorCode,\n  ) {\n    writer.rstStream(streamId, statusCode)\n  }\n\n  internal fun writeWindowUpdateLater(\n    streamId: Int,\n    unacknowledgedBytesRead: Long,\n  ) {\n    writerQueue.execute(\"$connectionName[$streamId] windowUpdate\") {\n      try {\n        writer.windowUpdate(streamId, unacknowledgedBytesRead)\n      } catch (e: IOException) {\n        failConnection(e)\n      }\n    }\n  }\n\n  fun writePing(\n    reply: Boolean,\n    payload1: Int,\n    payload2: Int,\n  ) {\n    try {\n      writer.ping(reply, payload1, payload2)\n    } catch (e: IOException) {\n      failConnection(e)\n    }\n  }\n\n  /** For testing: sends a ping and waits for a pong. */\n  @Throws(InterruptedException::class)\n  fun writePingAndAwaitPong() {\n    writePing()\n    awaitPong()\n  }\n\n  /** For testing: sends a ping to be awaited with [awaitPong]. */\n  @Throws(InterruptedException::class)\n  fun writePing() {\n    withLock {\n      awaitPingsSent++\n    }\n\n    // 0x4f 0x4b 0x6f 0x6b is \"OKok\".\n    writePing(false, AWAIT_PING, 0x4f4b6f6b)\n  }\n\n  /** For testing: awaits a pong. */\n  @Throws(InterruptedException::class)\n  fun awaitPong() {\n    withLock {\n      while (awaitPongsReceived < awaitPingsSent) {\n        wait()\n      }\n    }\n  }\n\n  @Throws(IOException::class)\n  fun flush() {\n    writer.flush()\n  }\n\n  /**\n   * Degrades this connection such that new streams can neither be created locally, nor accepted\n   * from the remote peer. Existing streams are not impacted. This is intended to permit an endpoint\n   * to gracefully stop accepting new requests without harming previously established streams.\n   */\n  @Throws(IOException::class)\n  fun shutdown(statusCode: ErrorCode) {\n    writer.withLock {\n      val lastGoodStreamId: Int\n      withLock {\n        if (isShutdown) {\n          return\n        }\n        isShutdown = true\n        lastGoodStreamId = this.lastGoodStreamId\n      }\n      // TODO: propagate exception message into debugData.\n      // TODO: configure a timeout on the reader so that it doesn’t block forever.\n      writer.goAway(lastGoodStreamId, statusCode, EMPTY_BYTE_ARRAY)\n    }\n  }\n\n  /**\n   * Closes this connection. This cancels all open streams and unanswered pings. It closes the\n   * underlying input and output streams and shuts down internal task queues.\n   */\n  override fun close() {\n    close(ErrorCode.NO_ERROR, ErrorCode.CANCEL, null)\n  }\n\n  internal fun close(\n    connectionCode: ErrorCode,\n    streamCode: ErrorCode,\n    cause: IOException?,\n  ) {\n    assertLockNotHeld()\n\n    ignoreIoExceptions {\n      shutdown(connectionCode)\n    }\n\n    var streamsToClose: Array<Http2Stream>? = null\n    withLock {\n      if (streams.isNotEmpty()) {\n        streamsToClose = streams.values.toTypedArray()\n        streams.clear()\n      }\n    }\n\n    streamsToClose?.forEach { stream ->\n      ignoreIoExceptions {\n        stream.close(streamCode, cause)\n      }\n    }\n\n    // Close the writer to release its resources (such as deflaters).\n    ignoreIoExceptions {\n      writer.close()\n    }\n\n    // Cancel the socket to break out the reader thread, which will clean up after itself.\n    ignoreIoExceptions {\n      socket.cancel()\n    }\n\n    // Release the threads.\n    writerQueue.shutdown()\n    pushQueue.shutdown()\n    settingsListenerQueue.shutdown()\n  }\n\n  private fun failConnection(e: IOException?) {\n    close(ErrorCode.PROTOCOL_ERROR, ErrorCode.PROTOCOL_ERROR, e)\n  }\n\n  /**\n   * Sends any initial frames and starts reading frames from the remote peer. This should be called\n   * after [Builder.build] for all new connections.\n   *\n   * @param sendConnectionPreface true to send connection preface frames. This should always be true\n   *     except for in tests that don't check for a connection preface.\n   * @param taskRunner the TaskRunner to use, daemon by default.\n   */\n  @Throws(IOException::class)\n  @JvmOverloads\n  fun start(sendConnectionPreface: Boolean = true) {\n    if (sendConnectionPreface) {\n      writer.connectionPreface()\n      writer.settings(okHttpSettings)\n      val windowSize = okHttpSettings.initialWindowSize\n      if (windowSize != DEFAULT_INITIAL_WINDOW_SIZE) {\n        writer.windowUpdate(0, (windowSize - DEFAULT_INITIAL_WINDOW_SIZE).toLong())\n      }\n    }\n    // Thread doesn't use client Dispatcher, since it is scoped potentially across clients via\n    // ConnectionPool.\n    taskRunner.newQueue().execute(name = connectionName, block = readerRunnable)\n  }\n\n  /** Merges [settings] into this peer's settings and sends them to the remote peer. */\n  @Throws(IOException::class)\n  fun setSettings(settings: Settings) {\n    writer.withLock {\n      withLock {\n        if (isShutdown) {\n          throw ConnectionShutdownException()\n        }\n        okHttpSettings.merge(settings)\n      }\n      writer.settings(settings)\n    }\n  }\n\n  fun isHealthy(nowNs: Long): Boolean {\n    withLock {\n      if (isShutdown) return false\n\n      // A degraded pong is overdue.\n      if (degradedPongsReceived < degradedPingsSent && nowNs >= degradedPongDeadlineNs) return false\n\n      return true\n    }\n  }\n\n  /**\n   * HTTP/2 can have both stream timeouts (due to a problem with a single stream) and connection\n   * timeouts (due to a problem with the transport). When a stream times out we don't know whether\n   * the problem impacts just one stream or the entire connection.\n   *\n   * To differentiate the two cases we ping the server when a stream times out. If the overall\n   * connection is fine the ping will receive a pong; otherwise it won't.\n   *\n   * The deadline to respond to this ping attempts to limit the cost of being wrong. If it is too\n   * long, streams created while we await the pong will reuse broken connections and inevitably\n   * fail. If it is too short, slow connections will be marked as failed and extra TCP and TLS\n   * handshakes will be required.\n   *\n   * The deadline is currently hardcoded. We may make this configurable in the future!\n   */\n  internal fun sendDegradedPingLater() {\n    withLock {\n      if (degradedPongsReceived < degradedPingsSent) return // Already awaiting a degraded pong.\n      degradedPingsSent++\n      degradedPongDeadlineNs = System.nanoTime() + DEGRADED_PONG_TIMEOUT_NS\n    }\n    writerQueue.execute(\"$connectionName ping\") {\n      writePing(false, DEGRADED_PING, 0)\n    }\n  }\n\n  class Builder(\n    /** True if this peer initiated the connection; false if this peer accepted the connection. */\n    internal var client: Boolean,\n    internal val taskRunner: TaskRunner,\n  ) {\n    internal lateinit var socket: BufferedSocket\n    internal lateinit var connectionName: String\n    internal var listener = Listener.REFUSE_INCOMING_STREAMS\n    internal var pushObserver = PushObserver.CANCEL\n    internal var pingIntervalMillis: Int = 0\n    internal var flowControlListener: FlowControlListener = FlowControlListener.None\n\n    @Throws(IOException::class)\n    fun socket(\n      socket: BufferedSocket,\n      peerName: String,\n    ) = apply {\n      this.socket = socket\n      this.connectionName =\n        when {\n          client -> \"$okHttpName $peerName\"\n          else -> \"MockWebServer $peerName\"\n        }\n    }\n\n    fun listener(listener: Listener) =\n      apply {\n        this.listener = listener\n      }\n\n    fun pushObserver(pushObserver: PushObserver) =\n      apply {\n        this.pushObserver = pushObserver\n      }\n\n    fun pingIntervalMillis(pingIntervalMillis: Int) =\n      apply {\n        this.pingIntervalMillis = pingIntervalMillis\n      }\n\n    fun flowControlListener(flowControlListener: FlowControlListener) =\n      apply {\n        this.flowControlListener = flowControlListener\n      }\n\n    fun build(): Http2Connection = Http2Connection(this)\n  }\n\n  /**\n   * Methods in this class must not lock FrameWriter. If a method needs to write a frame, create an\n   * async task to do so.\n   */\n  inner class ReaderRunnable internal constructor(\n    internal val reader: Http2Reader,\n  ) : Http2Reader.Handler,\n    () -> Unit {\n    override fun invoke() {\n      var connectionErrorCode = ErrorCode.INTERNAL_ERROR\n      var streamErrorCode = ErrorCode.INTERNAL_ERROR\n      var errorException: IOException? = null\n      try {\n        reader.readConnectionPreface(this)\n        while (reader.nextFrame(false, this)) {\n        }\n        connectionErrorCode = ErrorCode.NO_ERROR\n        streamErrorCode = ErrorCode.CANCEL\n      } catch (e: IOException) {\n        errorException = e\n        connectionErrorCode = ErrorCode.PROTOCOL_ERROR\n        streamErrorCode = ErrorCode.PROTOCOL_ERROR\n      } finally {\n        close(connectionErrorCode, streamErrorCode, errorException)\n        reader.closeQuietly()\n      }\n    }\n\n    @Throws(IOException::class)\n    override fun data(\n      inFinished: Boolean,\n      streamId: Int,\n      source: BufferedSource,\n      length: Int,\n    ) {\n      if (pushedStream(streamId)) {\n        pushDataLater(streamId, source, length, inFinished)\n        return\n      }\n      val dataStream = getStream(streamId)\n      if (dataStream == null) {\n        writeSynResetLater(streamId, ErrorCode.PROTOCOL_ERROR)\n        updateConnectionFlowControl(length.toLong())\n        source.skip(length.toLong())\n        return\n      }\n      dataStream.receiveData(source, length)\n      if (inFinished) {\n        dataStream.receiveHeaders(Headers.EMPTY, true)\n      }\n    }\n\n    override fun headers(\n      inFinished: Boolean,\n      streamId: Int,\n      associatedStreamId: Int,\n      headerBlock: List<Header>,\n    ) {\n      if (pushedStream(streamId)) {\n        pushHeadersLater(streamId, headerBlock, inFinished)\n        return\n      }\n      val stream: Http2Stream?\n      withLock {\n        stream = getStream(streamId)\n\n        if (stream == null) {\n          // If we're shutdown, don't bother with this stream.\n          if (isShutdown) return\n\n          // If the stream ID is less than the last created ID, assume it's already closed.\n          if (streamId <= lastGoodStreamId) return\n\n          // If the stream ID is in the client's namespace, assume it's already closed.\n          if (streamId % 2 == nextStreamId % 2) return\n\n          // Create a stream.\n          val headers = headerBlock.toHeaders()\n          val newStream = Http2Stream(streamId, this@Http2Connection, false, inFinished, headers)\n          lastGoodStreamId = streamId\n          streams[streamId] = newStream\n\n          // Use a different task queue for each stream because they should be handled in parallel.\n          taskRunner.newQueue().execute(\"$connectionName[$streamId] onStream\") {\n            try {\n              listener.onStream(newStream)\n            } catch (e: IOException) {\n              Platform.get().log(\"Http2Connection.Listener failure for $connectionName\", INFO, e)\n              ignoreIoExceptions {\n                newStream.close(ErrorCode.PROTOCOL_ERROR, e)\n              }\n            }\n          }\n          return\n        }\n      }\n\n      // Update an existing stream.\n      stream!!.receiveHeaders(headerBlock.toHeaders(), inFinished)\n    }\n\n    override fun rstStream(\n      streamId: Int,\n      errorCode: ErrorCode,\n    ) {\n      if (pushedStream(streamId)) {\n        pushResetLater(streamId, errorCode)\n        return\n      }\n      val rstStream = removeStream(streamId)\n      rstStream?.receiveRstStream(errorCode)\n    }\n\n    override fun settings(\n      clearPrevious: Boolean,\n      settings: Settings,\n    ) {\n      writerQueue.execute(\"$connectionName applyAndAckSettings\") {\n        applyAndAckSettings(clearPrevious, settings)\n      }\n    }\n\n    /**\n     * Apply inbound settings and send an acknowledgement to the peer that provided them.\n     *\n     * We need to apply the settings and ack them atomically. This is because some HTTP/2\n     * implementations (nghttp2) forbid peers from taking advantage of settings before they have\n     * acknowledged! In particular, we shouldn't send frames that assume a new `initialWindowSize`\n     * until we send the frame that acknowledges this new size.\n     *\n     * Since we can't ACK settings on the current reader thread (the reader thread can't write) we\n     * execute all peer settings logic on the writer thread. This relies on the fact that the\n     * writer task queue won't reorder tasks; otherwise settings could be applied in the opposite\n     * order than received.\n     */\n    fun applyAndAckSettings(\n      clearPrevious: Boolean,\n      settings: Settings,\n    ) {\n      var delta: Long\n      var streamsToNotify: Array<Http2Stream>?\n      var newPeerSettings: Settings\n      writer.withLock {\n        withLock {\n          val previousPeerSettings = peerSettings\n          newPeerSettings =\n            if (clearPrevious) {\n              settings\n            } else {\n              Settings().apply {\n                merge(previousPeerSettings)\n                merge(settings)\n              }\n            }\n\n          val peerInitialWindowSize = newPeerSettings.initialWindowSize.toLong()\n          delta = peerInitialWindowSize - previousPeerSettings.initialWindowSize.toLong()\n          streamsToNotify =\n            when {\n              delta == 0L || streams.isEmpty() -> null\n\n              // No adjustment is necessary.\n              else -> streams.values.toTypedArray()\n            }\n\n          peerSettings = newPeerSettings\n\n          settingsListenerQueue.execute(\"$connectionName onSettings\") {\n            listener.onSettings(this@Http2Connection, newPeerSettings)\n          }\n        }\n        try {\n          writer.applyAndAckSettings(newPeerSettings)\n        } catch (e: IOException) {\n          failConnection(e)\n        }\n      }\n      if (streamsToNotify != null) {\n        for (stream in streamsToNotify) {\n          stream.withLock {\n            stream.addBytesToWriteWindow(delta)\n          }\n        }\n      }\n    }\n\n    override fun ackSettings() {\n      // TODO: If we don't get this callback after sending settings to the peer, SETTINGS_TIMEOUT.\n    }\n\n    override fun ping(\n      ack: Boolean,\n      payload1: Int,\n      payload2: Int,\n    ) {\n      if (ack) {\n        withLock {\n          when (payload1) {\n            INTERVAL_PING -> {\n              intervalPongsReceived++\n            }\n\n            DEGRADED_PING -> {\n              degradedPongsReceived++\n            }\n\n            AWAIT_PING -> {\n              awaitPongsReceived++\n              notifyAll()\n            }\n\n            else -> {\n              // Ignore an unexpected pong.\n            }\n          }\n        }\n      } else {\n        // Send a reply to a client ping if this is a server and vice versa.\n        writerQueue.execute(\"$connectionName ping\") {\n          writePing(true, payload1, payload2)\n        }\n      }\n    }\n\n    override fun goAway(\n      lastGoodStreamId: Int,\n      errorCode: ErrorCode,\n      debugData: ByteString,\n    ) {\n      if (debugData.size > 0) {\n        // TODO: log the debugData\n      }\n\n      // Copy the streams first. We don't want to hold a lock when we call receiveRstStream().\n      val streamsCopy: Array<Http2Stream>\n      withLock {\n        streamsCopy = streams.values.toTypedArray()\n        isShutdown = true\n      }\n\n      // Fail all streams created after the last good stream ID.\n      for (http2Stream in streamsCopy) {\n        if (http2Stream.id > lastGoodStreamId && http2Stream.isLocallyInitiated) {\n          http2Stream.receiveRstStream(REFUSED_STREAM)\n          removeStream(http2Stream.id)\n        }\n      }\n    }\n\n    override fun windowUpdate(\n      streamId: Int,\n      windowSizeIncrement: Long,\n    ) {\n      if (streamId == 0) {\n        withLock {\n          writeBytesMaximum += windowSizeIncrement\n          notifyAll()\n        }\n      } else {\n        val stream = getStream(streamId)\n        if (stream != null) {\n          stream.withLock {\n            stream.addBytesToWriteWindow(windowSizeIncrement)\n          }\n        }\n      }\n    }\n\n    override fun priority(\n      streamId: Int,\n      streamDependency: Int,\n      weight: Int,\n      exclusive: Boolean,\n    ) {\n      // TODO: honor priority.\n    }\n\n    override fun pushPromise(\n      streamId: Int,\n      promisedStreamId: Int,\n      requestHeaders: List<Header>,\n    ) {\n      pushRequestLater(promisedStreamId, requestHeaders)\n    }\n\n    override fun alternateService(\n      streamId: Int,\n      origin: String,\n      protocol: ByteString,\n      host: String,\n      port: Int,\n      maxAge: Long,\n    ) {\n      // TODO: register alternate service.\n    }\n  }\n\n  /** Even, positive numbered streams are pushed streams in HTTP/2. */\n  internal fun pushedStream(streamId: Int): Boolean = streamId != 0 && streamId and 1 == 0\n\n  internal fun pushRequestLater(\n    streamId: Int,\n    requestHeaders: List<Header>,\n  ) {\n    withLock {\n      if (streamId in currentPushRequests) {\n        writeSynResetLater(streamId, ErrorCode.PROTOCOL_ERROR)\n        return\n      }\n      currentPushRequests.add(streamId)\n    }\n    pushQueue.execute(\"$connectionName[$streamId] onRequest\") {\n      val cancel = pushObserver.onRequest(streamId, requestHeaders)\n      ignoreIoExceptions {\n        if (cancel) {\n          writer.rstStream(streamId, ErrorCode.CANCEL)\n          withLock {\n            currentPushRequests.remove(streamId)\n          }\n        }\n      }\n    }\n  }\n\n  internal fun pushHeadersLater(\n    streamId: Int,\n    requestHeaders: List<Header>,\n    inFinished: Boolean,\n  ) {\n    pushQueue.execute(\"$connectionName[$streamId] onHeaders\") {\n      val cancel = pushObserver.onHeaders(streamId, requestHeaders, inFinished)\n      ignoreIoExceptions {\n        if (cancel) writer.rstStream(streamId, ErrorCode.CANCEL)\n        if (cancel || inFinished) {\n          withLock {\n            currentPushRequests.remove(streamId)\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Eagerly reads `byteCount` bytes from the source before launching a background task to\n   * process the data.  This avoids corrupting the stream.\n   */\n  @Throws(IOException::class)\n  internal fun pushDataLater(\n    streamId: Int,\n    source: BufferedSource,\n    byteCount: Int,\n    inFinished: Boolean,\n  ) {\n    val buffer = Buffer()\n    source.require(byteCount.toLong()) // Eagerly read the frame before firing client thread.\n    source.read(buffer, byteCount.toLong())\n    pushQueue.execute(\"$connectionName[$streamId] onData\") {\n      ignoreIoExceptions {\n        val cancel = pushObserver.onData(streamId, buffer, byteCount, inFinished)\n        if (cancel) writer.rstStream(streamId, ErrorCode.CANCEL)\n        if (cancel || inFinished) {\n          withLock {\n            currentPushRequests.remove(streamId)\n          }\n        }\n      }\n    }\n  }\n\n  internal fun pushResetLater(\n    streamId: Int,\n    errorCode: ErrorCode,\n  ) {\n    pushQueue.execute(\"$connectionName[$streamId] onReset\") {\n      pushObserver.onReset(streamId, errorCode)\n      withLock {\n        currentPushRequests.remove(streamId)\n      }\n    }\n  }\n\n  /** Listener of streams and settings initiated by the peer. */\n  abstract class Listener {\n    /**\n     * Handle a new stream from this connection's peer. Implementations should respond by either\n     * [replying to the stream][Http2Stream.writeHeaders] or [closing it][Http2Stream.close]. This\n     * response does not need to be synchronous.\n     *\n     * Multiple calls to this method may be made concurrently.\n     */\n    @Throws(IOException::class)\n    abstract fun onStream(stream: Http2Stream)\n\n    /**\n     * Notification that the connection's peer's settings may have changed to [settings].\n     * Implementations should take appropriate action to handle the updated settings.\n     *\n     * Methods to this method may be made concurrently with [onStream]. But a calls to this method\n     * are serialized.\n     */\n    open fun onSettings(\n      connection: Http2Connection,\n      settings: Settings,\n    ) {}\n\n    companion object {\n      @JvmField\n      val REFUSE_INCOMING_STREAMS: Listener =\n        object : Listener() {\n          @Throws(IOException::class)\n          override fun onStream(stream: Http2Stream) {\n            stream.close(REFUSED_STREAM, null)\n          }\n        }\n    }\n  }\n\n  companion object {\n    const val OKHTTP_CLIENT_WINDOW_SIZE = 16 * 1024 * 1024\n\n    val DEFAULT_SETTINGS =\n      Settings().apply {\n        set(Settings.INITIAL_WINDOW_SIZE, DEFAULT_INITIAL_WINDOW_SIZE)\n        set(Settings.MAX_FRAME_SIZE, Http2.INITIAL_MAX_FRAME_SIZE)\n      }\n\n    const val INTERVAL_PING = 1\n    const val DEGRADED_PING = 2\n    const val AWAIT_PING = 3\n    const val DEGRADED_PONG_TIMEOUT_NS = 1_000_000_000 // 1 second.\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2ExchangeCodec.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.IOException\nimport java.net.ProtocolException\nimport java.util.Locale\nimport java.util.concurrent.TimeUnit\nimport okhttp3.Headers\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.headersContentLength\nimport okhttp3.internal.http.ExchangeCodec\nimport okhttp3.internal.http.ExchangeCodec.Carrier\nimport okhttp3.internal.http.HTTP_CONTINUE\nimport okhttp3.internal.http.RealInterceptorChain\nimport okhttp3.internal.http.RequestLine\nimport okhttp3.internal.http.StatusLine\nimport okhttp3.internal.http.promisesBody\nimport okhttp3.internal.http2.Header.Companion.RESPONSE_STATUS_UTF8\nimport okhttp3.internal.http2.Header.Companion.TARGET_AUTHORITY\nimport okhttp3.internal.http2.Header.Companion.TARGET_AUTHORITY_UTF8\nimport okhttp3.internal.http2.Header.Companion.TARGET_METHOD\nimport okhttp3.internal.http2.Header.Companion.TARGET_METHOD_UTF8\nimport okhttp3.internal.http2.Header.Companion.TARGET_PATH\nimport okhttp3.internal.http2.Header.Companion.TARGET_PATH_UTF8\nimport okhttp3.internal.http2.Header.Companion.TARGET_SCHEME\nimport okhttp3.internal.http2.Header.Companion.TARGET_SCHEME_UTF8\nimport okhttp3.internal.immutableListOf\nimport okio.Sink\nimport okio.Socket\nimport okio.Source\n\n/** Encode requests and responses using HTTP/2 frames. */\nclass Http2ExchangeCodec(\n  client: OkHttpClient,\n  override val carrier: Carrier,\n  private val chain: RealInterceptorChain,\n  private val http2Connection: Http2Connection,\n) : ExchangeCodec {\n  @Volatile private var stream: Http2Stream? = null\n\n  private val protocol: Protocol =\n    if (Protocol.H2_PRIOR_KNOWLEDGE in client.protocols) {\n      Protocol.H2_PRIOR_KNOWLEDGE\n    } else {\n      Protocol.HTTP_2\n    }\n\n  @Volatile\n  private var canceled = false\n\n  override val isResponseComplete: Boolean\n    get() = stream?.isSourceComplete == true\n\n  override val socket: Socket\n    get() = stream!!\n\n  override fun createRequestBody(\n    request: Request,\n    contentLength: Long,\n  ): Sink = stream!!.sink\n\n  override fun writeRequestHeaders(request: Request) {\n    if (stream != null) return\n\n    val hasRequestBody = request.body != null\n    val requestHeaders = http2HeadersList(request)\n    stream = http2Connection.newStream(requestHeaders, hasRequestBody)\n    // We may have been asked to cancel while creating the new stream and sending the request\n    // headers, but there was still no stream to close.\n    if (canceled) {\n      stream!!.closeLater(ErrorCode.CANCEL)\n      throw IOException(\"Canceled\")\n    }\n    stream!!.readTimeout().timeout(chain.readTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)\n    stream!!.writeTimeout().timeout(chain.writeTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)\n  }\n\n  override fun flushRequest() {\n    http2Connection.flush()\n  }\n\n  override fun finishRequest() {\n    stream!!.sink.close()\n  }\n\n  override fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {\n    val stream = stream ?: throw IOException(\"stream wasn't created\")\n    val headers = stream.takeHeaders(callerIsIdle = expectContinue)\n    val responseBuilder = readHttp2HeadersList(headers, protocol)\n    return if (expectContinue && responseBuilder.code == HTTP_CONTINUE) {\n      null\n    } else {\n      responseBuilder\n    }\n  }\n\n  override fun reportedContentLength(response: Response): Long =\n    when {\n      !response.promisesBody() -> 0L\n      else -> response.headersContentLength()\n    }\n\n  override fun openResponseBodySource(response: Response): Source = stream!!.source\n\n  override fun peekTrailers(): Headers? = stream!!.peekTrailers()\n\n  override fun cancel() {\n    canceled = true\n    stream?.closeLater(ErrorCode.CANCEL)\n  }\n\n  companion object {\n    private const val CONNECTION = \"connection\"\n    private const val HOST = \"host\"\n    private const val KEEP_ALIVE = \"keep-alive\"\n    private const val PROXY_CONNECTION = \"proxy-connection\"\n    private const val TRANSFER_ENCODING = \"transfer-encoding\"\n    private const val TE = \"te\"\n    private const val ENCODING = \"encoding\"\n    private const val UPGRADE = \"upgrade\"\n\n    /** See http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-8.1.3. */\n    private val HTTP_2_SKIPPED_REQUEST_HEADERS =\n      immutableListOf(\n        CONNECTION,\n        HOST,\n        KEEP_ALIVE,\n        PROXY_CONNECTION,\n        TE,\n        TRANSFER_ENCODING,\n        ENCODING,\n        UPGRADE,\n        TARGET_METHOD_UTF8,\n        TARGET_PATH_UTF8,\n        TARGET_SCHEME_UTF8,\n        TARGET_AUTHORITY_UTF8,\n      )\n    private val HTTP_2_SKIPPED_RESPONSE_HEADERS =\n      immutableListOf(\n        CONNECTION,\n        HOST,\n        KEEP_ALIVE,\n        PROXY_CONNECTION,\n        TE,\n        TRANSFER_ENCODING,\n        ENCODING,\n        UPGRADE,\n      )\n\n    fun http2HeadersList(request: Request): List<Header> {\n      val headers = request.headers\n      val result = ArrayList<Header>(headers.size + 4)\n      result.add(Header(TARGET_METHOD, request.method))\n      result.add(Header(TARGET_PATH, RequestLine.requestPath(request.url)))\n      val host = request.header(\"Host\")\n      if (host != null) {\n        result.add(Header(TARGET_AUTHORITY, host)) // Optional.\n      }\n      result.add(Header(TARGET_SCHEME, request.url.scheme))\n\n      for (i in 0 until headers.size) {\n        // header names must be lowercase.\n        val name = headers.name(i).lowercase(Locale.US)\n        if (name !in HTTP_2_SKIPPED_REQUEST_HEADERS ||\n          name == TE &&\n          headers.value(i) == \"trailers\"\n        ) {\n          result.add(Header(name, headers.value(i)))\n        }\n      }\n      return result\n    }\n\n    /** Returns headers for a name value block containing an HTTP/2 response. */\n    fun readHttp2HeadersList(\n      headerBlock: Headers,\n      protocol: Protocol,\n    ): Response.Builder {\n      var statusLine: StatusLine? = null\n      val headersBuilder = Headers.Builder()\n      for (i in 0 until headerBlock.size) {\n        val name = headerBlock.name(i)\n        val value = headerBlock.value(i)\n        if (name == RESPONSE_STATUS_UTF8) {\n          statusLine = StatusLine.parse(\"HTTP/1.1 $value\")\n        } else if (name !in HTTP_2_SKIPPED_RESPONSE_HEADERS) {\n          headersBuilder.addLenient(name, value)\n        }\n      }\n      if (statusLine == null) throw ProtocolException(\"Expected ':status' header not present\")\n\n      return Response\n        .Builder()\n        .protocol(protocol)\n        .code(statusLine.code)\n        .message(statusLine.message)\n        .headers(headersBuilder.build())\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2Reader.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.Closeable\nimport java.io.EOFException\nimport java.io.IOException\nimport java.util.logging.Level.FINE\nimport java.util.logging.Logger\nimport okhttp3.internal.and\nimport okhttp3.internal.format\nimport okhttp3.internal.http2.Http2.CONNECTION_PREFACE\nimport okhttp3.internal.http2.Http2.FLAG_ACK\nimport okhttp3.internal.http2.Http2.FLAG_COMPRESSED\nimport okhttp3.internal.http2.Http2.FLAG_END_HEADERS\nimport okhttp3.internal.http2.Http2.FLAG_END_STREAM\nimport okhttp3.internal.http2.Http2.FLAG_PADDED\nimport okhttp3.internal.http2.Http2.FLAG_PRIORITY\nimport okhttp3.internal.http2.Http2.INITIAL_MAX_FRAME_SIZE\nimport okhttp3.internal.http2.Http2.TYPE_CONTINUATION\nimport okhttp3.internal.http2.Http2.TYPE_DATA\nimport okhttp3.internal.http2.Http2.TYPE_GOAWAY\nimport okhttp3.internal.http2.Http2.TYPE_HEADERS\nimport okhttp3.internal.http2.Http2.TYPE_PING\nimport okhttp3.internal.http2.Http2.TYPE_PRIORITY\nimport okhttp3.internal.http2.Http2.TYPE_PUSH_PROMISE\nimport okhttp3.internal.http2.Http2.TYPE_RST_STREAM\nimport okhttp3.internal.http2.Http2.TYPE_SETTINGS\nimport okhttp3.internal.http2.Http2.TYPE_WINDOW_UPDATE\nimport okhttp3.internal.http2.Http2.formattedType\nimport okhttp3.internal.http2.Http2.frameLog\nimport okhttp3.internal.http2.Http2.frameLogWindowUpdate\nimport okhttp3.internal.readMedium\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.Source\nimport okio.Timeout\n\n/**\n * Reads HTTP/2 transport frames.\n *\n * This implementation assumes we do not send an increased [frame][Settings.getMaxFrameSize] to the\n * peer. Hence, we expect all frames to have a max length of [Http2.INITIAL_MAX_FRAME_SIZE].\n */\nclass Http2Reader(\n  /** Creates a frame reader with max header table size of 4096. */\n  private val source: BufferedSource,\n  private val client: Boolean,\n) : Closeable {\n  private val continuation: ContinuationSource = ContinuationSource(this.source)\n  private val hpackReader: Hpack.Reader =\n    Hpack.Reader(\n      source = continuation,\n      headerTableSizeSetting = 4096,\n    )\n\n  @Throws(IOException::class)\n  fun readConnectionPreface(handler: Handler) {\n    if (client) {\n      // The client reads the initial SETTINGS frame.\n      if (!nextFrame(true, handler)) {\n        throw IOException(\"Required SETTINGS preface not received\")\n      }\n    } else {\n      // The server reads the CONNECTION_PREFACE byte string.\n      val connectionPreface = source.readByteString(CONNECTION_PREFACE.size.toLong())\n      if (logger.isLoggable(FINE)) logger.fine(format(\"<< CONNECTION ${connectionPreface.hex()}\"))\n      if (CONNECTION_PREFACE != connectionPreface) {\n        throw IOException(\"Expected a connection header but was ${connectionPreface.utf8()}\")\n      }\n    }\n  }\n\n  @Throws(IOException::class)\n  fun nextFrame(\n    requireSettings: Boolean,\n    handler: Handler,\n  ): Boolean {\n    try {\n      source.require(9) // Frame header size.\n    } catch (e: EOFException) {\n      return false // This might be a normal socket close.\n    }\n\n    //  0                   1                   2                   3\n    //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n    // |                 Length (24)                   |\n    // +---------------+---------------+---------------+\n    // |   Type (8)    |   Flags (8)   |\n    // +-+-+-----------+---------------+-------------------------------+\n    // |R|                 Stream Identifier (31)                      |\n    // +=+=============================================================+\n    // |                   Frame Payload (0...)                      ...\n    // +---------------------------------------------------------------+\n    val length = source.readMedium()\n    if (length > INITIAL_MAX_FRAME_SIZE) {\n      throw IOException(\"FRAME_SIZE_ERROR: $length\")\n    }\n    val type = source.readByte() and 0xff\n    val flags = source.readByte() and 0xff\n    val streamId = source.readInt() and 0x7fffffff // Ignore reserved bit.\n    if (type != TYPE_WINDOW_UPDATE && logger.isLoggable(FINE)) {\n      logger.fine(frameLog(true, streamId, length, type, flags))\n    }\n\n    if (requireSettings && type != TYPE_SETTINGS) {\n      throw IOException(\"Expected a SETTINGS frame but was ${formattedType(type)}\")\n    }\n\n    when (type) {\n      TYPE_DATA -> readData(handler, length, flags, streamId)\n      TYPE_HEADERS -> readHeaders(handler, length, flags, streamId)\n      TYPE_PRIORITY -> readPriority(handler, length, flags, streamId)\n      TYPE_RST_STREAM -> readRstStream(handler, length, flags, streamId)\n      TYPE_SETTINGS -> readSettings(handler, length, flags, streamId)\n      TYPE_PUSH_PROMISE -> readPushPromise(handler, length, flags, streamId)\n      TYPE_PING -> readPing(handler, length, flags, streamId)\n      TYPE_GOAWAY -> readGoAway(handler, length, flags, streamId)\n      TYPE_WINDOW_UPDATE -> readWindowUpdate(handler, length, flags, streamId)\n      else -> source.skip(length.toLong()) // Implementations MUST discard frames of unknown types.\n    }\n\n    return true\n  }\n\n  @Throws(IOException::class)\n  private fun readHeaders(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    if (streamId == 0) throw IOException(\"PROTOCOL_ERROR: TYPE_HEADERS streamId == 0\")\n\n    val endStream = (flags and FLAG_END_STREAM) != 0\n    val padding = if (flags and FLAG_PADDED != 0) source.readByte() and 0xff else 0\n\n    var headerBlockLength = length\n    if (flags and FLAG_PRIORITY != 0) {\n      readPriority(handler, streamId)\n      headerBlockLength -= 5 // account for above read.\n    }\n    headerBlockLength = lengthWithoutPadding(headerBlockLength, flags, padding)\n    val headerBlock = readHeaderBlock(headerBlockLength, padding, flags, streamId)\n\n    handler.headers(endStream, streamId, -1, headerBlock)\n  }\n\n  @Throws(IOException::class)\n  private fun readHeaderBlock(\n    length: Int,\n    padding: Int,\n    flags: Int,\n    streamId: Int,\n  ): List<Header> {\n    continuation.left = length\n    continuation.padding = padding\n    continuation.flags = flags\n    continuation.streamId = streamId\n\n    // TODO: Concat multi-value headers with 0x0, except COOKIE, which uses 0x3B, 0x20.\n    // http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8.1.2.5\n    hpackReader.readHeaders()\n    return hpackReader.getAndResetHeaderList()\n  }\n\n  @Throws(IOException::class)\n  private fun readData(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    if (streamId == 0) throw IOException(\"PROTOCOL_ERROR: TYPE_DATA streamId == 0\")\n\n    // TODO: checkState open or half-closed (local) or raise STREAM_CLOSED\n    val inFinished = flags and FLAG_END_STREAM != 0\n    val gzipped = flags and FLAG_COMPRESSED != 0\n    if (gzipped) {\n      throw IOException(\"PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA\")\n    }\n\n    val padding = if (flags and FLAG_PADDED != 0) source.readByte() and 0xff else 0\n    val dataLength = lengthWithoutPadding(length, flags, padding)\n\n    handler.data(inFinished, streamId, source, dataLength)\n    source.skip(padding.toLong())\n  }\n\n  @Throws(IOException::class)\n  private fun readPriority(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    if (length != 5) throw IOException(\"TYPE_PRIORITY length: $length != 5\")\n    if (streamId == 0) throw IOException(\"TYPE_PRIORITY streamId == 0\")\n    readPriority(handler, streamId)\n  }\n\n  @Throws(IOException::class)\n  private fun readPriority(\n    handler: Handler,\n    streamId: Int,\n  ) {\n    val w1 = source.readInt()\n    val exclusive = w1 and 0x80000000.toInt() != 0\n    val streamDependency = w1 and 0x7fffffff\n    val weight = (source.readByte() and 0xff) + 1\n    handler.priority(streamId, streamDependency, weight, exclusive)\n  }\n\n  @Throws(IOException::class)\n  private fun readRstStream(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    if (length != 4) throw IOException(\"TYPE_RST_STREAM length: $length != 4\")\n    if (streamId == 0) throw IOException(\"TYPE_RST_STREAM streamId == 0\")\n    val errorCodeInt = source.readInt()\n    val errorCode =\n      ErrorCode.fromHttp2(errorCodeInt) ?: throw IOException(\n        \"TYPE_RST_STREAM unexpected error code: $errorCodeInt\",\n      )\n    handler.rstStream(streamId, errorCode)\n  }\n\n  @Throws(IOException::class)\n  private fun readSettings(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    if (streamId != 0) throw IOException(\"TYPE_SETTINGS streamId != 0\")\n    if (flags and FLAG_ACK != 0) {\n      if (length != 0) throw IOException(\"FRAME_SIZE_ERROR ack frame should be empty!\")\n      handler.ackSettings()\n      return\n    }\n\n    if (length % 6 != 0) throw IOException(\"TYPE_SETTINGS length % 6 != 0: $length\")\n    val settings = Settings()\n    for (i in 0 until length step 6) {\n      val id = source.readShort() and 0xffff\n      val value = source.readInt()\n\n      when (id) {\n        // SETTINGS_HEADER_TABLE_SIZE\n        1 -> {\n        }\n\n        // SETTINGS_ENABLE_PUSH\n        2 -> {\n          if (value != 0 && value != 1) {\n            throw IOException(\"PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1\")\n          }\n        }\n\n        // SETTINGS_MAX_CONCURRENT_STREAMS\n        3 -> {\n        }\n\n        // SETTINGS_INITIAL_WINDOW_SIZE\n        4 -> {\n          if (value < 0) {\n            throw IOException(\"PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1\")\n          }\n        }\n\n        // SETTINGS_MAX_FRAME_SIZE\n        5 -> {\n          if (value < INITIAL_MAX_FRAME_SIZE || value > 16777215) {\n            throw IOException(\"PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: $value\")\n          }\n        }\n\n        // SETTINGS_MAX_HEADER_LIST_SIZE\n        6 -> { // Advisory only, so ignored.\n        }\n\n        // Must ignore setting with unknown id.\n        else -> {\n        }\n      }\n      settings[id] = value\n    }\n    handler.settings(false, settings)\n  }\n\n  @Throws(IOException::class)\n  private fun readPushPromise(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    if (streamId == 0) {\n      throw IOException(\"PROTOCOL_ERROR: TYPE_PUSH_PROMISE streamId == 0\")\n    }\n    val padding = if (flags and FLAG_PADDED != 0) source.readByte() and 0xff else 0\n    val promisedStreamId = source.readInt() and 0x7fffffff\n    val headerBlockLength = lengthWithoutPadding(length - 4, flags, padding) // - 4 for readInt().\n    val headerBlock = readHeaderBlock(headerBlockLength, padding, flags, streamId)\n    handler.pushPromise(streamId, promisedStreamId, headerBlock)\n  }\n\n  @Throws(IOException::class)\n  private fun readPing(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    if (length != 8) throw IOException(\"TYPE_PING length != 8: $length\")\n    if (streamId != 0) throw IOException(\"TYPE_PING streamId != 0\")\n    val payload1 = source.readInt()\n    val payload2 = source.readInt()\n    val ack = flags and FLAG_ACK != 0\n    handler.ping(ack, payload1, payload2)\n  }\n\n  @Throws(IOException::class)\n  private fun readGoAway(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    if (length < 8) throw IOException(\"TYPE_GOAWAY length < 8: $length\")\n    if (streamId != 0) throw IOException(\"TYPE_GOAWAY streamId != 0\")\n    val lastStreamId = source.readInt()\n    val errorCodeInt = source.readInt()\n    val opaqueDataLength = length - 8\n    val errorCode =\n      ErrorCode.fromHttp2(errorCodeInt) ?: throw IOException(\n        \"TYPE_GOAWAY unexpected error code: $errorCodeInt\",\n      )\n    var debugData = ByteString.EMPTY\n    if (opaqueDataLength > 0) { // Must read debug data in order to not corrupt the connection.\n      debugData = source.readByteString(opaqueDataLength.toLong())\n    }\n    handler.goAway(lastStreamId, errorCode, debugData)\n  }\n\n  /** Unlike other `readXxx()` functions, this one must log the frame before returning. */\n  @Throws(IOException::class)\n  private fun readWindowUpdate(\n    handler: Handler,\n    length: Int,\n    flags: Int,\n    streamId: Int,\n  ) {\n    val increment: Long\n    try {\n      if (length != 4) throw IOException(\"TYPE_WINDOW_UPDATE length !=4: $length\")\n      increment = source.readInt() and 0x7fffffffL\n      if (increment == 0L) throw IOException(\"windowSizeIncrement was 0\")\n    } catch (e: Exception) {\n      logger.fine(frameLog(true, streamId, length, TYPE_WINDOW_UPDATE, flags))\n      throw e\n    }\n    if (logger.isLoggable(FINE)) {\n      logger.fine(\n        frameLogWindowUpdate(\n          inbound = true,\n          streamId = streamId,\n          length = length,\n          windowSizeIncrement = increment,\n        ),\n      )\n    }\n    handler.windowUpdate(streamId, increment)\n  }\n\n  @Throws(IOException::class)\n  override fun close() {\n    source.close()\n  }\n\n  /**\n   * Decompression of the header block occurs above the framing layer. This class lazily reads\n   * continuation frames as they are needed by [Hpack.Reader.readHeaders].\n   */\n  internal class ContinuationSource(\n    private val source: BufferedSource,\n  ) : Source {\n    var flags: Int = 0\n    var streamId: Int = 0\n\n    var left: Int = 0\n    var padding: Int = 0\n\n    @Throws(IOException::class)\n    override fun read(\n      sink: Buffer,\n      byteCount: Long,\n    ): Long {\n      while (left == 0) {\n        source.skip(padding.toLong())\n        padding = 0\n        if (flags and FLAG_END_HEADERS != 0) return -1L\n        readContinuationHeader()\n        // TODO: test case for empty continuation header?\n      }\n\n      val read = source.read(sink, minOf(byteCount, left.toLong()))\n      if (read == -1L) return -1L\n      left -= read.toInt()\n      return read\n    }\n\n    override fun timeout(): Timeout = source.timeout()\n\n    @Throws(IOException::class)\n    override fun close() {\n    }\n\n    @Throws(IOException::class)\n    private fun readContinuationHeader() {\n      val previousStreamId = streamId\n\n      val length = source.readMedium()\n      left = length\n      val type = source.readByte() and 0xff\n      flags = source.readByte() and 0xff\n      if (logger.isLoggable(FINE)) logger.fine(frameLog(true, streamId, length, type, flags))\n      streamId = source.readInt() and 0x7fffffff\n      if (type != TYPE_CONTINUATION) throw IOException(\"$type != TYPE_CONTINUATION\")\n      if (streamId != previousStreamId) throw IOException(\"TYPE_CONTINUATION streamId changed\")\n    }\n  }\n\n  interface Handler {\n    @Throws(IOException::class)\n    fun data(\n      inFinished: Boolean,\n      streamId: Int,\n      source: BufferedSource,\n      length: Int,\n    )\n\n    /**\n     * Create or update incoming headers, creating the corresponding streams if necessary. Frames\n     * that trigger this are HEADERS and PUSH_PROMISE.\n     *\n     * @param inFinished true if the sender will not send further frames.\n     * @param streamId the stream owning these headers.\n     * @param associatedStreamId the stream that triggered the sender to create this stream.\n     */\n    fun headers(\n      inFinished: Boolean,\n      streamId: Int,\n      associatedStreamId: Int,\n      headerBlock: List<Header>,\n    )\n\n    fun rstStream(\n      streamId: Int,\n      errorCode: ErrorCode,\n    )\n\n    fun settings(\n      clearPrevious: Boolean,\n      settings: Settings,\n    )\n\n    /** HTTP/2 only. */\n    fun ackSettings()\n\n    /**\n     * Read a connection-level ping from the peer. `ack` indicates this is a reply. The data\n     * in `payload1` and `payload2` opaque binary, and there are no rules on the content.\n     */\n    fun ping(\n      ack: Boolean,\n      payload1: Int,\n      payload2: Int,\n    )\n\n    /**\n     * The peer tells us to stop creating streams. It is safe to replay streams with\n     * `ID > lastGoodStreamId` on a new connection.  In- flight streams with\n     * `ID <= lastGoodStreamId` can only be replayed on a new connection if they are idempotent.\n     *\n     * @param lastGoodStreamId the last stream ID the peer processed before sending this message. If\n     *     [lastGoodStreamId] is zero, the peer processed no frames.\n     * @param errorCode reason for closing the connection.\n     * @param debugData only valid for HTTP/2; opaque debug data to send.\n     */\n    fun goAway(\n      lastGoodStreamId: Int,\n      errorCode: ErrorCode,\n      debugData: ByteString,\n    )\n\n    /**\n     * Notifies that an additional `windowSizeIncrement` bytes can be sent on `streamId`, or the\n     * connection if `streamId` is zero.\n     */\n    fun windowUpdate(\n      streamId: Int,\n      windowSizeIncrement: Long,\n    )\n\n    /**\n     * Called when reading a headers or priority frame. This may be used to change the stream's\n     * weight from the default (16) to a new value.\n     *\n     * @param streamId stream which has a priority change.\n     * @param streamDependency the stream ID this stream is dependent on.\n     * @param weight relative proportion of priority in `[1..256]`.\n     * @param exclusive inserts this stream ID as the sole child of `streamDependency`.\n     */\n    fun priority(\n      streamId: Int,\n      streamDependency: Int,\n      weight: Int,\n      exclusive: Boolean,\n    )\n\n    /**\n     * HTTP/2 only. Receive a push promise header block.\n     *\n     * A push promise contains all the headers that pertain to a server-initiated request, and a\n     * `promisedStreamId` to which response frames will be delivered. Push promise frames are sent\n     * as a part of the response to `streamId`.\n     *\n     * @param streamId client-initiated stream ID.  Must be an odd number.\n     * @param promisedStreamId server-initiated stream ID.  Must be an even number.\n     * @param requestHeaders minimally includes `:method`, `:scheme`, `:authority`, and `:path`.\n     */\n    @Throws(IOException::class)\n    fun pushPromise(\n      streamId: Int,\n      promisedStreamId: Int,\n      requestHeaders: List<Header>,\n    )\n\n    /**\n     * HTTP/2 only. Expresses that resources for the connection or a client- initiated stream are\n     * available from a different network location or protocol configuration.\n     *\n     * See [alt-svc][alt_svc].\n     *\n     * [alt_svc]: http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-01\n     *\n     * @param streamId when a client-initiated stream ID (odd number), the origin of this alternate\n     *     service is the origin of the stream. When zero, the origin is specified in the `origin`\n     *     parameter.\n     * @param origin when present, the [origin](http://tools.ietf.org/html/rfc6454) is typically\n     *     represented as a combination of scheme, host and port. When empty, the origin is that of\n     *     the `streamId`.\n     * @param protocol an ALPN protocol, such as `h2`.\n     * @param host an IP address or hostname.\n     * @param port the IP port associated with the service.\n     * @param maxAge time in seconds that this alternative is considered fresh.\n     */\n    fun alternateService(\n      streamId: Int,\n      origin: String,\n      protocol: ByteString,\n      host: String,\n      port: Int,\n      maxAge: Long,\n    )\n  }\n\n  companion object {\n    val logger: Logger = Logger.getLogger(Http2::class.java.name)\n\n    @Throws(IOException::class)\n    fun lengthWithoutPadding(\n      length: Int,\n      flags: Int,\n      padding: Int,\n    ): Int {\n      var result = length\n      if (flags and FLAG_PADDED != 0) result-- // Account for reading the padding length.\n      if (padding > result) {\n        throw IOException(\"PROTOCOL_ERROR padding $padding > remaining length $result\")\n      }\n      result -= padding\n      return result\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2Stream.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.EOFException\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.SocketTimeoutException\nimport java.util.ArrayDeque\nimport okhttp3.Headers\nimport okhttp3.internal.concurrent.Lockable\nimport okhttp3.internal.concurrent.assertLockNotHeld\nimport okhttp3.internal.concurrent.notifyAll\nimport okhttp3.internal.concurrent.wait\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.http2.flowcontrol.WindowCounter\nimport okhttp3.internal.toHeaderList\nimport okio.AsyncTimeout\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.Sink\nimport okio.Socket\nimport okio.Source\nimport okio.Timeout\n\n/** A logical bidirectional stream. */\n@Suppress(\"NAME_SHADOWING\")\nclass Http2Stream internal constructor(\n  val id: Int,\n  val connection: Http2Connection,\n  outFinished: Boolean,\n  inFinished: Boolean,\n  headers: Headers?,\n) : Lockable,\n  Socket {\n  // Internal state is guarded by `this`. No long-running or potentially blocking operations are\n  // performed while the lock is held.\n\n  /** The bytes consumed and acknowledged by the stream. */\n  val readBytes: WindowCounter = WindowCounter(id)\n\n  /** The total number of bytes produced by the application. */\n  var writeBytesTotal = 0L\n    internal set\n\n  /** The total number of bytes permitted to be produced by incoming `WINDOW_UPDATE` frame. */\n  var writeBytesMaximum: Long = connection.peerSettings.initialWindowSize.toLong()\n    internal set\n\n  /** Received headers yet to be [taken][takeHeaders]. */\n  private val headersQueue = ArrayDeque<Headers>()\n\n  /** True if response headers have been sent or received. */\n  private var hasResponseHeaders: Boolean = false\n\n  override val source =\n    FramingSource(\n      maxByteCount = connection.okHttpSettings.initialWindowSize.toLong(),\n      finished = inFinished,\n    )\n  override val sink =\n    FramingSink(\n      finished = outFinished,\n    )\n  internal val readTimeout = StreamTimeout()\n  internal val writeTimeout = StreamTimeout()\n\n  /**\n   * The reason why this stream was closed, or null if it closed normally or has not yet been\n   * closed.\n   *\n   * If there are multiple reasons to abnormally close this stream (such as both peers closing it\n   * near-simultaneously) then this is the first reason known to this peer.\n   */\n  internal var errorCode: ErrorCode? = null\n    get() = withLock { field }\n\n  /** The exception that explains [errorCode]. Null if no exception was provided. */\n  internal var errorException: IOException? = null\n\n  init {\n    if (headers != null) {\n      check(!isLocallyInitiated) { \"locally-initiated streams shouldn't have headers yet\" }\n      headersQueue += headers\n    } else {\n      check(isLocallyInitiated) { \"remotely-initiated streams should have headers\" }\n    }\n  }\n\n  /**\n   * Returns true if this stream is open. A stream is open until either:\n   *\n   *  * A `SYN_RESET` frame abnormally terminates the stream.\n   *  * Both input and output streams have transmitted all data and headers.\n   *\n   * Note that the input stream may continue to yield data even after a stream reports itself as\n   * not open. This is because input data is buffered.\n   */\n  val isOpen: Boolean\n    get() {\n      withLock {\n        if (errorCode != null) {\n          return false\n        }\n        if ((source.finished || source.closed) &&\n          (sink.finished || sink.closed) &&\n          hasResponseHeaders\n        ) {\n          return false\n        }\n        return true\n      }\n    }\n\n  /** Returns true if this stream was created by this peer. */\n  val isLocallyInitiated: Boolean\n    get() {\n      val streamIsClient = (id and 1) == 1\n      return connection.client == streamIsClient\n    }\n\n  val isSourceComplete: Boolean\n    get() = withLock { source.finished && source.readBuffer.exhausted() }\n\n  /**\n   * Removes and returns the stream's received response headers, blocking if necessary until headers\n   * have been received. If the returned list contains multiple blocks of headers the blocks will be\n   * delimited by 'null'.\n   *\n   * @param callerIsIdle true if the caller isn't sending any more bytes until the peer responds.\n   *     This is true after a `Expect-Continue` request, false for duplex requests, and false for\n   *     all other requests.\n   */\n  @Throws(IOException::class)\n  fun takeHeaders(callerIsIdle: Boolean = false): Headers {\n    withLock {\n      while (headersQueue.isEmpty() && errorCode == null) {\n        val doReadTimeout = callerIsIdle || doReadTimeout()\n        if (doReadTimeout) {\n          readTimeout.enter()\n        }\n        try {\n          waitForIo()\n        } finally {\n          if (doReadTimeout) {\n            readTimeout.exitAndThrowIfTimedOut()\n          }\n        }\n      }\n      if (headersQueue.isNotEmpty()) {\n        return headersQueue.removeFirst()\n      }\n      throw errorException ?: StreamResetException(errorCode!!)\n    }\n  }\n\n  /**\n   * Returns the trailers if they're immediately available.\n   */\n  @Throws(IOException::class)\n  fun peekTrailers(): Headers? {\n    withLock {\n      if (source.finished && source.receiveBuffer.exhausted() && source.readBuffer.exhausted()) {\n        return source.trailers ?: Headers.EMPTY\n      }\n      if (errorCode != null) {\n        throw errorException ?: StreamResetException(errorCode!!)\n      }\n      return null\n    }\n  }\n\n  /**\n   * Sends a reply to an incoming stream.\n   *\n   * @param outFinished true to eagerly finish the output stream to send data to the remote peer.\n   *     Corresponds to `FLAG_FIN`.\n   * @param flushHeaders true to force flush the response headers. This should be true unless the\n   *     response body exists and will be written immediately.\n   */\n  @Throws(IOException::class)\n  fun writeHeaders(\n    responseHeaders: List<Header>,\n    outFinished: Boolean,\n    flushHeaders: Boolean,\n  ) {\n    assertLockNotHeld()\n\n    var flushHeaders = flushHeaders\n    withLock {\n      this.hasResponseHeaders = true\n      if (outFinished) {\n        this.sink.finished = true\n        notifyAll() // Because doReadTimeout() may have changed.\n      }\n    }\n\n    // Only DATA frames are subject to flow-control. Transmit the HEADER frame if the connection\n    // flow-control window is fully depleted.\n    if (!flushHeaders) {\n      withLock {\n        flushHeaders = (connection.writeBytesTotal >= connection.writeBytesMaximum)\n      }\n    }\n\n    connection.writeHeaders(id, outFinished, responseHeaders)\n\n    if (flushHeaders) {\n      connection.flush()\n    }\n  }\n\n  fun enqueueTrailers(trailers: Headers) {\n    withLock {\n      check(!sink.finished) { \"already finished\" }\n      require(trailers.size != 0) { \"trailers.size() == 0\" }\n      this.sink.trailers = trailers\n    }\n  }\n\n  fun readTimeout(): Timeout = readTimeout\n\n  fun writeTimeout(): Timeout = writeTimeout\n\n  /**\n   * Abnormally terminate this stream. This blocks until the `RST_STREAM` frame has been\n   * transmitted.\n   */\n  @Throws(IOException::class)\n  fun close(\n    rstStatusCode: ErrorCode,\n    errorException: IOException?,\n  ) {\n    if (!closeInternal(rstStatusCode, errorException)) {\n      return // Already closed.\n    }\n    connection.writeSynReset(id, rstStatusCode)\n  }\n\n  override fun cancel() {\n    closeLater(ErrorCode.CANCEL)\n  }\n\n  /**\n   * Abnormally terminate this stream. This enqueues a `RST_STREAM` frame and returns immediately.\n   */\n  fun closeLater(errorCode: ErrorCode) {\n    if (!closeInternal(errorCode, null)) {\n      return // Already closed.\n    }\n    connection.writeSynResetLater(id, errorCode)\n  }\n\n  /** Returns true if this stream was closed. */\n  private fun closeInternal(\n    errorCode: ErrorCode,\n    errorException: IOException?,\n  ): Boolean {\n    assertLockNotHeld()\n\n    withLock {\n      if (this.errorCode != null) {\n        return false\n      }\n      this.errorCode = errorCode\n      this.errorException = errorException\n      notifyAll()\n      if (source.finished && sink.finished) {\n        return false\n      }\n    }\n    connection.removeStream(id)\n    return true\n  }\n\n  @Throws(IOException::class)\n  fun receiveData(\n    source: BufferedSource,\n    length: Int,\n  ) {\n    assertLockNotHeld()\n\n    this.source.receive(source, length.toLong())\n  }\n\n  /** Accept headers from the network and store them until the client calls [takeHeaders]. */\n  fun receiveHeaders(\n    headers: Headers,\n    inFinished: Boolean,\n  ) {\n    assertLockNotHeld()\n\n    val open: Boolean\n    withLock {\n      if (!hasResponseHeaders ||\n        headers[Header.RESPONSE_STATUS_UTF8] != null ||\n        headers[Header.TARGET_METHOD_UTF8] != null\n      ) {\n        hasResponseHeaders = true\n        headersQueue += headers\n      } else {\n        this.source.trailers = headers\n      }\n      if (inFinished) {\n        this.source.finished = true\n      }\n      open = isOpen\n      notifyAll()\n    }\n    if (!open) {\n      connection.removeStream(id)\n    }\n  }\n\n  fun receiveRstStream(errorCode: ErrorCode) {\n    withLock {\n      if (this.errorCode == null) {\n        this.errorCode = errorCode\n        notifyAll()\n      }\n    }\n  }\n\n  /**\n   * Returns true if read timeouts should be enforced while reading response headers or body bytes.\n   * We always do timeouts in the HTTP server role. For clients, we only do timeouts after the\n   * request is transmitted. This is only interesting for duplex calls where the request and\n   * response may be interleaved.\n   *\n   * Read this value only once for each enter/exit pair because its value can change.\n   */\n  private fun doReadTimeout() = !connection.client || sink.closed || sink.finished\n\n  /**\n   * A source that reads the incoming data frames of a stream. Although this class uses\n   * synchronization to safely receive incoming data frames, it is not intended for use by multiple\n   * readers.\n   */\n  inner class FramingSource internal constructor(\n    /** Maximum number of bytes to buffer before reporting a flow control error. */\n    private val maxByteCount: Long,\n    /**\n     * True if either side has cleanly shut down this stream. We will receive no more bytes beyond\n     * those already in the buffer.\n     */\n    internal var finished: Boolean,\n  ) : Source {\n    /** Buffer to receive data from the network into. Only accessed by the reader thread. */\n    val receiveBuffer = Buffer()\n\n    /** Buffer with readable data. Guarded by Http2Stream.this. */\n    val readBuffer = Buffer()\n\n    /**\n     * Received trailers. Null unless the server has provided trailers. Undefined until the stream\n     * is exhausted. Guarded by Http2Stream.this.\n     */\n    var trailers: Headers? = null\n\n    /** True if the caller has closed this stream. */\n    internal var closed: Boolean = false\n\n    @Throws(IOException::class)\n    override fun read(\n      sink: Buffer,\n      byteCount: Long,\n    ): Long {\n      require(byteCount >= 0L) { \"byteCount < 0: $byteCount\" }\n\n      while (true) {\n        var tryAgain = false\n        var readBytesDelivered = -1L\n        var errorExceptionToDeliver: IOException? = null\n\n        // 1. Decide what to do in a synchronized block.\n\n        withLock {\n          val doReadTimeout = doReadTimeout()\n          if (doReadTimeout) {\n            readTimeout.enter()\n          }\n          try {\n            if (errorCode != null && !finished) {\n              // Prepare to deliver an error.\n              errorExceptionToDeliver = errorException ?: StreamResetException(errorCode!!)\n            }\n\n            if (closed) {\n              throw IOException(\"stream closed\")\n            } else if (readBuffer.size > 0L) {\n              // Prepare to read bytes. Start by moving them to the caller's buffer.\n              readBytesDelivered = readBuffer.read(sink, minOf(byteCount, readBuffer.size))\n              readBytes.update(total = readBytesDelivered)\n\n              val unacknowledgedBytesRead = readBytes.unacknowledged\n              if (errorExceptionToDeliver == null &&\n                unacknowledgedBytesRead >= connection.okHttpSettings.initialWindowSize / 2\n              ) {\n                // Flow control: notify the peer that we're ready for more data! Only send a\n                // WINDOW_UPDATE if the stream isn't in error.\n                connection.writeWindowUpdateLater(id, unacknowledgedBytesRead)\n                readBytes.update(acknowledged = unacknowledgedBytesRead)\n              }\n            } else if (!finished && errorExceptionToDeliver == null) {\n              // Nothing to do. Wait until that changes then try again.\n              waitForIo()\n              tryAgain = true\n            }\n          } finally {\n            if (doReadTimeout) {\n              readTimeout.exitAndThrowIfTimedOut()\n            }\n          }\n        }\n        connection.flowControlListener.receivingStreamWindowChanged(id, readBytes, readBuffer.size)\n\n        // 2. Do it outside of the synchronized block and timeout.\n\n        if (tryAgain) {\n          continue\n        }\n\n        if (readBytesDelivered != -1L) {\n          return readBytesDelivered\n        }\n\n        if (errorExceptionToDeliver != null) {\n          // We defer throwing the exception until now so that we can refill the connection\n          // flow-control window. This is necessary because we don't transmit window updates until\n          // the application reads the data. If we throw this prior to updating the connection\n          // flow-control window, we risk having it go to 0 preventing the server from sending data.\n          throw errorExceptionToDeliver!!\n        }\n\n        return -1L // This source is exhausted.\n      }\n    }\n\n    private fun updateConnectionFlowControl(read: Long) {\n      assertLockNotHeld()\n\n      connection.updateConnectionFlowControl(read)\n    }\n\n    /**\n     * Accept bytes on the connection's reader thread. This function avoids holding locks while it\n     * performs blocking reads for the incoming bytes.\n     */\n    @Throws(IOException::class)\n    internal fun receive(\n      source: BufferedSource,\n      byteCount: Long,\n    ) {\n      assertLockNotHeld()\n\n      var remainingByteCount = byteCount\n\n      while (remainingByteCount > 0L) {\n        val finished: Boolean\n        val flowControlError: Boolean\n        withLock {\n          finished = this.finished\n          flowControlError = remainingByteCount + readBuffer.size > maxByteCount\n        }\n\n        // If the peer sends more data than we can handle, discard it and close the connection.\n        if (flowControlError) {\n          source.skip(remainingByteCount)\n          closeLater(ErrorCode.FLOW_CONTROL_ERROR)\n          return\n        }\n\n        // Discard data received after the stream is finished. It's probably a benign race.\n        if (finished) {\n          source.skip(remainingByteCount)\n          return\n        }\n\n        // Fill the receive buffer without holding any locks.\n        val read = source.read(receiveBuffer, remainingByteCount)\n        if (read == -1L) throw EOFException()\n        remainingByteCount -= read\n\n        // Move the received data to the read buffer to the reader can read it. If this source has\n        // been closed since this read began we must discard the incoming data and tell the\n        // connection we've done so.\n        withLock {\n          if (closed) {\n            receiveBuffer.clear()\n          } else {\n            val wasEmpty = readBuffer.size == 0L\n            readBuffer.writeAll(receiveBuffer)\n            if (wasEmpty) {\n              notifyAll()\n            }\n          }\n        }\n      }\n\n      // Update the connection flow control, as this is a shared resource.\n      // Even if our stream doesn't need more data, others might.\n      // But delay updating the stream flow control until that stream has been\n      // consumed\n      updateConnectionFlowControl(byteCount)\n\n      // Notify that buffer size changed\n      connection.flowControlListener.receivingStreamWindowChanged(id, readBytes, readBuffer.size)\n    }\n\n    override fun timeout(): Timeout = readTimeout\n\n    @Throws(IOException::class)\n    override fun close() {\n      val bytesDiscarded: Long\n      withLock {\n        closed = true\n        bytesDiscarded = readBuffer.size\n        readBuffer.clear()\n        notifyAll() // TODO(jwilson): Unnecessary?\n      }\n      if (bytesDiscarded > 0L) {\n        updateConnectionFlowControl(bytesDiscarded)\n      }\n      cancelStreamIfNecessary()\n    }\n  }\n\n  @Throws(IOException::class)\n  internal fun cancelStreamIfNecessary() {\n    assertLockNotHeld()\n\n    val open: Boolean\n    val cancel: Boolean\n    withLock {\n      cancel = !source.finished && source.closed && (sink.finished || sink.closed)\n      open = isOpen\n    }\n    if (cancel) {\n      // RST this stream to prevent additional data from being sent. This is safe because the input\n      // stream is closed (we won't use any further bytes) and the output stream is either finished\n      // or closed (so RSTing both streams doesn't cause harm).\n      this@Http2Stream.close(ErrorCode.CANCEL, null)\n    } else if (!open) {\n      connection.removeStream(id)\n    }\n  }\n\n  /** A sink that writes outgoing data frames of a stream. This class is not thread safe. */\n  inner class FramingSink(\n    /** True if either side has cleanly shut down this stream. We shall send no more bytes. */\n    var finished: Boolean = false,\n  ) : Sink {\n    /**\n     * Buffer of outgoing data. This batches writes of small writes into this sink as larges frames\n     * written to the outgoing connection. Batching saves the (small) framing overhead.\n     */\n    private val sendBuffer = Buffer()\n\n    /** Trailers to send at the end of the stream. */\n    var trailers: Headers? = null\n\n    var closed: Boolean = false\n\n    @Throws(IOException::class)\n    override fun write(\n      source: Buffer,\n      byteCount: Long,\n    ) {\n      assertLockNotHeld()\n\n      sendBuffer.write(source, byteCount)\n      while (sendBuffer.size >= EMIT_BUFFER_SIZE) {\n        emitFrame(false)\n      }\n    }\n\n    /**\n     * Emit a single data frame to the connection. The frame's size be limited by this stream's\n     * write window. This method will block until the write window is nonempty.\n     */\n    @Throws(IOException::class)\n    private fun emitFrame(outFinishedOnLastFrame: Boolean) {\n      val toWrite: Long\n      val outFinished: Boolean\n      withLock {\n        writeTimeout.enter()\n        try {\n          while (writeBytesTotal >= writeBytesMaximum &&\n            !finished &&\n            !closed &&\n            errorCode == null\n          ) {\n            waitForIo() // Wait until we receive a WINDOW_UPDATE for this stream.\n          }\n        } finally {\n          writeTimeout.exitAndThrowIfTimedOut()\n        }\n\n        checkOutNotClosed() // Kick out if the stream was reset or closed while waiting.\n        toWrite = minOf(writeBytesMaximum - writeBytesTotal, sendBuffer.size)\n        writeBytesTotal += toWrite\n        outFinished = outFinishedOnLastFrame && toWrite == sendBuffer.size\n      }\n\n      writeTimeout.enter()\n      try {\n        connection.writeData(id, outFinished, sendBuffer, toWrite)\n      } finally {\n        writeTimeout.exitAndThrowIfTimedOut()\n      }\n    }\n\n    @Throws(IOException::class)\n    override fun flush() {\n      assertLockNotHeld()\n\n      withLock {\n        checkOutNotClosed()\n      }\n      // TODO(jwilson): flush the connection?!\n      while (sendBuffer.size > 0L) {\n        emitFrame(false)\n        connection.flush()\n      }\n    }\n\n    override fun timeout(): Timeout = writeTimeout\n\n    @Throws(IOException::class)\n    override fun close() {\n      assertLockNotHeld()\n\n      val outFinished: Boolean\n      withLock {\n        if (closed) return\n        outFinished = errorCode == null\n      }\n      if (!sink.finished) {\n        // We have 0 or more frames of data, and 0 or more frames of trailers. We need to send at\n        // least one frame with the END_STREAM flag set. That must be the last frame, and the\n        // trailers must be sent after all of the data.\n        val hasData = sendBuffer.size > 0L\n        val hasTrailers = trailers != null\n        when {\n          hasTrailers -> {\n            while (sendBuffer.size > 0L) {\n              emitFrame(false)\n            }\n            connection.writeHeaders(id, outFinished, trailers!!.toHeaderList())\n          }\n\n          hasData -> {\n            while (sendBuffer.size > 0L) {\n              emitFrame(true)\n            }\n          }\n\n          outFinished -> {\n            connection.writeData(id, true, null, 0L)\n          }\n        }\n      }\n      withLock {\n        closed = true\n        notifyAll() // Because doReadTimeout() may have changed.\n      }\n      connection.flush()\n      cancelStreamIfNecessary()\n    }\n  }\n\n  companion object {\n    internal const val EMIT_BUFFER_SIZE = 16384L\n  }\n\n  /** [delta] will be negative if a settings frame initial window is smaller than the last. */\n  fun addBytesToWriteWindow(delta: Long) {\n    writeBytesMaximum += delta\n    if (delta > 0L) {\n      notifyAll()\n    }\n  }\n\n  @Throws(IOException::class)\n  internal fun checkOutNotClosed() {\n    when {\n      sink.closed -> throw IOException(\"stream closed\")\n      sink.finished -> throw IOException(\"stream finished\")\n      errorCode != null -> throw errorException ?: StreamResetException(errorCode!!)\n    }\n  }\n\n  /**\n   * Like [Object.wait], but throws an [InterruptedIOException] when interrupted instead of the more\n   * awkward [InterruptedException].\n   */\n  @Throws(InterruptedIOException::class)\n  internal fun waitForIo() {\n    try {\n      wait()\n    } catch (_: InterruptedException) {\n      Thread.currentThread().interrupt() // Retain interrupted status.\n      throw InterruptedIOException()\n    }\n  }\n\n  /**\n   * The Okio timeout watchdog will call [timedOut] if the timeout is reached. In that case we close\n   * the stream (asynchronously) which will notify the waiting thread.\n   */\n  internal inner class StreamTimeout : AsyncTimeout() {\n    override fun timedOut() {\n      closeLater(ErrorCode.CANCEL)\n      connection.sendDegradedPingLater()\n    }\n\n    override fun newTimeoutException(cause: IOException?): IOException =\n      SocketTimeoutException(\"timeout\").apply {\n        if (cause != null) {\n          initCause(cause)\n        }\n      }\n\n    @Throws(IOException::class)\n    fun exitAndThrowIfTimedOut() {\n      if (exit()) throw newTimeoutException(null)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2Writer.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.util.logging.Level.FINE\nimport java.util.logging.Logger\nimport okhttp3.internal.concurrent.Lockable\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.format\nimport okhttp3.internal.http2.Http2.CONNECTION_PREFACE\nimport okhttp3.internal.http2.Http2.FLAG_ACK\nimport okhttp3.internal.http2.Http2.FLAG_END_HEADERS\nimport okhttp3.internal.http2.Http2.FLAG_END_STREAM\nimport okhttp3.internal.http2.Http2.FLAG_NONE\nimport okhttp3.internal.http2.Http2.INITIAL_MAX_FRAME_SIZE\nimport okhttp3.internal.http2.Http2.TYPE_CONTINUATION\nimport okhttp3.internal.http2.Http2.TYPE_DATA\nimport okhttp3.internal.http2.Http2.TYPE_GOAWAY\nimport okhttp3.internal.http2.Http2.TYPE_HEADERS\nimport okhttp3.internal.http2.Http2.TYPE_PING\nimport okhttp3.internal.http2.Http2.TYPE_PUSH_PROMISE\nimport okhttp3.internal.http2.Http2.TYPE_RST_STREAM\nimport okhttp3.internal.http2.Http2.TYPE_SETTINGS\nimport okhttp3.internal.http2.Http2.TYPE_WINDOW_UPDATE\nimport okhttp3.internal.http2.Http2.frameLog\nimport okhttp3.internal.http2.Http2.frameLogWindowUpdate\nimport okhttp3.internal.writeMedium\nimport okio.Buffer\nimport okio.BufferedSink\n\n/** Writes HTTP/2 transport frames. */\n@Suppress(\"NAME_SHADOWING\")\nclass Http2Writer(\n  private val sink: BufferedSink,\n  private val client: Boolean,\n) : Closeable,\n  Lockable {\n  private val hpackBuffer: Buffer = Buffer()\n  private var maxFrameSize: Int = INITIAL_MAX_FRAME_SIZE\n  private var closed: Boolean = false\n  val hpackWriter: Hpack.Writer = Hpack.Writer(out = hpackBuffer)\n\n  @Throws(IOException::class)\n  fun connectionPreface() {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      if (!client) return // Nothing to write; servers don't send connection headers!\n      if (logger.isLoggable(FINE)) {\n        logger.fine(format(\">> CONNECTION ${CONNECTION_PREFACE.hex()}\"))\n      }\n      sink.write(CONNECTION_PREFACE)\n      sink.flush()\n    }\n  }\n\n  /** Applies `peerSettings` and then sends a settings ACK. */\n  @Throws(IOException::class)\n  fun applyAndAckSettings(peerSettings: Settings) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      this.maxFrameSize = peerSettings.getMaxFrameSize(maxFrameSize)\n      if (peerSettings.headerTableSize != -1) {\n        hpackWriter.resizeHeaderTable(peerSettings.headerTableSize)\n      }\n      frameHeader(\n        streamId = 0,\n        length = 0,\n        type = TYPE_SETTINGS,\n        flags = FLAG_ACK,\n      )\n      sink.flush()\n    }\n  }\n\n  /**\n   * HTTP/2 only. Send a push promise header block.\n   *\n   * A push promise contains all the headers that pertain to a server-initiated request, and a\n   * `promisedStreamId` to which response frames will be delivered. Push promise frames are sent as\n   * a part of the response to `streamId`. The `promisedStreamId` has a priority of one greater than\n   * `streamId`.\n   *\n   * @param streamId client-initiated stream ID.  Must be an odd number.\n   * @param promisedStreamId server-initiated stream ID.  Must be an even number.\n   * @param requestHeaders minimally includes `:method`, `:scheme`, `:authority`, and `:path`.\n   */\n  @Throws(IOException::class)\n  fun pushPromise(\n    streamId: Int,\n    promisedStreamId: Int,\n    requestHeaders: List<Header>,\n  ) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      hpackWriter.writeHeaders(requestHeaders)\n\n      val byteCount = hpackBuffer.size\n      val length = minOf(maxFrameSize - 4L, byteCount).toInt()\n      frameHeader(\n        streamId = streamId,\n        length = length + 4,\n        type = TYPE_PUSH_PROMISE,\n        flags = if (byteCount == length.toLong()) FLAG_END_HEADERS else 0,\n      )\n      sink.writeInt(promisedStreamId and 0x7fffffff)\n      sink.write(hpackBuffer, length.toLong())\n\n      if (byteCount > length) writeContinuationFrames(streamId, byteCount - length)\n    }\n  }\n\n  @Throws(IOException::class)\n  fun flush() {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      sink.flush()\n    }\n  }\n\n  @Throws(IOException::class)\n  fun rstStream(\n    streamId: Int,\n    errorCode: ErrorCode,\n  ) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      require(errorCode.httpCode != -1)\n\n      frameHeader(\n        streamId = streamId,\n        length = 4,\n        type = TYPE_RST_STREAM,\n        flags = FLAG_NONE,\n      )\n      sink.writeInt(errorCode.httpCode)\n      sink.flush()\n    }\n  }\n\n  /** The maximum size of bytes that may be sent in a single call to [data]. */\n  fun maxDataLength(): Int = maxFrameSize\n\n  /**\n   * `source.length` may be longer than the max length of the variant's data frame. Implementations\n   * must send multiple frames as necessary.\n   *\n   * @param source the buffer to draw bytes from. May be null if byteCount is 0.\n   * @param byteCount must be between 0 and the minimum of `source.length` and [maxDataLength].\n   */\n  @Throws(IOException::class)\n  fun data(\n    outFinished: Boolean,\n    streamId: Int,\n    source: Buffer?,\n    byteCount: Int,\n  ) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      var flags = FLAG_NONE\n      if (outFinished) flags = flags or FLAG_END_STREAM\n      dataFrame(streamId, flags, source, byteCount)\n    }\n  }\n\n  @Throws(IOException::class)\n  fun dataFrame(\n    streamId: Int,\n    flags: Int,\n    buffer: Buffer?,\n    byteCount: Int,\n  ) {\n    frameHeader(\n      streamId = streamId,\n      length = byteCount,\n      type = TYPE_DATA,\n      flags = flags,\n    )\n    if (byteCount > 0) {\n      sink.write(buffer!!, byteCount.toLong())\n    }\n  }\n\n  /** Write okhttp's settings to the peer. */\n  @Throws(IOException::class)\n  fun settings(settings: Settings) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      frameHeader(\n        streamId = 0,\n        length = settings.size() * 6,\n        type = TYPE_SETTINGS,\n        flags = FLAG_NONE,\n      )\n      for (i in 0 until Settings.COUNT) {\n        if (!settings.isSet(i)) continue\n        sink.writeShort(i)\n        sink.writeInt(settings[i])\n      }\n      sink.flush()\n    }\n  }\n\n  /**\n   * Send a connection-level ping to the peer. `ack` indicates this is a reply. The data in\n   * `payload1` and `payload2` opaque binary, and there are no rules on the content.\n   */\n  @Throws(IOException::class)\n  fun ping(\n    ack: Boolean,\n    payload1: Int,\n    payload2: Int,\n  ) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      frameHeader(\n        streamId = 0,\n        length = 8,\n        type = TYPE_PING,\n        flags = if (ack) FLAG_ACK else FLAG_NONE,\n      )\n      sink.writeInt(payload1)\n      sink.writeInt(payload2)\n      sink.flush()\n    }\n  }\n\n  /**\n   * Tell the peer to stop creating streams and that we last processed `lastGoodStreamId`, or zero\n   * if no streams were processed.\n   *\n   * @param lastGoodStreamId the last stream ID processed, or zero if no streams were processed.\n   * @param errorCode reason for closing the connection.\n   * @param debugData only valid for HTTP/2; opaque debug data to send.\n   */\n  @Throws(IOException::class)\n  fun goAway(\n    lastGoodStreamId: Int,\n    errorCode: ErrorCode,\n    debugData: ByteArray,\n  ) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      require(errorCode.httpCode != -1) { \"errorCode.httpCode == -1\" }\n      frameHeader(\n        streamId = 0,\n        length = 8 + debugData.size,\n        type = TYPE_GOAWAY,\n        flags = FLAG_NONE,\n      )\n      sink.writeInt(lastGoodStreamId)\n      sink.writeInt(errorCode.httpCode)\n      if (debugData.isNotEmpty()) {\n        sink.write(debugData)\n      }\n      sink.flush()\n    }\n  }\n\n  /**\n   * Inform peer that an additional `windowSizeIncrement` bytes can be sent on `streamId`, or the\n   * connection if `streamId` is zero.\n   */\n  @Throws(IOException::class)\n  fun windowUpdate(\n    streamId: Int,\n    windowSizeIncrement: Long,\n  ) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      require(windowSizeIncrement != 0L && windowSizeIncrement <= 0x7fffffffL) {\n        \"windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: $windowSizeIncrement\"\n      }\n      if (logger.isLoggable(FINE)) {\n        logger.fine(\n          frameLogWindowUpdate(\n            inbound = false,\n            streamId = streamId,\n            length = 4,\n            windowSizeIncrement = windowSizeIncrement,\n          ),\n        )\n      }\n      frameHeader(\n        streamId = streamId,\n        length = 4,\n        type = TYPE_WINDOW_UPDATE,\n        flags = FLAG_NONE,\n      )\n      sink.writeInt(windowSizeIncrement.toInt())\n      sink.flush()\n    }\n  }\n\n  @Throws(IOException::class)\n  fun frameHeader(\n    streamId: Int,\n    length: Int,\n    type: Int,\n    flags: Int,\n  ) {\n    if (type != TYPE_WINDOW_UPDATE && logger.isLoggable(FINE)) {\n      logger.fine(frameLog(false, streamId, length, type, flags))\n    }\n    require(length <= maxFrameSize) { \"FRAME_SIZE_ERROR length > $maxFrameSize: $length\" }\n    require(streamId and 0x80000000.toInt() == 0) { \"reserved bit set: $streamId\" }\n    sink.writeMedium(length)\n    sink.writeByte(type and 0xff)\n    sink.writeByte(flags and 0xff)\n    sink.writeInt(streamId and 0x7fffffff)\n  }\n\n  @Throws(IOException::class)\n  override fun close() {\n    withLock {\n      closed = true\n      sink.close()\n    }\n  }\n\n  @Throws(IOException::class)\n  private fun writeContinuationFrames(\n    streamId: Int,\n    byteCount: Long,\n  ) {\n    var byteCount = byteCount\n    while (byteCount > 0L) {\n      val length = minOf(maxFrameSize.toLong(), byteCount)\n      byteCount -= length\n      frameHeader(\n        streamId = streamId,\n        length = length.toInt(),\n        type = TYPE_CONTINUATION,\n        flags = if (byteCount == 0L) FLAG_END_HEADERS else 0,\n      )\n      sink.write(hpackBuffer, length)\n    }\n  }\n\n  @Throws(IOException::class)\n  fun headers(\n    outFinished: Boolean,\n    streamId: Int,\n    headerBlock: List<Header>,\n  ) {\n    withLock {\n      if (closed) throw IOException(\"closed\")\n      hpackWriter.writeHeaders(headerBlock)\n\n      val byteCount = hpackBuffer.size\n      val length = minOf(maxFrameSize.toLong(), byteCount)\n      var flags = if (byteCount == length) FLAG_END_HEADERS else 0\n      if (outFinished) flags = flags or FLAG_END_STREAM\n      frameHeader(\n        streamId = streamId,\n        length = length.toInt(),\n        type = TYPE_HEADERS,\n        flags = flags,\n      )\n      sink.write(hpackBuffer, length)\n\n      if (byteCount > length) writeContinuationFrames(streamId, byteCount - length)\n    }\n  }\n\n  companion object {\n    private val logger = Logger.getLogger(Http2::class.java.name)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Huffman.kt",
    "content": "/*\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.IOException\nimport okhttp3.internal.and\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString\n\n/**\n * This class was originally composed from the following classes in\n * [Twitter Hpack][twitter_hpack].\n *\n *  * `com.twitter.hpack.HuffmanEncoder`\n *  * `com.twitter.hpack.HuffmanDecoder`\n *  * `com.twitter.hpack.HpackUtil`\n *\n * [twitter_hpack]: https://github.com/twitter/hpack\n */\nobject Huffman {\n  // Appendix C: Huffman Codes\n  // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-B\n  private val CODES =\n    intArrayOf(\n      0x1ff8,\n      0x7fffd8,\n      0xfffffe2,\n      0xfffffe3,\n      0xfffffe4,\n      0xfffffe5,\n      0xfffffe6,\n      0xfffffe7,\n      0xfffffe8,\n      0xffffea,\n      0x3ffffffc,\n      0xfffffe9,\n      0xfffffea,\n      0x3ffffffd,\n      0xfffffeb,\n      0xfffffec,\n      0xfffffed,\n      0xfffffee,\n      0xfffffef,\n      0xffffff0,\n      0xffffff1,\n      0xffffff2,\n      0x3ffffffe,\n      0xffffff3,\n      0xffffff4,\n      0xffffff5,\n      0xffffff6,\n      0xffffff7,\n      0xffffff8,\n      0xffffff9,\n      0xffffffa,\n      0xffffffb,\n      0x14,\n      0x3f8,\n      0x3f9,\n      0xffa,\n      0x1ff9,\n      0x15,\n      0xf8,\n      0x7fa,\n      0x3fa,\n      0x3fb,\n      0xf9,\n      0x7fb,\n      0xfa,\n      0x16,\n      0x17,\n      0x18,\n      0x0,\n      0x1,\n      0x2,\n      0x19,\n      0x1a,\n      0x1b,\n      0x1c,\n      0x1d,\n      0x1e,\n      0x1f,\n      0x5c,\n      0xfb,\n      0x7ffc,\n      0x20,\n      0xffb,\n      0x3fc,\n      0x1ffa,\n      0x21,\n      0x5d,\n      0x5e,\n      0x5f,\n      0x60,\n      0x61,\n      0x62,\n      0x63,\n      0x64,\n      0x65,\n      0x66,\n      0x67,\n      0x68,\n      0x69,\n      0x6a,\n      0x6b,\n      0x6c,\n      0x6d,\n      0x6e,\n      0x6f,\n      0x70,\n      0x71,\n      0x72,\n      0xfc,\n      0x73,\n      0xfd,\n      0x1ffb,\n      0x7fff0,\n      0x1ffc,\n      0x3ffc,\n      0x22,\n      0x7ffd,\n      0x3,\n      0x23,\n      0x4,\n      0x24,\n      0x5,\n      0x25,\n      0x26,\n      0x27,\n      0x6,\n      0x74,\n      0x75,\n      0x28,\n      0x29,\n      0x2a,\n      0x7,\n      0x2b,\n      0x76,\n      0x2c,\n      0x8,\n      0x9,\n      0x2d,\n      0x77,\n      0x78,\n      0x79,\n      0x7a,\n      0x7b,\n      0x7ffe,\n      0x7fc,\n      0x3ffd,\n      0x1ffd,\n      0xffffffc,\n      0xfffe6,\n      0x3fffd2,\n      0xfffe7,\n      0xfffe8,\n      0x3fffd3,\n      0x3fffd4,\n      0x3fffd5,\n      0x7fffd9,\n      0x3fffd6,\n      0x7fffda,\n      0x7fffdb,\n      0x7fffdc,\n      0x7fffdd,\n      0x7fffde,\n      0xffffeb,\n      0x7fffdf,\n      0xffffec,\n      0xffffed,\n      0x3fffd7,\n      0x7fffe0,\n      0xffffee,\n      0x7fffe1,\n      0x7fffe2,\n      0x7fffe3,\n      0x7fffe4,\n      0x1fffdc,\n      0x3fffd8,\n      0x7fffe5,\n      0x3fffd9,\n      0x7fffe6,\n      0x7fffe7,\n      0xffffef,\n      0x3fffda,\n      0x1fffdd,\n      0xfffe9,\n      0x3fffdb,\n      0x3fffdc,\n      0x7fffe8,\n      0x7fffe9,\n      0x1fffde,\n      0x7fffea,\n      0x3fffdd,\n      0x3fffde,\n      0xfffff0,\n      0x1fffdf,\n      0x3fffdf,\n      0x7fffeb,\n      0x7fffec,\n      0x1fffe0,\n      0x1fffe1,\n      0x3fffe0,\n      0x1fffe2,\n      0x7fffed,\n      0x3fffe1,\n      0x7fffee,\n      0x7fffef,\n      0xfffea,\n      0x3fffe2,\n      0x3fffe3,\n      0x3fffe4,\n      0x7ffff0,\n      0x3fffe5,\n      0x3fffe6,\n      0x7ffff1,\n      0x3ffffe0,\n      0x3ffffe1,\n      0xfffeb,\n      0x7fff1,\n      0x3fffe7,\n      0x7ffff2,\n      0x3fffe8,\n      0x1ffffec,\n      0x3ffffe2,\n      0x3ffffe3,\n      0x3ffffe4,\n      0x7ffffde,\n      0x7ffffdf,\n      0x3ffffe5,\n      0xfffff1,\n      0x1ffffed,\n      0x7fff2,\n      0x1fffe3,\n      0x3ffffe6,\n      0x7ffffe0,\n      0x7ffffe1,\n      0x3ffffe7,\n      0x7ffffe2,\n      0xfffff2,\n      0x1fffe4,\n      0x1fffe5,\n      0x3ffffe8,\n      0x3ffffe9,\n      0xffffffd,\n      0x7ffffe3,\n      0x7ffffe4,\n      0x7ffffe5,\n      0xfffec,\n      0xfffff3,\n      0xfffed,\n      0x1fffe6,\n      0x3fffe9,\n      0x1fffe7,\n      0x1fffe8,\n      0x7ffff3,\n      0x3fffea,\n      0x3fffeb,\n      0x1ffffee,\n      0x1ffffef,\n      0xfffff4,\n      0xfffff5,\n      0x3ffffea,\n      0x7ffff4,\n      0x3ffffeb,\n      0x7ffffe6,\n      0x3ffffec,\n      0x3ffffed,\n      0x7ffffe7,\n      0x7ffffe8,\n      0x7ffffe9,\n      0x7ffffea,\n      0x7ffffeb,\n      0xffffffe,\n      0x7ffffec,\n      0x7ffffed,\n      0x7ffffee,\n      0x7ffffef,\n      0x7fffff0,\n      0x3ffffee,\n    )\n\n  private val CODE_BIT_COUNTS =\n    byteArrayOf(\n      13,\n      23,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      24,\n      30,\n      28,\n      28,\n      30,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      30,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      28,\n      6,\n      10,\n      10,\n      12,\n      13,\n      6,\n      8,\n      11,\n      10,\n      10,\n      8,\n      11,\n      8,\n      6,\n      6,\n      6,\n      5,\n      5,\n      5,\n      6,\n      6,\n      6,\n      6,\n      6,\n      6,\n      6,\n      7,\n      8,\n      15,\n      6,\n      12,\n      10,\n      13,\n      6,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      7,\n      8,\n      7,\n      8,\n      13,\n      19,\n      13,\n      14,\n      6,\n      15,\n      5,\n      6,\n      5,\n      6,\n      5,\n      6,\n      6,\n      6,\n      5,\n      7,\n      7,\n      6,\n      6,\n      6,\n      5,\n      6,\n      7,\n      6,\n      5,\n      5,\n      6,\n      7,\n      7,\n      7,\n      7,\n      7,\n      15,\n      11,\n      14,\n      13,\n      28,\n      20,\n      22,\n      20,\n      20,\n      22,\n      22,\n      22,\n      23,\n      22,\n      23,\n      23,\n      23,\n      23,\n      23,\n      24,\n      23,\n      24,\n      24,\n      22,\n      23,\n      24,\n      23,\n      23,\n      23,\n      23,\n      21,\n      22,\n      23,\n      22,\n      23,\n      23,\n      24,\n      22,\n      21,\n      20,\n      22,\n      22,\n      23,\n      23,\n      21,\n      23,\n      22,\n      22,\n      24,\n      21,\n      22,\n      23,\n      23,\n      21,\n      21,\n      22,\n      21,\n      23,\n      22,\n      23,\n      23,\n      20,\n      22,\n      22,\n      22,\n      23,\n      22,\n      22,\n      23,\n      26,\n      26,\n      20,\n      19,\n      22,\n      23,\n      22,\n      25,\n      26,\n      26,\n      26,\n      27,\n      27,\n      26,\n      24,\n      25,\n      19,\n      21,\n      26,\n      27,\n      27,\n      26,\n      27,\n      24,\n      21,\n      21,\n      26,\n      26,\n      28,\n      27,\n      27,\n      27,\n      20,\n      24,\n      20,\n      21,\n      22,\n      21,\n      21,\n      23,\n      22,\n      22,\n      25,\n      25,\n      24,\n      24,\n      26,\n      23,\n      26,\n      27,\n      26,\n      26,\n      27,\n      27,\n      27,\n      27,\n      27,\n      28,\n      27,\n      27,\n      27,\n      27,\n      27,\n      26,\n    )\n\n  private val root = Node()\n\n  init {\n    for (i in CODE_BIT_COUNTS.indices) {\n      addCode(i, CODES[i], CODE_BIT_COUNTS[i].toInt())\n    }\n  }\n\n  @Throws(IOException::class)\n  fun encode(\n    source: ByteString,\n    sink: BufferedSink,\n  ) {\n    var accumulator = 0L\n    var accumulatorBitCount = 0\n\n    for (i in 0 until source.size) {\n      val symbol = source[i] and 0xff\n      val code = CODES[symbol]\n      val codeBitCount = CODE_BIT_COUNTS[symbol].toInt()\n\n      accumulator = (accumulator shl codeBitCount) or code.toLong()\n      accumulatorBitCount += codeBitCount\n\n      while (accumulatorBitCount >= 8) {\n        accumulatorBitCount -= 8\n        sink.writeByte((accumulator shr accumulatorBitCount).toInt())\n      }\n    }\n\n    if (accumulatorBitCount > 0) {\n      accumulator = accumulator shl (8 - accumulatorBitCount)\n      accumulator = accumulator or (0xffL ushr accumulatorBitCount)\n      sink.writeByte(accumulator.toInt())\n    }\n  }\n\n  fun encodedLength(bytes: ByteString): Int {\n    var bitCount = 0L\n\n    for (i in 0 until bytes.size) {\n      val byteIn = bytes[i] and 0xff\n      bitCount += CODE_BIT_COUNTS[byteIn].toLong()\n    }\n\n    return ((bitCount + 7) shr 3).toInt() // Round up to an even byte.\n  }\n\n  fun decode(\n    source: BufferedSource,\n    byteCount: Long,\n    sink: BufferedSink,\n  ) {\n    var node = root\n    var accumulator = 0\n    var accumulatorBitCount = 0\n    for (i in 0 until byteCount) {\n      val byteIn = source.readByte() and 0xff\n      accumulator = accumulator shl 8 or byteIn\n      accumulatorBitCount += 8\n      while (accumulatorBitCount >= 8) {\n        val childIndex = (accumulator ushr (accumulatorBitCount - 8)) and 0xff\n        node = node.children!![childIndex]!!\n        if (node.children == null) {\n          // Terminal node.\n          sink.writeByte(node.symbol)\n          accumulatorBitCount -= node.terminalBitCount\n          node = root\n        } else {\n          // Non-terminal node.\n          accumulatorBitCount -= 8\n        }\n      }\n    }\n\n    while (accumulatorBitCount > 0) {\n      val childIndex = (accumulator shl (8 - accumulatorBitCount)) and 0xff\n      node = node.children!![childIndex]!!\n      if (node.children != null || node.terminalBitCount > accumulatorBitCount) {\n        break\n      }\n      sink.writeByte(node.symbol)\n      accumulatorBitCount -= node.terminalBitCount\n      node = root\n    }\n  }\n\n  private fun addCode(\n    symbol: Int,\n    code: Int,\n    codeBitCount: Int,\n  ) {\n    val terminal = Node(symbol, codeBitCount)\n\n    var accumulatorBitCount = codeBitCount\n    var node = root\n    while (accumulatorBitCount > 8) {\n      accumulatorBitCount -= 8\n      val childIndex = (code ushr accumulatorBitCount) and 0xff\n      val children = node.children!!\n      var child = children[childIndex]\n      if (child == null) {\n        child = Node()\n        children[childIndex] = child\n      }\n      node = child\n    }\n\n    val shift = 8 - accumulatorBitCount\n    val start = (code shl shift) and 0xff\n    val end = 1 shl shift\n    node.children!!.fill(terminal, start, start + end)\n  }\n\n  private class Node {\n    /** Null if terminal. */\n    val children: Array<Node?>?\n\n    /** Terminal nodes have a symbol. */\n    val symbol: Int\n\n    /** Number of bits represented in the terminal node. */\n    val terminalBitCount: Int\n\n    /** Construct an internal node. */\n    constructor() {\n      this.children = arrayOfNulls(256)\n      this.symbol = 0 // Not read.\n      this.terminalBitCount = 0 // Not read.\n    }\n\n    /** Construct a terminal node. */\n    constructor(symbol: Int, bits: Int) {\n      this.children = null\n      this.symbol = symbol\n      val b = bits and 0x07\n      this.terminalBitCount = if (b == 0) 8 else b\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/PushObserver.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.IOException\nimport okhttp3.Protocol\nimport okio.BufferedSource\n\n/**\n * [HTTP/2][Protocol.HTTP_2] only. Processes server-initiated HTTP requests on the client.\n * Implementations must quickly dispatch callbacks to avoid creating a bottleneck.\n *\n * While [onReset] may occur at any time, the following callbacks are expected in order,\n * correlated by stream ID.\n *\n *  * [onRequest]\n *  * [onHeaders] (unless canceled)\n *  * [onData] (optional sequence of data frames)\n *\n * As a stream ID is scoped to a single HTTP/2 connection, implementations which target multiple\n * connections should expect repetition of stream IDs.\n *\n * Return true to request cancellation of a pushed stream.  Note that this does not guarantee\n * future frames won't arrive on the stream ID.\n */\ninterface PushObserver {\n  /**\n   * Describes the request that the server intends to push a response for.\n   *\n   * @param streamId server-initiated stream ID: an even number.\n   * @param requestHeaders minimally includes `:method`, `:scheme`, `:authority`,\n   * and `:path`.\n   */\n  fun onRequest(\n    streamId: Int,\n    requestHeaders: List<Header>,\n  ): Boolean\n\n  /**\n   * The response headers corresponding to a pushed request.  When [last] is true, there are\n   * no data frames to follow.\n   *\n   * @param streamId server-initiated stream ID: an even number.\n   * @param responseHeaders minimally includes `:status`.\n   * @param last when true, there is no response data.\n   */\n  fun onHeaders(\n    streamId: Int,\n    responseHeaders: List<Header>,\n    last: Boolean,\n  ): Boolean\n\n  /**\n   * A chunk of response data corresponding to a pushed request.  This data must either be read or\n   * skipped.\n   *\n   * @param streamId server-initiated stream ID: an even number.\n   * @param source location of data corresponding with this stream ID.\n   * @param byteCount number of bytes to read or skip from the source.\n   * @param last when true, there are no data frames to follow.\n   */\n  @Throws(IOException::class)\n  fun onData(\n    streamId: Int,\n    source: BufferedSource,\n    byteCount: Int,\n    last: Boolean,\n  ): Boolean\n\n  /** Indicates the reason why this stream was canceled. */\n  fun onReset(\n    streamId: Int,\n    errorCode: ErrorCode,\n  )\n\n  companion object {\n    @JvmField val CANCEL: PushObserver = PushObserverCancel()\n\n    private class PushObserverCancel : PushObserver {\n      override fun onRequest(\n        streamId: Int,\n        requestHeaders: List<Header>,\n      ): Boolean = true\n\n      override fun onHeaders(\n        streamId: Int,\n        responseHeaders: List<Header>,\n        last: Boolean,\n      ): Boolean = true\n\n      @Throws(IOException::class)\n      override fun onData(\n        streamId: Int,\n        source: BufferedSource,\n        byteCount: Int,\n        last: Boolean,\n      ): Boolean {\n        source.skip(byteCount.toLong())\n        return true\n      }\n\n      override fun onReset(\n        streamId: Int,\n        errorCode: ErrorCode,\n      ) {\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Settings.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\n/**\n * Settings describe characteristics of the sending peer, which are used by the receiving peer.\n * Settings are [connection][Http2Connection] scoped.\n */\nclass Settings {\n  /** Bitfield of which flags that values. */\n  private var set: Int = 0\n\n  /** Flag values. */\n  private val values = IntArray(COUNT)\n\n  /** Returns -1 if unset. */\n  val headerTableSize: Int\n    get() {\n      val bit = 1 shl HEADER_TABLE_SIZE\n      return if (bit and set != 0) values[HEADER_TABLE_SIZE] else -1\n    }\n\n  val initialWindowSize: Int\n    get() {\n      val bit = 1 shl INITIAL_WINDOW_SIZE\n      return if (bit and set != 0) values[INITIAL_WINDOW_SIZE] else DEFAULT_INITIAL_WINDOW_SIZE\n    }\n\n  fun clear() {\n    set = 0\n    values.fill(0)\n  }\n\n  operator fun set(\n    id: Int,\n    value: Int,\n  ): Settings {\n    if (id < 0 || id >= values.size) {\n      return this // Discard unknown settings.\n    }\n\n    val bit = 1 shl id\n    set = set or bit\n    values[id] = value\n    return this\n  }\n\n  /** Returns true if a value has been assigned for the setting `id`. */\n  fun isSet(id: Int): Boolean {\n    val bit = 1 shl id\n    return set and bit != 0\n  }\n\n  /** Returns the value for the setting `id`, or 0 if unset. */\n  operator fun get(id: Int): Int = values[id]\n\n  /** Returns the number of settings that have values assigned. */\n  fun size(): Int = Integer.bitCount(set)\n\n  // TODO: honor this setting.\n  fun getEnablePush(defaultValue: Boolean): Boolean {\n    val bit = 1 shl ENABLE_PUSH\n    return if (bit and set != 0) values[ENABLE_PUSH] == 1 else defaultValue\n  }\n\n  fun getMaxConcurrentStreams(): Int {\n    val bit = 1 shl MAX_CONCURRENT_STREAMS\n    return if (bit and set != 0) values[MAX_CONCURRENT_STREAMS] else Int.MAX_VALUE\n  }\n\n  fun getMaxFrameSize(defaultValue: Int): Int {\n    val bit = 1 shl MAX_FRAME_SIZE\n    return if (bit and set != 0) values[MAX_FRAME_SIZE] else defaultValue\n  }\n\n  fun getMaxHeaderListSize(defaultValue: Int): Int {\n    val bit = 1 shl MAX_HEADER_LIST_SIZE\n    return if (bit and set != 0) values[MAX_HEADER_LIST_SIZE] else defaultValue\n  }\n\n  /**\n   * Writes `other` into this. If any setting is populated by this and `other`, the\n   * value and flags from `other` will be kept.\n   */\n  fun merge(other: Settings) {\n    for (i in 0 until COUNT) {\n      if (!other.isSet(i)) continue\n      set(i, other[i])\n    }\n  }\n\n  companion object {\n    /**\n     * From the HTTP/2 specs, the default initial window size for all streams is 64 KiB. (Chrome 25\n     * uses 10 MiB).\n     */\n    const val DEFAULT_INITIAL_WINDOW_SIZE = 65535\n\n    /** HTTP/2: Size in bytes of the table used to decode the sender's header blocks. */\n    const val HEADER_TABLE_SIZE = 1\n\n    /** HTTP/2: The peer must not send a PUSH_PROMISE frame when this is 0. */\n    const val ENABLE_PUSH = 2\n\n    /** Sender's maximum number of concurrent streams. */\n    const val MAX_CONCURRENT_STREAMS = 3\n\n    /** Window size in bytes. */\n    const val INITIAL_WINDOW_SIZE = 4\n\n    /** HTTP/2: Size in bytes of the largest frame payload the sender will accept. */\n    const val MAX_FRAME_SIZE = 5\n\n    /** HTTP/2: Advisory only. Size in bytes of the largest header list the sender will accept. */\n    const val MAX_HEADER_LIST_SIZE = 6\n\n    /** Total number of settings. */\n    const val COUNT = 10\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/StreamResetException.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.IOException\n\n/** Thrown when an HTTP/2 stream is canceled without damage to the socket that carries it. */\nclass StreamResetException(\n  @JvmField val errorCode: ErrorCode,\n) : IOException(\"stream was reset: $errorCode\")\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/flowcontrol/WindowCounter.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2.flowcontrol\n\nclass WindowCounter(\n  val streamId: Int,\n) {\n  /** The total number of bytes consumed. */\n  var total: Long = 0L\n    private set\n\n  /** The total number of bytes acknowledged by outgoing `WINDOW_UPDATE` frames. */\n  var acknowledged: Long = 0L\n    private set\n\n  val unacknowledged: Long\n    @Synchronized get() = total - acknowledged\n\n  @Synchronized\n  fun update(\n    total: Long = 0,\n    acknowledged: Long = 0,\n  ) {\n    check(total >= 0)\n    check(acknowledged >= 0)\n\n    this.total += total\n    this.acknowledged += acknowledged\n\n    check(this.acknowledged <= this.total)\n  }\n\n  override fun toString(): String =\n    \"WindowCounter(streamId=$streamId, total=$total, acknowledged=$acknowledged, unacknowledged=$unacknowledged)\"\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/idn/IdnaMappingTable.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\nimport okio.BufferedSink\n\n/**\n * An IDNA mapping table optimized for small code and data size.\n *\n * Code Points in Sections\n * =======================\n *\n * The full range of code points is 0..0x10fffe. We can represent any of these code points with 21\n * bits.\n *\n * We split each code point into a 14-bit prefix and a 7-bit suffix. All code points with the same\n * prefix are called a 'section'. There are 128 code points per section.\n *\n * Ranges Data (32,612 bytes)\n * ==========================\n *\n * Each entry is 4 bytes, and represents a _range_ of code points that all share a common 14-bit\n * prefix. Entries are sorted by their complete code points.\n *\n * The 4 bytes are named b0, b1, b2 and b3. We also define these supplemental values:\n *\n *  * **b2a**: b2 + 0x80\n *  * **b3a**: b3 + 0x80\n *  * **b2b3**: (b2 << 7) + b3\n *\n * b0\n * --\n *\n * The inclusive start of the range. We get the first 14 bits of this code point from the section\n * and the last 7 bits from this byte.\n *\n * The end of the range is not encoded, but can be inferred by looking at the start of the range\n * that follows.\n *\n * b1\n * --\n *\n * This is either a mapping decision or the length of the mapped output, according to this table:\n *\n * ```\n *  0..63 : Length of the UTF-16 sequence that this range maps to. The offset is b2b3.\n * 64..79 : Offset by a fixed negative offset. The bottom 4 bits of b1 are the top 4 bits of the offset.\n * 80..95 : Offset by a fixed positive offset. The bottom 4 bits of b1 are the top 4 bits of the offset.\n *    119 : Ignored.\n *    120 : Valid.\n *    121 : Disallowed\n *    122 : Mapped inline to the sequence: [b2].\n *    123 : Mapped inline to the sequence: [b2a].\n *    124 : Mapped inline to the sequence: [b2, b3].\n *    125 : Mapped inline to the sequence: [b2a, b3].\n *    126 : Mapped inline to the sequence: [b2, b3a].\n *    127 : Mapped inline to the sequence: [b2a, b3a].\n *\n * The range goes until the beginning of the next range.\n *\n * When b2 and b3 are unused, their values are set to 0x2d ('-').\n *\n * Section Index (1,240 bytes)\n * ===========================\n *\n * Each entry is 4 bytes, and represents all the code points that share a 14-bit prefix. Entries are\n * sorted by this 14-bit prefix.\n *\n * We define these values:\n *\n *  * **b0b1s7**: (b0 << 14) + (b1 << 7)\n *  * **b2b3s2**: (b2 << 9) + (b3 << 2)\n *\n * b0b1s7 is the section prefix. If a section is omitted, that means its ranges data exactly matches\n * that of the preceding section.\n *\n * b2b3s2 is the offset into the ranges data. It is shifted by 2 because ranges are 4-byte aligned.\n *\n * Mappings Data (4,719 bytes)\n * ===========================\n *\n * This is UTF-8 character data. It is indexed into by b2b3 in the ranges dataset.\n *\n * Mappings may overlap.\n *\n * ASCII-Only\n * ==========\n *\n * Neither the section index nor the ranges data use bit 0x80 anywhere. That means the data is\n * strictly ASCII. This is intended to make it efficient to encode this data as a string, and to\n * index into it as a string.\n *\n * The mappings data contains non-ASCII characters.\n */\ninternal class IdnaMappingTable internal constructor(\n  val sections: String,\n  val ranges: String,\n  val mappings: String,\n) {\n  /**\n   * Returns true if the [codePoint] was applied successfully. Returns false if it was disallowed.\n   */\n  fun map(\n    codePoint: Int,\n    sink: BufferedSink,\n  ): Boolean {\n    val sectionsIndex = findSectionsIndex(codePoint)\n\n    val rangesPosition = sections.read14BitInt(sectionsIndex + 2)\n\n    val rangesLimit =\n      when {\n        sectionsIndex + 4 < sections.length -> sections.read14BitInt(sectionsIndex + 6)\n        else -> ranges.length / 4\n      }\n\n    val rangesIndex = findRangesOffset(codePoint, rangesPosition, rangesLimit)\n\n    when (val b1 = ranges[rangesIndex + 1].code) {\n      in 0..63 -> {\n        // Length of the UTF-16 sequence that this range maps to. The offset is b2b3.\n        val beginIndex = ranges.read14BitInt(rangesIndex + 2)\n        sink.writeUtf8(mappings, beginIndex, beginIndex + b1)\n      }\n\n      in 64..79 -> {\n        // Mapped inline as codePoint delta to subtract\n        val b2 = ranges[rangesIndex + 2].code\n        val b3 = ranges[rangesIndex + 3].code\n\n        val codepointDelta = (b1 and 0xF shl 14) or (b2 shl 7) or b3\n        sink.writeUtf8CodePoint(codePoint - codepointDelta)\n      }\n\n      in 80..95 -> {\n        // Mapped inline as codePoint delta to add\n        val b2 = ranges[rangesIndex + 2].code\n        val b3 = ranges[rangesIndex + 3].code\n\n        val codepointDelta = (b1 and 0xF shl 14) or (b2 shl 7) or b3\n        sink.writeUtf8CodePoint(codePoint + codepointDelta)\n      }\n\n      119 -> {\n        // Ignored.\n      }\n\n      120 -> {\n        // Valid.\n        sink.writeUtf8CodePoint(codePoint)\n      }\n\n      121 -> {\n        // Disallowed.\n        sink.writeUtf8CodePoint(codePoint)\n        return false\n      }\n\n      122 -> {\n        // Mapped inline to the sequence: [b2].\n        sink.writeByte(ranges[rangesIndex + 2].code)\n      }\n\n      123 -> {\n        // Mapped inline to the sequence: [b2a].\n        sink.writeByte(ranges[rangesIndex + 2].code or 0x80)\n      }\n\n      124 -> {\n        // Mapped inline to the sequence: [b2, b3].\n        sink.writeByte(ranges[rangesIndex + 2].code)\n        sink.writeByte(ranges[rangesIndex + 3].code)\n      }\n\n      125 -> {\n        // Mapped inline to the sequence: [b2a, b3].\n        sink.writeByte(ranges[rangesIndex + 2].code or 0x80)\n        sink.writeByte(ranges[rangesIndex + 3].code)\n      }\n\n      126 -> {\n        // Mapped inline to the sequence: [b2, b3a].\n        sink.writeByte(ranges[rangesIndex + 2].code)\n        sink.writeByte(ranges[rangesIndex + 3].code or 0x80)\n      }\n\n      127 -> {\n        // Mapped inline to the sequence: [b2a, b3a].\n        sink.writeByte(ranges[rangesIndex + 2].code or 0x80)\n        sink.writeByte(ranges[rangesIndex + 3].code or 0x80)\n      }\n\n      else -> {\n        error(\"unexpected rangesIndex for $codePoint\")\n      }\n    }\n\n    return true\n  }\n\n  /**\n   * Binary search [sections] for [codePoint], looking at its top 14 bits.\n   *\n   * This binary searches over 4-byte entries, and so it needs to adjust binary search indices\n   * in (by dividing by 4) and out (by multiplying by 4).\n   */\n  private fun findSectionsIndex(codePoint: Int): Int {\n    val target = (codePoint and 0x1fff80) shr 7\n    val offset =\n      binarySearch(\n        position = 0,\n        limit = sections.length / 4,\n      ) { index ->\n        val entryIndex = index * 4\n        val b0b1 = sections.read14BitInt(entryIndex)\n        return@binarySearch target.compareTo(b0b1)\n      }\n\n    return when {\n      offset >= 0 -> offset * 4\n\n      // This section was found by binary search.\n      else -> (-offset - 2) * 4 // Not found? Use the preceding element.\n    }\n  }\n\n  /**\n   * Binary search [ranges] for [codePoint], looking at its bottom 7 bits.\n   *\n   * This binary searches over 4-byte entries, and so it needs to adjust binary search indices\n   * in (by dividing by 4) and out (by multiplying by 4).\n   */\n  private fun findRangesOffset(\n    codePoint: Int,\n    position: Int,\n    limit: Int,\n  ): Int {\n    val target = codePoint and 0x7f\n    val offset =\n      binarySearch(\n        position = position,\n        limit = limit,\n      ) { index ->\n        val entryIndex = index * 4\n        val b0 = ranges[entryIndex].code\n        return@binarySearch target.compareTo(b0)\n      }\n\n    return when {\n      offset >= 0 -> offset * 4\n\n      // This entry was found by binary search.\n      else -> (-offset - 2) * 4 // Not found? Use the preceding element.\n    }\n  }\n}\n\ninternal fun String.read14BitInt(index: Int): Int {\n  val b0 = this[index].code\n  val b1 = this[index + 1].code\n  return (b0 shl 7) + b1\n}\n\n/**\n * An extremely generic binary search that doesn't know what data it's searching over. The caller\n * provides indexes and a comparison function, and this calls that function iteratively.\n *\n * @return the index of the match. If no match is found this is `(-1 - insertionPoint)`, where the\n *     inserting the element at `insertionPoint` will retain sorted order.\n */\ninline fun binarySearch(\n  position: Int,\n  limit: Int,\n  compare: (Int) -> Int,\n): Int {\n  // Do the binary searching bit.\n  var low = position\n  var high = limit - 1\n  while (low <= high) {\n    val mid = (low + high) / 2\n    val compareResult = compare(mid)\n    when {\n      compareResult < 0 -> high = mid - 1\n      compareResult > 0 -> low = mid + 1\n      else -> return mid // Match!\n    }\n  }\n\n  return -low - 1 // insertionPoint is before the first element.\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/idn/Punycode.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\nimport okio.Buffer\nimport okio.ByteString.Companion.encodeUtf8\n\n/**\n * An [RFC 3492] punycode decoder for converting ASCII to Unicode domain name labels. This is\n * intended for use in Internationalized Domain Names (IDNs).\n *\n * This class contains a Kotlin implementation of the pseudocode specified by RFC 3492. It includes\n * direct translation of the pseudocode presented there.\n *\n * Partner this class with [UTS #46] to implement IDNA2008 [RFC 5890] like most browsers do.\n *\n * [RFC 3492]: https://datatracker.ietf.org/doc/html/rfc3492\n * [RFC 5890]: https://datatracker.ietf.org/doc/html/rfc5890\n * [UTS #46]: https://www.unicode.org/reports/tr46/\n */\nobject Punycode {\n  val PREFIX_STRING = \"xn--\"\n  val PREFIX = PREFIX_STRING.encodeUtf8()\n\n  private const val BASE = 36\n  private const val TMIN = 1\n  private const val TMAX = 26\n  private const val SKEW = 38\n  private const val DAMP = 700\n  private const val INITIAL_BIAS = 72\n  private const val INITIAL_N = 0x80\n\n  /**\n   * Returns null if any label is oversized so much that the encoder cannot encode it without\n   * integer overflow. This will not return null for labels that fit within the DNS size\n   * limits.\n   */\n  fun encode(string: String): String? {\n    var pos = 0\n    val limit = string.length\n    val result = Buffer()\n\n    while (pos < limit) {\n      var dot = string.indexOf('.', startIndex = pos)\n      if (dot == -1) dot = limit\n\n      if (!encodeLabel(string, pos, dot, result)) {\n        // If we couldn't encode the label, give up.\n        return null\n      }\n\n      if (dot < limit) {\n        result.writeByte('.'.code)\n        pos = dot + 1\n      } else {\n        break\n      }\n    }\n\n    return result.readUtf8()\n  }\n\n  private fun encodeLabel(\n    string: String,\n    pos: Int,\n    limit: Int,\n    result: Buffer,\n  ): Boolean {\n    if (!string.requiresEncode(pos, limit)) {\n      result.writeUtf8(string, pos, limit)\n      return true\n    }\n\n    result.write(PREFIX)\n\n    val input = string.codePoints(pos, limit)\n\n    // Copy all the basic code points to the output.\n    var b = 0\n    for (codePoint in input) {\n      if (codePoint < INITIAL_N) {\n        result.writeByte(codePoint)\n        b++\n      }\n    }\n\n    // Copy a delimiter if any basic code points were emitted.\n    if (b > 0) result.writeByte('-'.code)\n\n    var n = INITIAL_N\n    var delta = 0\n    var bias = INITIAL_BIAS\n    var h = b\n    while (h < input.size) {\n      val m = input.minBy { if (it >= n) it else Int.MAX_VALUE }\n\n      val increment = (m - n) * (h + 1)\n      if (delta > Int.MAX_VALUE - increment) return false // Prevent overflow.\n      delta += increment\n\n      n = m\n\n      for (c in input) {\n        if (c < n) {\n          if (delta == Int.MAX_VALUE) return false // Prevent overflow.\n          delta++\n        } else if (c == n) {\n          var q = delta\n\n          for (k in BASE until Int.MAX_VALUE step BASE) {\n            val t =\n              when {\n                k <= bias -> TMIN\n                k >= bias + TMAX -> TMAX\n                else -> k - bias\n              }\n            if (q < t) break\n            result.writeByte((t + ((q - t) % (BASE - t))).punycodeDigit)\n            q = (q - t) / (BASE - t)\n          }\n\n          result.writeByte(q.punycodeDigit)\n          bias = adapt(delta, h + 1, h == b)\n          delta = 0\n          h++\n        }\n      }\n      delta++\n      n++\n    }\n\n    return true\n  }\n\n  /**\n   * Converts a punycode-encoded domain name with `.`-separated labels into a human-readable\n   * Internationalized Domain Name.\n   */\n  fun decode(string: String): String? {\n    var pos = 0\n    val limit = string.length\n    val result = Buffer()\n\n    while (pos < limit) {\n      var dot = string.indexOf('.', startIndex = pos)\n      if (dot == -1) dot = limit\n\n      if (!decodeLabel(string, pos, dot, result)) return null\n\n      if (dot < limit) {\n        result.writeByte('.'.code)\n        pos = dot + 1\n      } else {\n        break\n      }\n    }\n\n    return result.readUtf8()\n  }\n\n  /**\n   * Converts a single label from Punycode to Unicode.\n   *\n   * @return true if the range of [string] from [pos] to [limit] was valid and decoded successfully.\n   *     Otherwise, the decode failed.\n   */\n  private fun decodeLabel(\n    string: String,\n    pos: Int,\n    limit: Int,\n    result: Buffer,\n  ): Boolean {\n    if (!string.regionMatches(pos, PREFIX_STRING, 0, 4, ignoreCase = true)) {\n      result.writeUtf8(string, pos, limit)\n      return true\n    }\n\n    var pos = pos + 4 // 'xn--'.size.\n\n    // We'd prefer to operate directly on `result` but it doesn't offer insertCodePoint(), only\n    // appendCodePoint(). The Punycode algorithm processes code points in increasing code-point\n    // order, not in increasing index order.\n    val codePoints = mutableListOf<Int>()\n\n    // consume all code points before the last delimiter (if there is one)\n    //  and copy them to output, fail on any non-basic code point\n    val lastDelimiter = string.lastIndexOf('-', limit)\n    if (lastDelimiter >= pos) {\n      while (pos < lastDelimiter) {\n        when (val codePoint = string[pos++]) {\n          in 'a'..'z', in 'A'..'Z', in '0'..'9', '-' -> {\n            codePoints += codePoint.code\n          }\n\n          else -> {\n            return false\n          } // Malformed.\n        }\n      }\n      pos++ // Consume '-'.\n    }\n\n    var n = INITIAL_N\n    var i = 0\n    var bias = INITIAL_BIAS\n\n    while (pos < limit) {\n      val oldi = i\n      var w = 1\n      for (k in BASE until Int.MAX_VALUE step BASE) {\n        if (pos == limit) return false // Malformed.\n        val c = string[pos++]\n        val digit =\n          when (c) {\n            in 'a'..'z' -> c - 'a'\n            in 'A'..'Z' -> c - 'A'\n            in '0'..'9' -> c - '0' + 26\n            else -> return false // Malformed.\n          }\n        val deltaI = digit * w\n        if (i > Int.MAX_VALUE - deltaI) return false // Prevent overflow.\n        i += deltaI\n        val t =\n          when {\n            k <= bias -> TMIN\n            k >= bias + TMAX -> TMAX\n            else -> k - bias\n          }\n        if (digit < t) break\n        val scaleW = BASE - t\n        if (w > Int.MAX_VALUE / scaleW) return false // Prevent overflow.\n        w *= scaleW\n      }\n      bias = adapt(i - oldi, codePoints.size + 1, oldi == 0)\n      val deltaN = i / (codePoints.size + 1)\n      if (n > Int.MAX_VALUE - deltaN) return false // Prevent overflow.\n      n += deltaN\n      i %= (codePoints.size + 1)\n\n      if (n > 0x10ffff) return false // Not a valid code point.\n\n      codePoints.add(i, n)\n\n      i++\n    }\n\n    for (codePoint in codePoints) {\n      result.writeUtf8CodePoint(codePoint)\n    }\n\n    return true\n  }\n\n  /** Returns a new bias. */\n  private fun adapt(\n    delta: Int,\n    numpoints: Int,\n    first: Boolean,\n  ): Int {\n    var delta =\n      when {\n        first -> delta / DAMP\n        else -> delta / 2\n      }\n    delta += (delta / numpoints)\n    var k = 0\n    while (delta > ((BASE - TMIN) * TMAX) / 2) {\n      delta /= (BASE - TMIN)\n      k += BASE\n    }\n    return k + (((BASE - TMIN + 1) * delta) / (delta + SKEW))\n  }\n\n  private fun String.requiresEncode(\n    pos: Int,\n    limit: Int,\n  ): Boolean {\n    for (i in pos until limit) {\n      if (this[i].code >= INITIAL_N) return true\n    }\n    return false\n  }\n\n  private fun String.codePoints(\n    pos: Int,\n    limit: Int,\n  ): List<Int> {\n    val result = mutableListOf<Int>()\n    var i = pos\n    while (i < limit) {\n      val c = this[i]\n      result +=\n        when {\n          c.isSurrogate() -> {\n            val low = (if (i + 1 < limit) this[i + 1] else '\\u0000')\n            if (c.isLowSurrogate() || !low.isLowSurrogate()) {\n              '?'.code\n            } else {\n              i++\n              0x010000 + (c.code and 0x03ff shl 10 or (low.code and 0x03ff))\n            }\n          }\n\n          else -> {\n            c.code\n          }\n        }\n      i++\n    }\n    return result\n  }\n\n  private val Int.punycodeDigit: Int\n    get() =\n      when {\n        this < 26 -> this + 'a'.code\n        this < 36 -> (this - 26) + '0'.code\n        else -> error(\"unexpected digit: $this\")\n      }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/internal.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:JvmName(\"Internal\")\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal\n\n// Exposes Kotlin-internal APIs to Java test code and code in other modules.\n\nimport java.nio.charset.Charset\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Cache\nimport okhttp3.CipherSuite\nimport okhttp3.ConnectionPool\nimport okhttp3.ConnectionSpec\nimport okhttp3.Cookie\nimport okhttp3.Headers\nimport okhttp3.HttpUrl\nimport okhttp3.MediaType\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.connection.ConnectionListener\nimport okhttp3.internal.connection.RealConnection\n\n// Exposes Kotlin-internal APIs to Java test code and code in other modules.\n\ninternal fun parseCookie(\n  currentTimeMillis: Long,\n  url: HttpUrl,\n  setCookie: String,\n): Cookie? = Cookie.parse(currentTimeMillis, url, setCookie)\n\ninternal fun cookieToString(\n  cookie: Cookie,\n  forObsoleteRfc2965: Boolean,\n): String = cookie.toString(forObsoleteRfc2965)\n\ninternal fun addHeaderLenient(\n  builder: Headers.Builder,\n  line: String,\n): Headers.Builder = builder.addLenient(line)\n\ninternal fun addHeaderLenient(\n  builder: Headers.Builder,\n  name: String,\n  value: String,\n): Headers.Builder = builder.addLenient(name, value)\n\ninternal fun cacheGet(\n  cache: Cache,\n  request: Request,\n): Response? = cache.get(request)\n\ninternal fun applyConnectionSpec(\n  connectionSpec: ConnectionSpec,\n  sslSocket: SSLSocket,\n  isFallback: Boolean,\n) = connectionSpec.apply(sslSocket, isFallback)\n\ninternal fun ConnectionSpec.effectiveCipherSuites(socketEnabledCipherSuites: Array<String>): Array<String> =\n  if (cipherSuitesAsString != null) {\n    // 3 options here for ordering\n    // 1) Legacy Platform - based on the Platform/Provider existing ordering in\n    // sslSocket.enabledCipherSuites\n    // 2) OkHttp Client - based on MODERN_TLS source code ordering\n    // 3) Caller specified but assuming the visible defaults in MODERN_CIPHER_SUITES are rejigged\n    // to match legacy i.e. the platform/provider\n    //\n    // Opting for 2 here and keeping MODERN_TLS in line with secure browsers.\n    cipherSuitesAsString.intersect(socketEnabledCipherSuites, CipherSuite.ORDER_BY_NAME)\n  } else {\n    socketEnabledCipherSuites\n  }\n\ninternal fun MediaType?.chooseCharset(): Pair<Charset, MediaType?> {\n  var charset: Charset = Charsets.UTF_8\n  var finalContentType: MediaType? = this\n  if (this != null) {\n    val resolvedCharset = this.charset()\n    if (resolvedCharset == null) {\n      charset = Charsets.UTF_8\n      finalContentType = \"$this; charset=utf-8\".toMediaTypeOrNull()\n    } else {\n      charset = resolvedCharset\n    }\n  }\n  return charset to finalContentType\n}\n\ninternal fun MediaType?.charsetOrUtf8(): Charset = this?.charset() ?: Charsets.UTF_8\n\ninternal val Response.connection: RealConnection\n  get() = this.exchange!!.connection\n\ninternal fun OkHttpClient.Builder.taskRunnerInternal(taskRunner: TaskRunner) = this.taskRunner(taskRunner)\n\ninternal fun buildConnectionPool(\n  connectionListener: ConnectionListener,\n  taskRunner: TaskRunner,\n): ConnectionPool =\n  ConnectionPool(\n    connectionListener = connectionListener,\n    taskRunner = taskRunner,\n  )\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/platform/Jdk9Platform.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport java.security.NoSuchAlgorithmException\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Protocol\nimport okhttp3.internal.SuppressSignatureCheck\n\n/**\n * OpenJDK 9+ and JDK8 build 252+.\n *\n * This may also be used for Android tests with Robolectric.\n */\n@Suppress(\"NewApi\")\nopen class Jdk9Platform : Platform() {\n  @SuppressSignatureCheck\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<@JvmSuppressWildcards Protocol>,\n  ) {\n    val sslParameters = sslSocket.sslParameters\n\n    val names = alpnProtocolNames(protocols)\n\n    sslParameters.applicationProtocols = names.toTypedArray()\n\n    sslSocket.sslParameters = sslParameters\n  }\n\n  @SuppressSignatureCheck\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? =\n    try {\n      // SSLSocket.getApplicationProtocol returns \"\" if application protocols values will not\n      // be used. Observed if you didn't specify SSLParameters.setApplicationProtocols\n      when (val protocol = sslSocket.applicationProtocol) {\n        null, \"\" -> null\n        else -> protocol\n      }\n    } catch (e: UnsupportedOperationException) {\n      // https://docs.oracle.com/javase/9/docs/api/javax/net/ssl/SSLSocket.html#getApplicationProtocol--\n      null\n    }\n\n  override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? {\n    // Not supported due to access checks on JDK 9+:\n    // java.lang.reflect.InaccessibleObjectException: Unable to make member of class\n    // sun.security.ssl.SSLSocketFactoryImpl accessible:  module java.base does not export\n    // sun.security.ssl to unnamed module @xxx\n    throw UnsupportedOperationException(\n      \"clientBuilder.sslSocketFactory(SSLSocketFactory) not supported on JDK 8 (>= 252) or JDK 9+\",\n    )\n  }\n\n  override fun newSSLContext(): SSLContext =\n    when {\n      majorVersion != null && majorVersion >= 9 -> {\n        SSLContext.getInstance(\"TLS\")\n      }\n\n      else -> {\n        try {\n          // Based on SSLSocket.getApplicationProtocol check we should\n          // have TLSv1.3 if we request it.\n          // See https://www.oracle.com/java/technologies/javase/8u261-relnotes.html\n          SSLContext.getInstance(\"TLSv1.3\")\n        } catch (nsae: NoSuchAlgorithmException) {\n          SSLContext.getInstance(\"TLS\")\n        }\n      }\n    }\n\n  companion object {\n    val isAvailable: Boolean\n\n    val majorVersion = System.getProperty(\"java.specification.version\")?.toIntOrNull()\n\n    init {\n      isAvailable =\n        if (majorVersion != null) {\n          majorVersion >= 9\n        } else {\n          try {\n            // also present on JDK8 after build 252.\n            SSLSocket::class.java.getMethod(\"getApplicationProtocol\")\n            true\n          } catch (nsme: NoSuchMethodException) {\n            false\n          }\n        }\n    }\n\n    fun buildIfSupported(): Jdk9Platform? = if (isAvailable) Jdk9Platform() else null\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/platform/Platform.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport java.io.IOException\nimport java.net.InetSocketAddress\nimport java.net.Socket\nimport java.security.GeneralSecurityException\nimport java.security.KeyStore\nimport java.util.logging.Level\nimport java.util.logging.Logger\nimport javax.net.ssl.ExtendedSSLSession\nimport javax.net.ssl.SNIHostName\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManager\nimport javax.net.ssl.TrustManagerFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.internal.publicsuffix.PublicSuffixDatabase\nimport okhttp3.internal.readFieldOrNull\nimport okhttp3.internal.tls.BasicCertificateChainCleaner\nimport okhttp3.internal.tls.BasicTrustRootIndex\nimport okhttp3.internal.tls.CertificateChainCleaner\nimport okhttp3.internal.tls.TrustRootIndex\nimport okio.Buffer\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n\n/**\n * Access to platform-specific features.\n *\n * ### Session Tickets\n *\n * Supported on Android 2.3+.\n * Supported on JDK 8+ via Conscrypt.\n *\n * ### ALPN (Application Layer Protocol Negotiation)\n *\n * Supported on Android 5.0+.\n *\n * Supported on OpenJDK 8 via the JettyALPN-boot library or Conscrypt.\n *\n * Supported on OpenJDK 9+ via SSLParameters and SSLSocket features.\n *\n * ### Trust Manager Extraction\n *\n * Supported on Android 2.3+ and OpenJDK 7+. There are no public APIs to recover the trust\n * manager that was used to create an [SSLSocketFactory].\n *\n * Not supported by choice on JDK9+ due to access checks.\n *\n * ### Android Cleartext Permit Detection\n *\n * Supported on Android 6.0+ via `NetworkSecurityPolicy`.\n */\nopen class Platform {\n  /** Prefix used on custom headers. */\n  fun getPrefix() = \"OkHttp\"\n\n  open fun newSSLContext(): SSLContext = SSLContext.getInstance(\"TLS\")\n\n  open fun platformTrustManager(): X509TrustManager {\n    val factory =\n      TrustManagerFactory.getInstance(\n        TrustManagerFactory.getDefaultAlgorithm(),\n      )\n    factory.init(null as KeyStore?)\n    val trustManagers = factory.trustManagers!!\n    check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) {\n      \"Unexpected default trust managers: ${trustManagers.contentToString()}\"\n    }\n    return trustManagers[0] as X509TrustManager\n  }\n\n  open fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? {\n    return try {\n      // Attempt to get the trust manager from an OpenJDK socket factory. We attempt this on all\n      // platforms in order to support Robolectric, which mixes classes from both Android and the\n      // Oracle JDK. Note that we don't support HTTP/2 or other nice features on Robolectric.\n      val sslContextClass = Class.forName(\"sun.security.ssl.SSLContextImpl\")\n      val context = readFieldOrNull(sslSocketFactory, sslContextClass, \"context\") ?: return null\n      readFieldOrNull(context, X509TrustManager::class.java, \"trustManager\")\n    } catch (e: ClassNotFoundException) {\n      null\n    } catch (e: RuntimeException) {\n      // Throws InaccessibleObjectException (added in JDK9) on JDK 17 due to\n      // JEP 403 Strongly Encapsulate JDK Internals.\n      if (e.javaClass.name != \"java.lang.reflect.InaccessibleObjectException\") {\n        throw e\n      }\n\n      null\n    }\n  }\n\n  /**\n   * Configure TLS extensions on `sslSocket` for `route`.\n   */\n  open fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<@JvmSuppressWildcards Protocol>,\n  ) {\n  }\n\n  /** Called after the TLS handshake to release resources allocated by [configureTlsExtensions]. */\n  open fun afterHandshake(sslSocket: SSLSocket) {\n  }\n\n  /** Returns the negotiated protocol, or null if no protocol was negotiated. */\n  open fun getSelectedProtocol(sslSocket: SSLSocket): String? = null\n\n  /** For MockWebServer. This returns the inbound SNI names. */\n  @Suppress(\"NewApi\")\n  @IgnoreJRERequirement // This function is overridden to require API >= 24.\n  open fun getHandshakeServerNames(sslSocket: SSLSocket): List<String> {\n    val session = sslSocket.session as? ExtendedSSLSession ?: return listOf()\n    return try {\n      session.requestedServerNames.mapNotNull { (it as? SNIHostName)?.asciiName }\n    } catch (_: UnsupportedOperationException) {\n      // UnsupportedOperationException – if the underlying provider does not implement the operation\n      // https://github.com/bcgit/bc-java/issues/1773\n      listOf()\n    }\n  }\n\n  @Throws(IOException::class)\n  open fun connectSocket(\n    socket: Socket,\n    address: InetSocketAddress,\n    connectTimeout: Int,\n  ) {\n    socket.connect(address, connectTimeout)\n  }\n\n  open fun log(\n    message: String,\n    level: Int = INFO,\n    t: Throwable? = null,\n  ) {\n    val logLevel = if (level == WARN) Level.WARNING else Level.INFO\n    logger.log(logLevel, message, t)\n  }\n\n  open fun isCleartextTrafficPermitted(hostname: String): Boolean = true\n\n  /**\n   * Returns an object that holds a stack trace created at the moment this method is executed. This\n   * should be used specifically for [java.io.Closeable] objects and in conjunction with\n   * [logCloseableLeak].\n   */\n  open fun getStackTraceForCloseable(closer: String): Any? =\n    when {\n      logger.isLoggable(Level.FINE) -> Throwable(closer)\n\n      // These are expensive to allocate.\n      else -> null\n    }\n\n  open fun logCloseableLeak(\n    message: String,\n    stackTrace: Any?,\n  ) {\n    var logMessage = message\n    if (stackTrace == null) {\n      logMessage += \" To see where this was allocated, set the OkHttpClient logger level to \" +\n        \"FINE: Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE);\"\n    }\n    log(logMessage, WARN, stackTrace as Throwable?)\n  }\n\n  open fun buildCertificateChainCleaner(trustManager: X509TrustManager): CertificateChainCleaner =\n    BasicCertificateChainCleaner(buildTrustRootIndex(trustManager))\n\n  open fun buildTrustRootIndex(trustManager: X509TrustManager): TrustRootIndex = BasicTrustRootIndex(*trustManager.acceptedIssuers)\n\n  open fun newSslSocketFactory(trustManager: X509TrustManager): SSLSocketFactory {\n    try {\n      return newSSLContext()\n        .apply {\n          init(null, arrayOf<TrustManager>(trustManager), null)\n        }.socketFactory\n    } catch (e: GeneralSecurityException) {\n      throw AssertionError(\"No System TLS: $e\", e) // The system has no TLS. Just give up.\n    }\n  }\n\n  override fun toString(): String = javaClass.simpleName\n\n  companion object {\n    @Volatile private var platform = findPlatform()\n\n    const val INFO = 4\n    const val WARN = 5\n\n    private val logger = Logger.getLogger(OkHttpClient::class.java.name)\n\n    @JvmStatic\n    fun get(): Platform = platform\n\n    fun resetForTests(platform: Platform = findPlatform()) {\n      this.platform = platform\n      PublicSuffixDatabase.resetForTests()\n    }\n\n    fun alpnProtocolNames(protocols: List<Protocol>) = protocols.filter { it != Protocol.HTTP_1_0 }.map { it.toString() }\n\n    val isAndroid: Boolean\n      get() = PlatformRegistry.isAndroid\n\n    /** Attempt to match the host runtime to a capable Platform implementation. */\n    private fun findPlatform(): Platform = PlatformRegistry.findPlatform()\n\n    /**\n     * Returns the concatenation of 8-bit, length prefixed protocol names.\n     * http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4\n     */\n    fun concatLengthPrefixed(protocols: List<Protocol>): ByteArray {\n      val result = Buffer()\n      for (protocol in alpnProtocolNames(protocols)) {\n        result.writeByte(protocol.length)\n        result.writeUtf8(protocol)\n      }\n      return result.readByteArray()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/platform/PlatformRegistry.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nexpect object PlatformRegistry {\n  fun findPlatform(): Platform\n\n  val isAndroid: Boolean\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/proxy/NullProxySelector.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.proxy\n\nimport java.io.IOException\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.SocketAddress\nimport java.net.URI\n\n/**\n * A proxy selector that always returns the [Proxy.NO_PROXY].\n */\nobject NullProxySelector : ProxySelector() {\n  override fun select(uri: URI?): List<Proxy> {\n    requireNotNull(uri) { \"uri must not be null\" }\n    return listOf(Proxy.NO_PROXY)\n  }\n\n  override fun connectFailed(\n    uri: URI?,\n    sa: SocketAddress?,\n    ioe: IOException?,\n  ) {\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/publicsuffix/BasePublicSuffixList.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.atomic.AtomicBoolean\nimport okio.ByteString\nimport okio.Source\nimport okio.buffer\n\ninternal abstract class BasePublicSuffixList : PublicSuffixList {\n  /** True after we've attempted to read the list for the first time. */\n  private val listRead = AtomicBoolean(false)\n\n  /** Used for concurrent threads reading the list for the first time. */\n  private val readCompleteLatch = CountDownLatch(1)\n\n  // The lists are held as a large array of UTF-8 bytes. This is to avoid allocating lots of strings\n  // that will likely never be used. Each rule is separated by '\\n'. Please see the\n  // PublicSuffixListGenerator class for how these lists are generated.\n  // Guarded by this.\n  override lateinit var bytes: ByteString\n  override lateinit var exceptionBytes: ByteString\n\n  private var readFailure: IOException? = null\n\n  @Throws(IOException::class)\n  private fun readTheList() {\n    var publicSuffixListBytes: ByteString?\n    var publicSuffixExceptionListBytes: ByteString?\n\n    try {\n      listSource().buffer().use { bufferedSource ->\n        val totalBytes = bufferedSource.readInt()\n        publicSuffixListBytes = bufferedSource.readByteString(totalBytes.toLong())\n\n        val totalExceptionBytes = bufferedSource.readInt()\n        publicSuffixExceptionListBytes = bufferedSource.readByteString(totalExceptionBytes.toLong())\n      }\n\n      synchronized(this) {\n        this.bytes = publicSuffixListBytes!!\n        this.exceptionBytes = publicSuffixExceptionListBytes!!\n      }\n    } finally {\n      readCompleteLatch.countDown()\n    }\n  }\n\n  abstract fun listSource(): Source\n\n  override fun ensureLoaded() {\n    if (!listRead.get() && listRead.compareAndSet(false, true)) {\n      readTheListUninterruptibly()\n    } else {\n      try {\n        readCompleteLatch.await()\n      } catch (_: InterruptedException) {\n        Thread.currentThread().interrupt() // Retain interrupted status.\n      }\n    }\n\n    if (!::bytes.isInitialized) {\n      // May have failed with an IOException\n      throw IllegalStateException(\"Unable to load $path resource.\").apply {\n        initCause(readFailure)\n      }\n    }\n  }\n\n  abstract val path: Any\n\n  /**\n   * Reads the public suffix list treating the operation as uninterruptible. We always want to read\n   * the list otherwise we'll be left in a bad state. If the thread was interrupted prior to this\n   * operation, it will be re-interrupted after the list is read.\n   */\n  private fun readTheListUninterruptibly() {\n    var interrupted = false\n    try {\n      while (true) {\n        try {\n          readTheList()\n          return\n        } catch (_: InterruptedIOException) {\n          Thread.interrupted() // Temporarily clear the interrupted state.\n          interrupted = true\n        } catch (e: IOException) {\n          readFailure = e\n          return\n        }\n      }\n    } finally {\n      if (interrupted) {\n        Thread.currentThread().interrupt() // Retain interrupted status.\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/publicsuffix/PublicSuffixDatabase.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport java.net.IDN\nimport okhttp3.internal.and\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\n\n/**\n * A database of public suffixes provided by [publicsuffix.org][publicsuffix_org].\n *\n * [publicsuffix_org]: https://publicsuffix.org/\n */\nclass PublicSuffixDatabase internal constructor(\n  private val publicSuffixList: PublicSuffixList,\n) {\n  /**\n   * Returns the effective top-level domain plus one (eTLD+1) by referencing the public suffix list.\n   * Returns null if the domain is a public suffix or a private address.\n   *\n   * Here are some examples:\n   *\n   * ```java\n   * assertEquals(\"google.com\", getEffectiveTldPlusOne(\"google.com\"));\n   * assertEquals(\"google.com\", getEffectiveTldPlusOne(\"www.google.com\"));\n   * assertNull(getEffectiveTldPlusOne(\"com\"));\n   * assertNull(getEffectiveTldPlusOne(\"localhost\"));\n   * assertNull(getEffectiveTldPlusOne(\"mymacbook\"));\n   * ```\n   *\n   * @param domain A canonicalized domain. An International Domain Name (IDN) should be punycode\n   *     encoded.\n   */\n  fun getEffectiveTldPlusOne(domain: String): String? {\n    // We use UTF-8 in the list so we need to convert to Unicode.\n    val unicodeDomain = IDN.toUnicode(domain)\n    val domainLabels = splitDomain(unicodeDomain)\n\n    val rule = findMatchingRule(domainLabels)\n    if (domainLabels.size == rule.size && rule[0][0] != EXCEPTION_MARKER) {\n      return null // The domain is a public suffix.\n    }\n\n    val firstLabelOffset =\n      if (rule[0][0] == EXCEPTION_MARKER) {\n        // Exception rules hold the effective TLD plus one.\n        domainLabels.size - rule.size\n      } else {\n        // Otherwise the rule is for a public suffix, so we must take one more label.\n        domainLabels.size - (rule.size + 1)\n      }\n\n    return splitDomain(domain).asSequence().drop(firstLabelOffset).joinToString(\".\")\n  }\n\n  private fun splitDomain(domain: String): List<String> {\n    val domainLabels = domain.split('.')\n\n    if (domainLabels.last() == \"\") {\n      // allow for domain name trailing dot\n      return domainLabels.dropLast(1)\n    }\n\n    return domainLabels\n  }\n\n  private fun findMatchingRule(domainLabels: List<String>): List<String> {\n    publicSuffixList.ensureLoaded()\n\n    // Break apart the domain into UTF-8 labels, i.e. foo.bar.com turns into [foo, bar, com].\n    val domainLabelsUtf8Bytes = Array(domainLabels.size) { i -> domainLabels[i].encodeUtf8() }\n\n    // Start by looking for exact matches. We start at the leftmost label. For example, foo.bar.com\n    // will look like: [foo, bar, com], [bar, com], [com]. The longest matching rule wins.\n    var exactMatch: String? = null\n    for (i in domainLabelsUtf8Bytes.indices) {\n      val rule = publicSuffixList.bytes.binarySearch(domainLabelsUtf8Bytes, i)\n      if (rule != null) {\n        exactMatch = rule\n        break\n      }\n    }\n\n    // In theory, wildcard rules are not restricted to having the wildcard in the leftmost position.\n    // In practice, wildcards are always in the leftmost position. For now, this implementation\n    // cheats and does not attempt every possible permutation. Instead, it only considers wildcards\n    // in the leftmost position. We assert this fact when we generate the public suffix file. If\n    // this assertion ever fails we'll need to refactor this implementation.\n    var wildcardMatch: String? = null\n    if (domainLabelsUtf8Bytes.size > 1) {\n      val labelsWithWildcard = domainLabelsUtf8Bytes.clone()\n      for (labelIndex in 0 until labelsWithWildcard.size - 1) {\n        labelsWithWildcard[labelIndex] = WILDCARD_LABEL\n        val rule = publicSuffixList.bytes.binarySearch(labelsWithWildcard, labelIndex)\n        if (rule != null) {\n          wildcardMatch = rule\n          break\n        }\n      }\n    }\n\n    // Exception rules only apply to wildcard rules, so only try it if we matched a wildcard.\n    var exception: String? = null\n    if (wildcardMatch != null) {\n      for (labelIndex in 0 until domainLabelsUtf8Bytes.size - 1) {\n        val rule =\n          publicSuffixList.exceptionBytes.binarySearch(\n            domainLabelsUtf8Bytes,\n            labelIndex,\n          )\n        if (rule != null) {\n          exception = rule\n          break\n        }\n      }\n    }\n\n    if (exception != null) {\n      // Signal we've identified an exception rule.\n      exception = \"!$exception\"\n      return exception.split('.')\n    } else if (exactMatch == null && wildcardMatch == null) {\n      return PREVAILING_RULE\n    }\n\n    val exactRuleLabels = exactMatch?.split('.') ?: listOf()\n    val wildcardRuleLabels = wildcardMatch?.split('.') ?: listOf()\n\n    return if (exactRuleLabels.size > wildcardRuleLabels.size) {\n      exactRuleLabels\n    } else {\n      wildcardRuleLabels\n    }\n  }\n\n  companion object {\n    private val WILDCARD_LABEL = ByteString.of('*'.code.toByte())\n    private val PREVAILING_RULE = listOf(\"*\")\n\n    private const val EXCEPTION_MARKER = '!'\n\n    private var instance = PublicSuffixDatabase(PublicSuffixList.Default)\n\n    fun get(): PublicSuffixDatabase = instance\n\n    private fun ByteString.binarySearch(\n      labels: Array<ByteString>,\n      labelIndex: Int,\n    ): String? {\n      var low = 0\n      var high = size\n      var match: String? = null\n      while (low < high) {\n        var mid = (low + high) / 2\n        // Search for a '\\n' that marks the start of a value. Don't go back past the start of the\n        // array.\n        while (mid > -1 && this[mid] != '\\n'.code.toByte()) {\n          mid--\n        }\n        mid++\n\n        // Now look for the ending '\\n'.\n        var end = 1\n        while (this[mid + end] != '\\n'.code.toByte()) {\n          end++\n        }\n        val publicSuffixLength = mid + end - mid\n\n        // Compare the bytes. Note that the file stores UTF-8 encoded bytes, so we must compare the\n        // unsigned bytes.\n        var compareResult: Int\n        var currentLabelIndex = labelIndex\n        var currentLabelByteIndex = 0\n        var publicSuffixByteIndex = 0\n\n        var expectDot = false\n        while (true) {\n          val byte0: Int\n          if (expectDot) {\n            byte0 = '.'.code\n            expectDot = false\n          } else {\n            byte0 = labels[currentLabelIndex][currentLabelByteIndex] and 0xff\n          }\n\n          val byte1 = this[mid + publicSuffixByteIndex] and 0xff\n\n          compareResult = byte0 - byte1\n          if (compareResult != 0) break\n\n          publicSuffixByteIndex++\n          currentLabelByteIndex++\n          if (publicSuffixByteIndex == publicSuffixLength) break\n\n          if (labels[currentLabelIndex].size == currentLabelByteIndex) {\n            // We've exhausted our current label. Either there are more labels to compare, in which\n            // case we expect a dot as the next character. Otherwise, we've checked all our labels.\n            if (currentLabelIndex == labels.size - 1) {\n              break\n            } else {\n              currentLabelIndex++\n              currentLabelByteIndex = -1\n              expectDot = true\n            }\n          }\n        }\n\n        if (compareResult < 0) {\n          high = mid - 1\n        } else if (compareResult > 0) {\n          low = mid + end + 1\n        } else {\n          // We found a match, but are the lengths equal?\n          val publicSuffixBytesLeft = publicSuffixLength - publicSuffixByteIndex\n          var labelBytesLeft = labels[currentLabelIndex].size - currentLabelByteIndex\n          for (i in currentLabelIndex + 1 until labels.size) {\n            labelBytesLeft += labels[i].size\n          }\n\n          if (labelBytesLeft < publicSuffixBytesLeft) {\n            high = mid - 1\n          } else if (labelBytesLeft > publicSuffixBytesLeft) {\n            low = mid + end + 1\n          } else {\n            // Found a match.\n            match = this.substring(mid, mid + publicSuffixLength).string(Charsets.UTF_8)\n            break\n          }\n        }\n      }\n      return match\n    }\n\n    internal fun resetForTests() {\n      instance = PublicSuffixDatabase(PublicSuffixList.Default)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/publicsuffix/PublicSuffixList.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport okio.ByteString\n\n/**\n * Basic I/O for `PublicSuffixDatabase.list`\n */\ninternal interface PublicSuffixList {\n  fun ensureLoaded()\n\n  val bytes: ByteString\n  val exceptionBytes: ByteString\n\n  companion object\n}\n\ninternal expect val PublicSuffixList.Companion.Default: PublicSuffixList\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/publicsuffix/ResourcePublicSuffixList.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport okio.FileSystem\nimport okio.Path\nimport okio.Path.Companion.toPath\nimport okio.Source\n\ninternal class ResourcePublicSuffixList(\n  override val path: Path = PUBLIC_SUFFIX_RESOURCE,\n  val fileSystem: FileSystem = FileSystem.Companion.RESOURCES,\n) : BasePublicSuffixList() {\n  override fun listSource(): Source = fileSystem.source(path)\n\n  companion object {\n    @JvmField\n    val PUBLIC_SUFFIX_RESOURCE =\n      \"okhttp3/internal/publicsuffix/${PublicSuffixDatabase::class.java.simpleName}.list\".toPath()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/BasicCertificateChainCleaner.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.tls\n\nimport java.security.GeneralSecurityException\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\nimport java.util.ArrayDeque\nimport java.util.Deque\nimport javax.net.ssl.SSLPeerUnverifiedException\n\n/**\n * A certificate chain cleaner that uses a set of trusted root certificates to build the trusted\n * chain. This class duplicates the clean chain building performed during the TLS handshake. We\n * prefer other mechanisms where they exist, such as with\n * [okhttp3.internal.platform.AndroidPlatform.AndroidCertificateChainCleaner].\n *\n * This class includes code from [Conscrypt's][Conscrypt] [TrustManagerImpl] and\n * [TrustedCertificateIndex].\n *\n * [Conscrypt]: https://conscrypt.org/\n */\nclass BasicCertificateChainCleaner(\n  private val trustRootIndex: TrustRootIndex,\n) : CertificateChainCleaner() {\n  /**\n   * Returns a cleaned chain for [chain].\n   *\n   * This method throws if the complete chain to a trusted CA certificate cannot be constructed.\n   * This is unexpected unless the trust root index in this class has a different trust manager than\n   * what was used to establish [chain].\n   */\n  @Throws(SSLPeerUnverifiedException::class)\n  override fun clean(\n    chain: List<Certificate>,\n    hostname: String,\n  ): List<Certificate> {\n    val queue: Deque<Certificate> = ArrayDeque(chain)\n    val result = mutableListOf<Certificate>()\n    result.add(queue.removeFirst())\n    var foundTrustedCertificate = false\n\n    followIssuerChain@\n    for (c in 0 until MAX_SIGNERS) {\n      val toVerify = result[result.size - 1] as X509Certificate\n\n      // If this cert has been signed by a trusted cert, use that. Add the trusted certificate to\n      // the end of the chain unless it's already present. (That would happen if the first\n      // certificate in the chain is itself a self-signed and trusted CA certificate.)\n      val trustedCert = trustRootIndex.findByIssuerAndSignature(toVerify)\n      if (trustedCert != null) {\n        if (result.size > 1 || toVerify != trustedCert) {\n          result.add(trustedCert)\n        }\n        if (verifySignature(trustedCert, trustedCert, result.size - 2)) {\n          return result // The self-signed cert is a root CA. We're done.\n        }\n        foundTrustedCertificate = true\n        continue\n      }\n\n      // Search for the certificate in the chain that signed this certificate. This is typically\n      // the next element in the chain, but it could be any element.\n      val i = queue.iterator()\n      while (i.hasNext()) {\n        val signingCert = i.next() as X509Certificate\n        if (verifySignature(toVerify, signingCert, result.size - 1)) {\n          i.remove()\n          result.add(signingCert)\n          continue@followIssuerChain\n        }\n      }\n\n      // We've reached the end of the chain. If any cert in the chain is trusted, we're done.\n      if (foundTrustedCertificate) {\n        return result\n      }\n\n      // The last link isn't trusted. Fail.\n      throw SSLPeerUnverifiedException(\n        \"Failed to find a trusted cert that signed $toVerify\",\n      )\n    }\n\n    throw SSLPeerUnverifiedException(\"Certificate chain too long: $result\")\n  }\n\n  /**\n   * Returns true if [toVerify] was signed by [signingCert]'s public key.\n   *\n   * @param minIntermediates the minimum number of intermediate certificates in [signingCert]. This\n   *     is -1 if signing cert is a lone self-signed certificate.\n   */\n  private fun verifySignature(\n    toVerify: X509Certificate,\n    signingCert: X509Certificate,\n    minIntermediates: Int,\n  ): Boolean {\n    if (toVerify.issuerDN != signingCert.subjectDN) {\n      return false\n    }\n    if (signingCert.basicConstraints < minIntermediates) {\n      return false // The signer can't have this many intermediates beneath it.\n    }\n    return try {\n      toVerify.verify(signingCert.publicKey)\n      true\n    } catch (verifyFailed: GeneralSecurityException) {\n      false\n    }\n  }\n\n  override fun hashCode(): Int = trustRootIndex.hashCode()\n\n  override fun equals(other: Any?): Boolean =\n    if (other === this) {\n      true\n    } else {\n      other is BasicCertificateChainCleaner && other.trustRootIndex == trustRootIndex\n    }\n\n  companion object {\n    private const val MAX_SIGNERS = 9\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/BasicTrustRootIndex.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.tls\n\nimport java.security.cert.X509Certificate\nimport javax.security.auth.x500.X500Principal\n\n/** A simple index that of trusted root certificates that have been loaded into memory. */\nclass BasicTrustRootIndex(\n  vararg caCerts: X509Certificate,\n) : TrustRootIndex {\n  private val subjectToCaCerts: Map<X500Principal, Set<X509Certificate>>\n\n  init {\n    val map = mutableMapOf<X500Principal, MutableSet<X509Certificate>>()\n    for (caCert in caCerts) {\n      map.getOrPut(caCert.subjectX500Principal) { mutableSetOf() }.add(caCert)\n    }\n    this.subjectToCaCerts = map\n  }\n\n  override fun findByIssuerAndSignature(cert: X509Certificate): X509Certificate? {\n    val issuer = cert.issuerX500Principal\n    val subjectCaCerts = subjectToCaCerts[issuer] ?: return null\n\n    return subjectCaCerts.firstOrNull {\n      try {\n        cert.verify(it.publicKey)\n        return@firstOrNull true\n      } catch (_: Exception) {\n        return@firstOrNull false\n      }\n    }\n  }\n\n  override fun equals(other: Any?): Boolean =\n    other === this ||\n      (other is BasicTrustRootIndex && other.subjectToCaCerts == subjectToCaCerts)\n\n  override fun hashCode(): Int = subjectToCaCerts.hashCode()\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/CertificateChainCleaner.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3.internal.tls\n\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.internal.platform.Platform\n\n/**\n * Computes the effective certificate chain from the raw array returned by Java's built in TLS APIs.\n * Cleaning a chain returns a list of certificates where the first element is `chain[0]`, each\n * certificate is signed by the certificate that follows, and the last certificate is a trusted CA\n * certificate.\n *\n * Use of the chain cleaner is necessary to omit unexpected certificates that aren't relevant to\n * the TLS handshake and to extract the trusted CA certificate for the benefit of certificate\n * pinning.\n */\nabstract class CertificateChainCleaner {\n  @Throws(SSLPeerUnverifiedException::class)\n  abstract fun clean(\n    chain: List<Certificate>,\n    hostname: String,\n  ): List<Certificate>\n\n  companion object {\n    fun get(trustManager: X509TrustManager): CertificateChainCleaner = Platform.get().buildCertificateChainCleaner(trustManager)\n\n    fun get(vararg caCerts: X509Certificate): CertificateChainCleaner = BasicCertificateChainCleaner(BasicTrustRootIndex(*caCerts))\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/OkHostnameVerifier.kt",
    "content": "/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage okhttp3.internal.tls\n\nimport java.security.cert.CertificateParsingException\nimport java.security.cert.X509Certificate\nimport java.util.Locale\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLException\nimport javax.net.ssl.SSLSession\nimport okhttp3.internal.canParseAsIpAddress\nimport okhttp3.internal.toCanonicalHost\nimport okio.utf8Size\n\n/**\n * A HostnameVerifier consistent with [RFC 2818][rfc_2818].\n *\n * [rfc_2818]: http://www.ietf.org/rfc/rfc2818.txt\n */\n@Suppress(\"NAME_SHADOWING\")\nobject OkHostnameVerifier : HostnameVerifier {\n  private const val ALT_DNS_NAME = 2\n  private const val ALT_IPA_NAME = 7\n\n  override fun verify(\n    host: String,\n    session: SSLSession,\n  ): Boolean =\n    if (!host.isAscii()) {\n      false\n    } else {\n      try {\n        verify(host, session.peerCertificates[0] as X509Certificate)\n      } catch (_: SSLException) {\n        false\n      }\n    }\n\n  fun verify(\n    host: String,\n    certificate: X509Certificate,\n  ): Boolean =\n    when {\n      host.canParseAsIpAddress() -> verifyIpAddress(host, certificate)\n      else -> verifyHostname(host, certificate)\n    }\n\n  /** Returns true if [certificate] matches [ipAddress]. */\n  private fun verifyIpAddress(\n    ipAddress: String,\n    certificate: X509Certificate,\n  ): Boolean {\n    val canonicalIpAddress = ipAddress.toCanonicalHost()\n\n    return getSubjectAltNames(certificate, ALT_IPA_NAME).any {\n      canonicalIpAddress == it.toCanonicalHost()\n    }\n  }\n\n  /** Returns true if [certificate] matches [hostname]. */\n  private fun verifyHostname(\n    hostname: String,\n    certificate: X509Certificate,\n  ): Boolean {\n    val hostname = hostname.asciiToLowercase()\n    return getSubjectAltNames(certificate, ALT_DNS_NAME).any {\n      verifyHostname(hostname, it)\n    }\n  }\n\n  /**\n   * This is like [toLowerCase] except that it does nothing if this contains any non-ASCII\n   * characters. We want to avoid lower casing special chars like U+212A (Kelvin symbol) because\n   * they can return ASCII characters that match real hostnames.\n   */\n  private fun String.asciiToLowercase(): String =\n    when {\n      isAscii() -> lowercase(Locale.US)\n\n      // This is an ASCII string.\n      else -> this\n    }\n\n  /** Returns true if the [String] is ASCII encoded (0-127). */\n  private fun String.isAscii() = length == utf8Size().toInt()\n\n  /**\n   * Returns true if [hostname] matches the domain name [pattern].\n   *\n   * @param hostname lower-case host name.\n   * @param pattern domain name pattern from certificate. May be a wildcard pattern such as\n   *     `*.android.com`.\n   */\n  private fun verifyHostname(\n    hostname: String?,\n    pattern: String?,\n  ): Boolean {\n    var hostname = hostname\n    var pattern = pattern\n    if (hostname.isNullOrEmpty() ||\n      hostname.startsWith(\".\") ||\n      hostname.endsWith(\"..\")\n    ) {\n      // Invalid domain name.\n      return false\n    }\n    if (pattern.isNullOrEmpty() ||\n      pattern.startsWith(\".\") ||\n      pattern.endsWith(\"..\")\n    ) {\n      // Invalid pattern.\n      return false\n    }\n\n    // Normalize hostname and pattern by turning them into absolute domain names if they are not\n    // yet absolute. This is needed because server certificates do not normally contain absolute\n    // names or patterns, but they should be treated as absolute. At the same time, any hostname\n    // presented to this method should also be treated as absolute for the purposes of matching\n    // to the server certificate.\n    //   www.android.com  matches www.android.com\n    //   www.android.com  matches www.android.com.\n    //   www.android.com. matches www.android.com.\n    //   www.android.com. matches www.android.com\n    if (!hostname.endsWith(\".\")) {\n      hostname += \".\"\n    }\n    if (!pattern.endsWith(\".\")) {\n      pattern += \".\"\n    }\n    // Hostname and pattern are now absolute domain names.\n\n    pattern = pattern.asciiToLowercase()\n    // Hostname and pattern are now in lower case -- domain names are case-insensitive.\n\n    if (\"*\" !in pattern) {\n      // Not a wildcard pattern -- hostname and pattern must match exactly.\n      return hostname == pattern\n    }\n\n    // Wildcard pattern\n\n    // WILDCARD PATTERN RULES:\n    // 1. Asterisk (*) is only permitted in the left-most domain name label and must be the\n    //    only character in that label (i.e., must match the whole left-most label).\n    //    For example, *.example.com is permitted, while *a.example.com, a*.example.com,\n    //    a*b.example.com, a.*.example.com are not permitted.\n    // 2. Asterisk (*) cannot match across domain name labels.\n    //    For example, *.example.com matches test.example.com but does not match\n    //    sub.test.example.com.\n    // 3. Wildcard patterns for single-label domain names are not permitted.\n\n    if (!pattern.startsWith(\"*.\") || pattern.indexOf('*', 1) != -1) {\n      // Asterisk (*) is only permitted in the left-most domain name label and must be the only\n      // character in that label\n      return false\n    }\n\n    // Optimization: check whether hostname is too short to match the pattern. hostName must be at\n    // least as long as the pattern because asterisk must match the whole left-most label and\n    // hostname starts with a non-empty label. Thus, asterisk has to match one or more characters.\n    if (hostname.length < pattern.length) {\n      return false // Hostname too short to match the pattern.\n    }\n\n    if (\"*.\" == pattern) {\n      return false // Wildcard pattern for single-label domain name -- not permitted.\n    }\n\n    // Hostname must end with the region of pattern following the asterisk.\n    val suffix = pattern.substring(1)\n    if (!hostname.endsWith(suffix)) {\n      return false // Hostname does not end with the suffix.\n    }\n\n    // Check that asterisk did not match across domain name labels.\n    val suffixStartIndexInHostname = hostname.length - suffix.length\n    if (suffixStartIndexInHostname > 0 &&\n      hostname.lastIndexOf('.', suffixStartIndexInHostname - 1) != -1\n    ) {\n      return false // Asterisk is matching across domain name labels -- not permitted.\n    }\n\n    // Hostname matches pattern.\n    return true\n  }\n\n  fun allSubjectAltNames(certificate: X509Certificate): List<String> {\n    val altIpaNames = getSubjectAltNames(certificate, ALT_IPA_NAME)\n    val altDnsNames = getSubjectAltNames(certificate, ALT_DNS_NAME)\n    return altIpaNames + altDnsNames\n  }\n\n  private fun getSubjectAltNames(\n    certificate: X509Certificate,\n    type: Int,\n  ): List<String> {\n    try {\n      val subjectAltNames = certificate.subjectAlternativeNames ?: return emptyList()\n      val result = mutableListOf<String>()\n      for (subjectAltName in subjectAltNames) {\n        if (subjectAltName == null || subjectAltName.size < 2) continue\n        if (subjectAltName[0] != type) continue\n        val altName = subjectAltName[1] ?: continue\n        result.add(altName as String)\n      }\n      return result\n    } catch (_: CertificateParsingException) {\n      return emptyList()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/TrustRootIndex.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.tls\n\nimport java.security.cert.X509Certificate\n\nfun interface TrustRootIndex {\n  /** Returns the trusted CA certificate that signed [cert]. */\n  fun findByIssuerAndSignature(cert: X509Certificate): X509Certificate?\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/url/-Url.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal.url\n\nimport java.nio.charset.Charset\nimport okhttp3.internal.parseHexDigit\nimport okio.Buffer\n\ninternal val HEX_DIGITS =\n  charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')\ninternal const val USERNAME_ENCODE_SET = \" \\\"':;<=>@[]^`{}|/\\\\?#\"\ninternal const val PASSWORD_ENCODE_SET = \" \\\"':;<=>@[]^`{}|/\\\\?#\"\ninternal const val PATH_SEGMENT_ENCODE_SET = \" \\\"<>^`{}|/\\\\?#\"\ninternal const val PATH_SEGMENT_ENCODE_SET_URI = \"[]\"\ninternal const val QUERY_ENCODE_SET = \" \\\"'<>#\"\ninternal const val QUERY_COMPONENT_REENCODE_SET = \" \\\"'<>#&=\"\ninternal const val QUERY_COMPONENT_ENCODE_SET = \" !\\\"#$&'(),/:;<=>?@[]\\\\^`{|}~\"\ninternal const val QUERY_COMPONENT_ENCODE_SET_URI = \"\\\\^`{|}\"\ninternal const val FORM_ENCODE_SET = \" !\\\"#$&'()+,/:;<=>?@[\\\\]^`{|}~\"\ninternal const val FRAGMENT_ENCODE_SET = \"\"\ninternal const val FRAGMENT_ENCODE_SET_URI = \" \\\"#<>\\\\^`{|}\"\n\ninternal fun Buffer.writeCanonicalized(\n  input: String,\n  pos: Int,\n  limit: Int,\n  encodeSet: String,\n  alreadyEncoded: Boolean,\n  strict: Boolean,\n  plusIsSpace: Boolean,\n  unicodeAllowed: Boolean,\n  charset: Charset?,\n) {\n  var encodedCharBuffer: Buffer? = null // Lazily allocated.\n  var codePoint: Int\n  var i = pos\n  while (i < limit) {\n    codePoint = input.codePointAt(i)\n    if (alreadyEncoded &&\n      (\n        codePoint == '\\t'.code ||\n          codePoint == '\\n'.code ||\n          codePoint == '\\u000c'.code ||\n          codePoint == '\\r'.code\n      )\n    ) {\n      // Skip this character.\n    } else if (codePoint == ' '.code && encodeSet === FORM_ENCODE_SET) {\n      // Encode ' ' as '+'.\n      writeUtf8(\"+\")\n    } else if (codePoint == '+'.code && plusIsSpace) {\n      // Encode '+' as '%2B' since we permit ' ' to be encoded as either '+' or '%20'.\n      writeUtf8(if (alreadyEncoded) \"+\" else \"%2B\")\n    } else if (codePoint < 0x20 ||\n      codePoint == 0x7f ||\n      codePoint >= 0x80 &&\n      !unicodeAllowed ||\n      codePoint.toChar() in encodeSet ||\n      codePoint == '%'.code &&\n      (!alreadyEncoded || strict && !input.isPercentEncoded(i, limit))\n    ) {\n      // Percent encode this character.\n      if (encodedCharBuffer == null) {\n        encodedCharBuffer = Buffer()\n      }\n\n      if (charset == null || charset == Charsets.UTF_8) {\n        encodedCharBuffer.writeUtf8CodePoint(codePoint)\n      } else {\n        encodedCharBuffer.writeString(input, i, i + Character.charCount(codePoint), charset)\n      }\n\n      while (!encodedCharBuffer.exhausted()) {\n        val b = encodedCharBuffer.readByte().toInt() and 0xff\n        writeByte('%'.code)\n        writeByte(HEX_DIGITS[b shr 4 and 0xf].code)\n        writeByte(HEX_DIGITS[b and 0xf].code)\n      }\n    } else {\n      // This character doesn't need encoding. Just copy it over.\n      writeUtf8CodePoint(codePoint)\n    }\n    i += Character.charCount(codePoint)\n  }\n}\n\n/**\n * Returns a substring of `input` on the range `[pos..limit)` with the following\n * transformations:\n *\n *  * Tabs, newlines, form feeds and carriage returns are skipped.\n *\n *  * In queries, ' ' is encoded to '+' and '+' is encoded to \"%2B\".\n *\n *  * Characters in `encodeSet` are percent-encoded.\n *\n *  * Control characters and non-ASCII characters are percent-encoded.\n *\n *  * All other characters are copied without transformation.\n *\n * @param alreadyEncoded true to leave '%' as-is; false to convert it to '%25'.\n * @param strict true to encode '%' if it is not the prefix of a valid percent encoding.\n * @param plusIsSpace true to encode '+' as \"%2B\" if it is not already encoded.\n * @param unicodeAllowed true to leave non-ASCII codepoint unencoded.\n * @param charset which charset to use, null equals UTF-8.\n */\ninternal fun String.canonicalizeWithCharset(\n  pos: Int = 0,\n  limit: Int = length,\n  encodeSet: String,\n  alreadyEncoded: Boolean = false,\n  strict: Boolean = false,\n  plusIsSpace: Boolean = false,\n  unicodeAllowed: Boolean = false,\n  charset: Charset? = null,\n): String {\n  var codePoint: Int\n  var i = pos\n  while (i < limit) {\n    codePoint = codePointAt(i)\n    if (codePoint < 0x20 ||\n      codePoint == 0x7f ||\n      codePoint >= 0x80 &&\n      !unicodeAllowed ||\n      codePoint.toChar() in encodeSet ||\n      codePoint == '%'.code &&\n      (!alreadyEncoded || strict && !isPercentEncoded(i, limit)) ||\n      codePoint == '+'.code &&\n      plusIsSpace\n    ) {\n      // Slow path: the character at i requires encoding!\n      val out = Buffer()\n      out.writeUtf8(this, pos, i)\n      out.writeCanonicalized(\n        input = this,\n        pos = i,\n        limit = limit,\n        encodeSet = encodeSet,\n        alreadyEncoded = alreadyEncoded,\n        strict = strict,\n        plusIsSpace = plusIsSpace,\n        unicodeAllowed = unicodeAllowed,\n        charset = charset,\n      )\n      return out.readUtf8()\n    }\n    i += Character.charCount(codePoint)\n  }\n\n  // Fast path: no characters in [pos..limit) required encoding.\n  return substring(pos, limit)\n}\n\ninternal fun Buffer.writePercentDecoded(\n  encoded: String,\n  pos: Int,\n  limit: Int,\n  plusIsSpace: Boolean,\n) {\n  var codePoint: Int\n  var i = pos\n  while (i < limit) {\n    codePoint = encoded.codePointAt(i)\n    if (codePoint == '%'.code && i + 2 < limit) {\n      val d1 = encoded[i + 1].parseHexDigit()\n      val d2 = encoded[i + 2].parseHexDigit()\n      if (d1 != -1 && d2 != -1) {\n        writeByte((d1 shl 4) + d2)\n        i += 2\n        i += Character.charCount(codePoint)\n        continue\n      }\n    } else if (codePoint == '+'.code && plusIsSpace) {\n      writeByte(' '.code)\n      i++\n      continue\n    }\n    writeUtf8CodePoint(codePoint)\n    i += Character.charCount(codePoint)\n  }\n}\n\ninternal fun String.canonicalize(\n  pos: Int = 0,\n  limit: Int = length,\n  encodeSet: String,\n  alreadyEncoded: Boolean = false,\n  strict: Boolean = false,\n  plusIsSpace: Boolean = false,\n  unicodeAllowed: Boolean = false,\n): String =\n  canonicalizeWithCharset(\n    pos = pos,\n    limit = limit,\n    encodeSet = encodeSet,\n    alreadyEncoded = alreadyEncoded,\n    strict = strict,\n    plusIsSpace = plusIsSpace,\n    unicodeAllowed = unicodeAllowed,\n  )\n\ninternal fun String.percentDecode(\n  pos: Int = 0,\n  limit: Int = length,\n  plusIsSpace: Boolean = false,\n): String {\n  for (i in pos until limit) {\n    val c = this[i]\n    if (c == '%' || c == '+' && plusIsSpace) {\n      // Slow path: the character at i requires decoding!\n      val out = Buffer()\n      out.writeUtf8(this, pos, i)\n      out.writePercentDecoded(this, pos = i, limit = limit, plusIsSpace = plusIsSpace)\n      return out.readUtf8()\n    }\n  }\n\n  // Fast path: no characters in [pos..limit) required decoding.\n  return substring(pos, limit)\n}\n\ninternal fun String.isPercentEncoded(\n  pos: Int,\n  limit: Int,\n): Boolean =\n  pos + 2 < limit &&\n    this[pos] == '%' &&\n    this[pos + 1].parseHexDigit() != -1 &&\n    this[pos + 2].parseHexDigit() != -1\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/ws/MessageDeflater.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.util.zip.Deflater\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeHex\nimport okio.DeflaterSink\n\nprivate val EMPTY_DEFLATE_BLOCK = \"000000ffff\".decodeHex()\nprivate const val LAST_OCTETS_COUNT_TO_REMOVE_AFTER_DEFLATION = 4\n\nclass MessageDeflater(\n  private val noContextTakeover: Boolean,\n) : Closeable {\n  private val deflatedBytes = Buffer()\n\n  private val deflater =\n    Deflater(\n      Deflater.DEFAULT_COMPRESSION,\n      // nowrap (omits zlib header):\n      true,\n    )\n\n  private val deflaterSink = DeflaterSink(deflatedBytes, deflater)\n\n  /** Deflates [buffer] in place as described in RFC 7692 section 7.2.1. */\n  @Throws(IOException::class)\n  fun deflate(buffer: Buffer) {\n    require(deflatedBytes.size == 0L)\n\n    if (noContextTakeover) {\n      deflater.reset()\n    }\n\n    deflaterSink.write(buffer, buffer.size)\n    deflaterSink.flush()\n\n    if (deflatedBytes.endsWith(EMPTY_DEFLATE_BLOCK)) {\n      val newSize = deflatedBytes.size - LAST_OCTETS_COUNT_TO_REMOVE_AFTER_DEFLATION\n      deflatedBytes.readAndWriteUnsafe().use { cursor ->\n        cursor.resizeBuffer(newSize)\n      }\n    } else {\n      // Same as adding EMPTY_DEFLATE_BLOCK and then removing 4 bytes.\n      deflatedBytes.writeByte(0x00)\n    }\n\n    buffer.write(deflatedBytes, deflatedBytes.size)\n  }\n\n  @Throws(IOException::class)\n  override fun close() = deflaterSink.close()\n\n  private fun Buffer.endsWith(suffix: ByteString): Boolean = rangeEquals(size - suffix.size, suffix)\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/ws/MessageInflater.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.util.zip.Inflater\nimport okio.Buffer\nimport okio.InflaterSource\n\nprivate const val OCTETS_TO_ADD_BEFORE_INFLATION = 0x0000ffff\n\nclass MessageInflater(\n  private val noContextTakeover: Boolean,\n) : Closeable {\n  private val deflatedBytes = Buffer()\n\n  // Lazily-created.\n  private var inflater: Inflater? = null\n  private var inflaterSource: InflaterSource? = null\n\n  /** Inflates [buffer] in place as described in RFC 7692 section 7.2.2. */\n  @Throws(IOException::class)\n  fun inflate(buffer: Buffer) {\n    require(deflatedBytes.size == 0L)\n\n    val inflater =\n      this.inflater\n        ?: Inflater(true).also { this.inflater = it }\n    val inflaterSource =\n      this.inflaterSource\n        ?: InflaterSource(deflatedBytes, inflater).also { this.inflaterSource = it }\n\n    if (noContextTakeover) {\n      inflater.reset()\n    }\n\n    deflatedBytes.writeAll(buffer)\n    deflatedBytes.writeInt(OCTETS_TO_ADD_BEFORE_INFLATION)\n\n    val totalBytesToRead = inflater.bytesRead + deflatedBytes.size\n\n    // We cannot read all, as the source does not close.\n    // Instead, we ensure that all bytes from source have been processed by inflater.\n    do {\n      inflaterSource.readOrInflate(buffer, Long.MAX_VALUE)\n    } while (inflater.bytesRead < totalBytesToRead && !inflater.finished())\n\n    // The inflater data was self-terminated and there's unexpected trailing data. Tear it all down\n    // so we don't leak that data into the input of the next message.\n    if (inflater.bytesRead < totalBytesToRead) {\n      deflatedBytes.clear()\n      inflaterSource.close()\n      this.inflaterSource = null\n      this.inflater = null\n    }\n  }\n\n  @Throws(IOException::class)\n  override fun close() {\n    inflaterSource?.close()\n    inflaterSource = null\n    inflater = null\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/ws/RealWebSocket.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport java.io.IOException\nimport java.net.ProtocolException\nimport java.net.SocketTimeoutException\nimport java.util.ArrayDeque\nimport java.util.Random\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.TimeUnit.MILLISECONDS\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.EventListener\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.WebSocket\nimport okhttp3.WebSocketListener\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.concurrent.Lockable\nimport okhttp3.internal.concurrent.Task\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.assertLockHeld\nimport okhttp3.internal.connection.BufferedSocket\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.connection.asBufferedSocket\nimport okhttp3.internal.okHttpName\nimport okhttp3.internal.ws.WebSocketProtocol.CLOSE_CLIENT_GOING_AWAY\nimport okhttp3.internal.ws.WebSocketProtocol.CLOSE_MESSAGE_MAX\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_BINARY\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_TEXT\nimport okhttp3.internal.ws.WebSocketProtocol.validateCloseCode\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.ByteString.Companion.toByteString\nimport okio.Socket\n\nclass RealWebSocket(\n  taskRunner: TaskRunner,\n  /** The application's original request unadulterated by web socket headers. */\n  private val originalRequest: Request,\n  internal val listener: WebSocketListener,\n  private val random: Random,\n  private val pingIntervalMillis: Long,\n  /**\n   * For clients this is initially null, and will be assigned to the agreed-upon extensions. For\n   * servers, it should be the agreed-upon extensions immediately.\n   */\n  private var extensions: WebSocketExtensions?,\n  /** If compression is negotiated, outbound messages of this size and larger will be compressed. */\n  private var minimumDeflateSize: Long,\n  private val webSocketCloseTimeout: Long,\n) : WebSocket,\n  WebSocketReader.FrameCallback,\n  Lockable {\n  private val key: String\n\n  /** Non-null for client web sockets. These can be canceled. */\n  internal var call: Call? = null\n\n  /** This task processes the outgoing queues. Call [runWriter] to after enqueueing. */\n  private var writerTask: Task? = null\n\n  /** Null until this web socket is connected. Only accessed by the reader thread. */\n  private var reader: WebSocketReader? = null\n\n  // All mutable web socket state is guarded by this.\n\n  /** Null until this web socket is connected. Note that messages may be enqueued before that. */\n  private var writer: WebSocketWriter? = null\n\n  /** Used for writes, pings, and close timeouts. */\n  private var taskQueue = taskRunner.newQueue()\n\n  /** Names this web socket for observability and debugging. */\n  private var name: String? = null\n\n  /** The socket that carries this web socket. This is canceled when the web socket fails. */\n  private var socket: Socket? = null\n\n  /** Outgoing pongs in the order they should be written. */\n  private val pongQueue = ArrayDeque<ByteString>()\n\n  /** Outgoing messages and close frames in the order they should be written. */\n  private val messageAndCloseQueue = ArrayDeque<Any>()\n\n  /** The total size in bytes of enqueued but not yet transmitted messages. */\n  private var queueSize = 0L\n\n  /** True if we've enqueued a close frame. No further message frames will be enqueued. */\n  private var enqueuedClose = false\n\n  /** The close code from the peer, or -1 if this web socket has not yet read a close frame. */\n  private var receivedCloseCode = -1\n\n  /** The close reason from the peer, or null if this web socket has not yet read a close frame. */\n  private var receivedCloseReason: String? = null\n\n  /** True if this web socket failed and the listener has been notified. */\n  private var failed = false\n\n  /** Total number of pings sent by this web socket. */\n  private var sentPingCount = 0\n\n  /** Total number of pings received by this web socket. */\n  private var receivedPingCount = 0\n\n  /** Total number of pongs received by this web socket. */\n  private var receivedPongCount = 0\n\n  /** True if we have sent a ping that is still awaiting a reply. */\n  private var awaitingPong = false\n\n  init {\n    require(\"GET\" == originalRequest.method) {\n      \"Request must be GET: ${originalRequest.method}\"\n    }\n\n    this.key = ByteArray(16).apply { random.nextBytes(this) }.toByteString().base64()\n  }\n\n  override fun request(): Request = originalRequest\n\n  @Synchronized override fun queueSize(): Long = queueSize\n\n  override fun cancel() {\n    call!!.cancel()\n  }\n\n  fun connect(client: OkHttpClient) {\n    if (originalRequest.header(\"Sec-WebSocket-Extensions\") != null) {\n      failWebSocket(ProtocolException(\"Request header not permitted: 'Sec-WebSocket-Extensions'\"))\n      return\n    }\n\n    val webSocketClient =\n      client\n        .newBuilder()\n        .eventListener(EventListener.NONE)\n        .protocols(ONLY_HTTP1)\n        .build()\n    val request =\n      originalRequest\n        .newBuilder()\n        .header(\"Upgrade\", \"websocket\")\n        .header(\"Connection\", \"Upgrade\")\n        .header(\"Sec-WebSocket-Key\", key)\n        .header(\"Sec-WebSocket-Version\", \"13\")\n        .header(\"Sec-WebSocket-Extensions\", \"permessage-deflate\")\n        .build()\n    call = RealCall(webSocketClient, request, forWebSocket = true)\n    call!!.enqueue(\n      object : Callback {\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          val socket =\n            try {\n              checkUpgradeSuccess(response)\n            } catch (e: IOException) {\n              failWebSocket(e, response)\n              response.closeQuietly()\n              response.socket?.sink?.closeQuietly()\n              response.socket?.source?.closeQuietly()\n              return\n            }\n\n          // Apply the extensions. If they're unacceptable initiate a graceful shut down.\n          // TODO(jwilson): Listeners should get onFailure() instead of onClosing() + onClosed(1010).\n          val extensions = WebSocketExtensions.parse(response.headers)\n          this@RealWebSocket.extensions = extensions\n          if (!extensions.isValid()) {\n            synchronized(this@RealWebSocket) {\n              messageAndCloseQueue.clear() // Don't transmit any messages.\n              close(1010, \"unexpected Sec-WebSocket-Extensions in response header\")\n            }\n          }\n\n          // Process all web socket messages.\n          val name = \"$okHttpName WebSocket ${request.url.redact()}\"\n          initReaderAndWriter(\n            name = name,\n            socket = socket.asBufferedSocket(),\n            client = true,\n          )\n          loopReader(response)\n        }\n\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          failWebSocket(e)\n        }\n      },\n    )\n  }\n\n  private fun WebSocketExtensions.isValid(): Boolean {\n    // If the server returned parameters we don't understand, fail the web socket.\n    if (unknownValues) return false\n\n    // If the server returned a value for client_max_window_bits, fail the web socket.\n    if (clientMaxWindowBits != null) return false\n\n    // If the server returned an illegal server_max_window_bits, fail the web socket.\n    if (serverMaxWindowBits != null && serverMaxWindowBits !in 8..15) return false\n\n    // Success.\n    return true\n  }\n\n  @Throws(IOException::class)\n  internal fun checkUpgradeSuccess(response: Response): Socket {\n    if (response.code != 101) {\n      throw ProtocolException(\n        \"Expected HTTP 101 response but was '${response.code} ${response.message}'\",\n      )\n    }\n\n    val headerConnection = response.header(\"Connection\")\n    if (!\"Upgrade\".equals(headerConnection, ignoreCase = true)) {\n      throw ProtocolException(\n        \"Expected 'Connection' header value 'Upgrade' but was '$headerConnection'\",\n      )\n    }\n\n    val headerUpgrade = response.header(\"Upgrade\")\n    if (!\"websocket\".equals(headerUpgrade, ignoreCase = true)) {\n      throw ProtocolException(\n        \"Expected 'Upgrade' header value 'websocket' but was '$headerUpgrade'\",\n      )\n    }\n\n    val headerAccept = response.header(\"Sec-WebSocket-Accept\")\n    val acceptExpected = (key + WebSocketProtocol.ACCEPT_MAGIC).encodeUtf8().sha1().base64()\n    if (acceptExpected != headerAccept) {\n      throw ProtocolException(\n        \"Expected 'Sec-WebSocket-Accept' header value '$acceptExpected' but was '$headerAccept'\",\n      )\n    }\n\n    return response.socket\n      ?: throw ProtocolException(\"Web Socket socket missing: bad interceptor?\")\n  }\n\n  /**\n   * This accepts a [BufferedSource] instead of using [Socket.source], just in case we've already\n   * received data from the peer. This accepts a [BufferedSink] for symmetry with the source.\n   */\n  fun initReaderAndWriter(\n    name: String,\n    socket: BufferedSocket,\n    client: Boolean,\n  ) {\n    val extensions = this.extensions!!\n    synchronized(this) {\n      this.name = name\n      this.socket = socket\n      this.writer =\n        WebSocketWriter(\n          isClient = client,\n          sink = socket.sink,\n          random = random,\n          perMessageDeflate = extensions.perMessageDeflate,\n          noContextTakeover = extensions.noContextTakeover(client),\n          minimumDeflateSize = minimumDeflateSize,\n        )\n      this.writerTask = WriterTask()\n      if (pingIntervalMillis != 0L) {\n        val pingIntervalNanos = MILLISECONDS.toNanos(pingIntervalMillis)\n        taskQueue.schedule(\"$name ping\", pingIntervalNanos) {\n          writePingFrame()\n          return@schedule pingIntervalNanos\n        }\n      }\n      if (messageAndCloseQueue.isNotEmpty()) {\n        runWriter() // Send messages that were enqueued before we were connected.\n      }\n    }\n\n    reader =\n      WebSocketReader(\n        isClient = client,\n        source = socket.source,\n        frameCallback = this,\n        perMessageDeflate = extensions.perMessageDeflate,\n        noContextTakeover = extensions.noContextTakeover(!client),\n      )\n  }\n\n  /** Receive frames until there are no more. Invoked only by the reader thread. */\n  @Throws(IOException::class)\n  fun loopReader(response: Response) {\n    try {\n      listener.onOpen(this@RealWebSocket, response)\n      while (receivedCloseCode == -1) {\n        // This method call results in one or more onRead* methods being called on this thread.\n        reader!!.processNextFrame()\n      }\n    } catch (e: Exception) {\n      failWebSocket(e = e)\n    } finally {\n      finishReader()\n    }\n  }\n\n  /**\n   * For testing: receive a single frame and return true if there are more frames to read. Invoked\n   * only by the reader thread.\n   */\n  @Throws(IOException::class)\n  fun processNextFrame(): Boolean =\n    try {\n      reader!!.processNextFrame()\n      receivedCloseCode == -1\n    } catch (e: Exception) {\n      failWebSocket(e = e)\n      false\n    }\n\n  /**\n   * Clean up and publish necessary close events when the reader is done. Invoked only by the reader\n   * thread.\n   */\n  fun finishReader() {\n    val code: Int\n    val reason: String?\n    val sendOnClosed: Boolean\n    var readerToClose: WebSocketReader?\n    synchronized(this) {\n      code = receivedCloseCode\n      reason = receivedCloseReason\n\n      readerToClose = reader\n      reader = null\n\n      if (enqueuedClose && messageAndCloseQueue.isEmpty()) {\n        // Close the writer on the writer's thread.\n        val writerToClose = this.writer\n        if (writerToClose != null) {\n          this.writer = null\n          taskQueue.execute(\"$name writer close\", cancelable = false) {\n            writerToClose.closeQuietly()\n          }\n        }\n\n        this.taskQueue.shutdown()\n      }\n\n      sendOnClosed = !failed && writer == null && receivedCloseCode != -1\n    }\n\n    if (sendOnClosed) {\n      listener.onClosed(this, code, reason!!)\n    }\n\n    readerToClose?.closeQuietly()\n  }\n\n  /** For testing: force this web socket to release its threads. */\n  @Throws(InterruptedException::class)\n  fun tearDown() {\n    taskQueue.shutdown()\n    taskQueue.idleLatch().await(10, TimeUnit.SECONDS)\n  }\n\n  @Synchronized fun sentPingCount(): Int = sentPingCount\n\n  @Synchronized fun receivedPingCount(): Int = receivedPingCount\n\n  @Synchronized fun receivedPongCount(): Int = receivedPongCount\n\n  @Throws(IOException::class)\n  override fun onReadMessage(text: String) {\n    listener.onMessage(this, text)\n  }\n\n  @Throws(IOException::class)\n  override fun onReadMessage(bytes: ByteString) {\n    listener.onMessage(this, bytes)\n  }\n\n  @Synchronized override fun onReadPing(payload: ByteString) {\n    // Don't respond to pings after we've failed or sent the close frame.\n    if (failed || enqueuedClose && messageAndCloseQueue.isEmpty()) return\n\n    pongQueue.add(payload)\n    runWriter()\n    receivedPingCount++\n  }\n\n  @Synchronized override fun onReadPong(payload: ByteString) {\n    // This API doesn't expose pings.\n    receivedPongCount++\n    awaitingPong = false\n  }\n\n  override fun onReadClose(\n    code: Int,\n    reason: String,\n  ) {\n    require(code != -1)\n\n    synchronized(this) {\n      check(receivedCloseCode == -1) { \"already closed\" }\n      receivedCloseCode = code\n      receivedCloseReason = reason\n    }\n\n    listener.onClosing(this, code, reason)\n  }\n\n  // Writer methods to enqueue frames. They'll be sent asynchronously by the writer thread.\n\n  override fun send(text: String): Boolean = send(text.encodeUtf8(), OPCODE_TEXT)\n\n  override fun send(bytes: ByteString): Boolean = send(bytes, OPCODE_BINARY)\n\n  @Synchronized private fun send(\n    data: ByteString,\n    formatOpcode: Int,\n  ): Boolean {\n    // Don't send new frames after we've failed or enqueued a close frame.\n    if (failed || enqueuedClose) return false\n\n    // If this frame overflows the buffer, reject it and close the web socket.\n    if (queueSize + data.size > MAX_QUEUE_SIZE) {\n      close(CLOSE_CLIENT_GOING_AWAY, null)\n      return false\n    }\n\n    // Enqueue the message frame.\n    queueSize += data.size.toLong()\n    messageAndCloseQueue.add(Message(formatOpcode, data))\n    runWriter()\n    return true\n  }\n\n  @Synchronized fun pong(payload: ByteString): Boolean {\n    // Don't send pongs after we've failed or sent the close frame.\n    if (failed || enqueuedClose && messageAndCloseQueue.isEmpty()) return false\n\n    pongQueue.add(payload)\n    runWriter()\n    return true\n  }\n\n  override fun close(\n    code: Int,\n    reason: String?,\n  ): Boolean = close(code, reason, webSocketCloseTimeout)\n\n  @Synchronized fun close(\n    code: Int,\n    reason: String?,\n    cancelAfterCloseMillis: Long,\n  ): Boolean {\n    validateCloseCode(code)\n\n    var reasonBytes: ByteString? = null\n    if (reason != null) {\n      reasonBytes = reason.encodeUtf8()\n      require(reasonBytes.size <= CLOSE_MESSAGE_MAX) {\n        \"reason.size() > $CLOSE_MESSAGE_MAX: $reason\"\n      }\n    }\n\n    if (failed || enqueuedClose) return false\n\n    // Immediately prevent further frames from being enqueued.\n    enqueuedClose = true\n\n    // Enqueue the close frame.\n    messageAndCloseQueue.add(Close(code, reasonBytes, cancelAfterCloseMillis))\n    runWriter()\n    return true\n  }\n\n  private fun runWriter() {\n    assertLockHeld()\n\n    val writerTask = writerTask\n    if (writerTask != null) {\n      taskQueue.schedule(writerTask)\n    }\n  }\n\n  /**\n   * Attempts to remove a single frame from a queue and send it. This prefers to write urgent pongs\n   * before less urgent messages and close frames. For example, it's possible that a caller will\n   * enqueue messages followed by pongs, but this sends pongs followed by messages. Pongs are always\n   * written in the order they were enqueued.\n   *\n   * If a frame cannot be sent - because there are none enqueued or because the web socket is not\n   * connected - this does nothing and returns false. Otherwise, this returns true and the caller\n   * should immediately invoke this method again until it returns false.\n   *\n   * This method may only be invoked by the writer thread. There may be only thread invoking this\n   * method at a time.\n   */\n  @Throws(IOException::class)\n  internal fun writeOneFrame(): Boolean {\n    val writer: WebSocketWriter?\n    val pong: ByteString?\n    var messageOrClose: Any? = null\n    var receivedCloseCode = -1\n    var receivedCloseReason: String? = null\n    var sendOnClosed = false\n    var writerToClose: WebSocketWriter? = null\n\n    synchronized(this@RealWebSocket) {\n      if (failed) {\n        return false // Failed web socket.\n      }\n\n      writer = this.writer\n      pong = pongQueue.poll()\n      if (pong == null) {\n        messageOrClose = messageAndCloseQueue.poll()\n        if (messageOrClose is Close) {\n          receivedCloseCode = this.receivedCloseCode\n          receivedCloseReason = this.receivedCloseReason\n          if (receivedCloseCode != -1) {\n            writerToClose = this.writer\n            this.writer = null\n            sendOnClosed = writerToClose != null && reader == null\n            this.taskQueue.shutdown()\n          } else {\n            // When we request a graceful close also schedule a cancel of the web socket.\n            val cancelAfterCloseMillis = messageOrClose.cancelAfterCloseMillis\n            taskQueue.execute(\"$name cancel\", MILLISECONDS.toNanos(cancelAfterCloseMillis)) {\n              cancel()\n            }\n          }\n        } else if (messageOrClose == null) {\n          return false // The queue is exhausted.\n        }\n      }\n    }\n\n    try {\n      if (pong != null) {\n        writer!!.writePong(pong)\n      } else if (messageOrClose is Message) {\n        val message = messageOrClose\n        writer!!.writeMessageFrame(message.formatOpcode, message.data)\n        synchronized(this) {\n          queueSize -= message.data.size.toLong()\n        }\n      } else if (messageOrClose is Close) {\n        val close = messageOrClose\n        writer!!.writeClose(close.code, close.reason)\n\n        // We closed the writer: now both reader and writer are closed.\n        if (sendOnClosed) {\n          listener.onClosed(this, receivedCloseCode, receivedCloseReason!!)\n        }\n      } else {\n        throw AssertionError()\n      }\n\n      return true\n    } finally {\n      writerToClose?.closeQuietly()\n    }\n  }\n\n  internal fun writePingFrame() {\n    val writer: WebSocketWriter\n    val failedPing: Int\n    synchronized(this) {\n      if (failed) return\n      writer = this.writer ?: return\n      failedPing = if (awaitingPong) sentPingCount else -1\n      sentPingCount++\n      awaitingPong = true\n    }\n\n    if (failedPing != -1) {\n      failWebSocket(\n        e =\n          SocketTimeoutException(\n            \"sent ping but didn't receive pong within \" +\n              \"${pingIntervalMillis}ms (after ${failedPing - 1} successful ping/pongs)\",\n          ),\n        isWriter = true,\n      )\n      return\n    }\n\n    try {\n      writer.writePing(ByteString.EMPTY)\n    } catch (e: IOException) {\n      failWebSocket(e = e, isWriter = true)\n    }\n  }\n\n  fun failWebSocket(\n    e: Exception,\n    response: Response? = null,\n    isWriter: Boolean = false,\n  ) {\n    val socketToCancel: Socket?\n    val writerToClose: WebSocketWriter?\n    synchronized(this) {\n      if (failed) return // Already failed.\n      failed = true\n\n      socketToCancel = this.socket\n\n      writerToClose = this.writer\n      this.writer = null\n\n      if (!isWriter && writerToClose != null) {\n        // If the caller isn't the writer thread, get that thread to close the writer.\n        taskQueue.execute(\"$name writer close\", cancelable = false) {\n          writerToClose.closeQuietly()\n        }\n      }\n\n      taskQueue.shutdown()\n    }\n\n    try {\n      listener.onFailure(this, e, response)\n    } finally {\n      socketToCancel?.cancel()\n\n      // If the caller is the writer thread, close it on this thread.\n      if (isWriter) {\n        writerToClose?.closeQuietly()\n      }\n    }\n  }\n\n  internal class Message(\n    val formatOpcode: Int,\n    val data: ByteString,\n  )\n\n  internal class Close(\n    val code: Int,\n    val reason: ByteString?,\n    val cancelAfterCloseMillis: Long,\n  )\n\n  private inner class WriterTask : Task(\"$name writer\") {\n    override fun runOnce(): Long {\n      try {\n        if (writeOneFrame()) return 0L\n      } catch (e: IOException) {\n        failWebSocket(e = e, isWriter = true)\n      }\n      return -1L\n    }\n  }\n\n  companion object {\n    private val ONLY_HTTP1 = listOf(Protocol.HTTP_1_1)\n\n    /**\n     * The maximum number of bytes to enqueue. Rather than enqueueing beyond this limit we tear down\n     * the web socket! It's possible that we're writing faster than the peer can read.\n     */\n    private const val MAX_QUEUE_SIZE = 16L * 1024 * 1024 // 16 MiB.\n\n    /**\n     * The maximum amount of time after the client calls [close] to wait for a graceful shutdown. If\n     * the server doesn't respond the web socket will be canceled.\n     */\n    const val CANCEL_AFTER_CLOSE_MILLIS = 60L * 1000\n\n    /**\n     * The smallest message that will be compressed. We use 1024 because smaller messages already\n     * fit comfortably within a single ethernet packet (1500 bytes) even with framing overhead.\n     *\n     * For tests this must be big enough to realize real compression on test messages like\n     * 'aaaaaaaaaa...'. Our tests check if compression was applied just by looking at the size if\n     * the inbound buffer.\n     */\n    const val DEFAULT_MINIMUM_DEFLATE_SIZE = 1024L\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/ws/WebSocketExtensions.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport java.io.IOException\nimport okhttp3.Headers\nimport okhttp3.internal.delimiterOffset\nimport okhttp3.internal.trimSubstring\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n\n/**\n * Models the contents of a `Sec-WebSocket-Extensions` response header. OkHttp honors one extension\n * `permessage-deflate` and four parameters, `client_max_window_bits`, `client_no_context_takeover`,\n * `server_max_window_bits`, and `server_no_context_takeover`.\n *\n * Typically this will look like one of the following:\n *\n * ```\n * Sec-WebSocket-Extensions: permessage-deflate\n * Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits=\"15\"\n * Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits=15\n * Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover\n * Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=\"15\"\n * Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15\n * Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover\n * Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover;\n *     client_no_context_takeover\n * Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=\"15\";\n *     client_max_window_bits=\"15\"; server_no_context_takeover; client_no_context_takeover\n * ```\n *\n * If any other extension or parameter is specified, then [unknownValues] will be true. Such\n * responses should be refused as their web socket extensions will not be understood.\n *\n * Note that [java.util.zip.Deflater] is hardcoded to use 15 bits (32 KiB) for\n * `client_max_window_bits` and [java.util.zip.Inflater] is hardcoded to use 15 bits (32 KiB) for\n * `server_max_window_bits`. This harms our ability to support these parameters:\n *\n *  * If `client_max_window_bits` is less than 15, OkHttp must close the web socket with code 1010.\n *    Otherwise it would compress values in a way that servers could not decompress.\n *  * If `server_max_window_bits` is less than 15, OkHttp will waste memory on an oversized buffer.\n *\n * See [RFC 7692, 7.1][rfc_7692] for details on negotiation process.\n *\n * [rfc_7692]: https://tools.ietf.org/html/rfc7692#section-7.1\n */\n@IgnoreJRERequirement // As of AGP 3.4.1, D8 desugars API 24 hashCode methods.\ndata class WebSocketExtensions(\n  /** True if the agreed upon extensions includes the permessage-deflate extension. */\n  @JvmField val perMessageDeflate: Boolean = false,\n  /** Should be a value in [8..15]. Only 15 is acceptable by OkHttp as Java APIs are limited. */\n  @JvmField val clientMaxWindowBits: Int? = null,\n  /** True if the agreed upon extension parameters includes \"client_no_context_takeover\". */\n  @JvmField val clientNoContextTakeover: Boolean = false,\n  /** Should be a value in [8..15]. Any value in that range is acceptable by OkHttp. */\n  @JvmField val serverMaxWindowBits: Int? = null,\n  /** True if the agreed upon extension parameters includes \"server_no_context_takeover\". */\n  @JvmField val serverNoContextTakeover: Boolean = false,\n  /**\n   * True if the agreed upon extensions or parameters contained values unrecognized by OkHttp.\n   * Typically this indicates that the client will need to close the web socket with code 1010.\n   */\n  @JvmField val unknownValues: Boolean = false,\n) {\n  fun noContextTakeover(clientOriginated: Boolean): Boolean =\n    if (clientOriginated) {\n      clientNoContextTakeover // Client is deflating.\n    } else {\n      serverNoContextTakeover // Server is deflating.\n    }\n\n  companion object {\n    private const val HEADER_WEB_SOCKET_EXTENSION = \"Sec-WebSocket-Extensions\"\n\n    @Throws(IOException::class)\n    fun parse(responseHeaders: Headers): WebSocketExtensions {\n      // Note that this code does case-insensitive comparisons, even though the spec doesn't specify\n      // whether extension tokens and parameters are case-insensitive or not.\n\n      var compressionEnabled = false\n      var clientMaxWindowBits: Int? = null\n      var clientNoContextTakeover = false\n      var serverMaxWindowBits: Int? = null\n      var serverNoContextTakeover = false\n      var unexpectedValues = false\n\n      // Parse each header.\n      for (i in 0 until responseHeaders.size) {\n        if (!responseHeaders.name(i).equals(HEADER_WEB_SOCKET_EXTENSION, ignoreCase = true)) {\n          continue // Not a header we're interested in.\n        }\n        val header = responseHeaders.value(i)\n\n        // Parse each extension.\n        var pos = 0\n        while (pos < header.length) {\n          val extensionEnd = header.delimiterOffset(',', pos)\n          val extensionTokenEnd = header.delimiterOffset(';', pos, extensionEnd)\n          val extensionToken = header.trimSubstring(pos, extensionTokenEnd)\n          pos = extensionTokenEnd + 1\n\n          when {\n            extensionToken.equals(\"permessage-deflate\", ignoreCase = true) -> {\n              if (compressionEnabled) unexpectedValues = true // Repeated extension!\n              compressionEnabled = true\n\n              // Parse each permessage-deflate parameter.\n              while (pos < extensionEnd) {\n                val parameterEnd = header.delimiterOffset(';', pos, extensionEnd)\n                val equals = header.delimiterOffset('=', pos, parameterEnd)\n                val name = header.trimSubstring(pos, equals)\n                val value =\n                  if (equals < parameterEnd) {\n                    header.trimSubstring(equals + 1, parameterEnd).removeSurrounding(\"\\\"\")\n                  } else {\n                    null\n                  }\n                pos = parameterEnd + 1\n                when {\n                  name.equals(\"client_max_window_bits\", ignoreCase = true) -> {\n                    if (clientMaxWindowBits != null) unexpectedValues = true // Repeated parameter!\n                    clientMaxWindowBits = value?.toIntOrNull()\n                    if (clientMaxWindowBits == null) unexpectedValues = true // Not an int!\n                  }\n\n                  name.equals(\"client_no_context_takeover\", ignoreCase = true) -> {\n                    if (clientNoContextTakeover) unexpectedValues = true // Repeated parameter!\n                    if (value != null) unexpectedValues = true // Unexpected value!\n                    clientNoContextTakeover = true\n                  }\n\n                  name.equals(\"server_max_window_bits\", ignoreCase = true) -> {\n                    if (serverMaxWindowBits != null) unexpectedValues = true // Repeated parameter!\n                    serverMaxWindowBits = value?.toIntOrNull()\n                    if (serverMaxWindowBits == null) unexpectedValues = true // Not an int!\n                  }\n\n                  name.equals(\"server_no_context_takeover\", ignoreCase = true) -> {\n                    if (serverNoContextTakeover) unexpectedValues = true // Repeated parameter!\n                    if (value != null) unexpectedValues = true // Unexpected value!\n                    serverNoContextTakeover = true\n                  }\n\n                  else -> {\n                    unexpectedValues = true // Unexpected parameter.\n                  }\n                }\n              }\n            }\n\n            else -> {\n              unexpectedValues = true // Unexpected extension.\n            }\n          }\n        }\n      }\n\n      return WebSocketExtensions(\n        perMessageDeflate = compressionEnabled,\n        clientMaxWindowBits = clientMaxWindowBits,\n        clientNoContextTakeover = clientNoContextTakeover,\n        serverMaxWindowBits = serverMaxWindowBits,\n        serverNoContextTakeover = serverNoContextTakeover,\n        unknownValues = unexpectedValues,\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/ws/WebSocketProtocol.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport okio.Buffer\nimport okio.ByteString.Companion.encodeUtf8\n\nobject WebSocketProtocol {\n  /** Magic value which must be appended to the key in a response header. */\n  internal const val ACCEPT_MAGIC = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n  /*\n  Each frame starts with two bytes of data.\n\n   0 1 2 3 4 5 6 7    0 1 2 3 4 5 6 7\n  +-+-+-+-+-------+  +-+-------------+\n  |F|R|R|R| OP    |  |M| LENGTH      |\n  |I|S|S|S| CODE  |  |A|             |\n  |N|V|V|V|       |  |S|             |\n  | |1|2|3|       |  |K|             |\n  +-+-+-+-+-------+  +-+-------------+\n   */\n\n  /** Byte 0 flag for whether this is the final fragment in a message. */\n  internal const val B0_FLAG_FIN = 128\n\n  /** Byte 0 reserved flag 1. Must be 0 unless negotiated otherwise. */\n  internal const val B0_FLAG_RSV1 = 64\n\n  /** Byte 0 reserved flag 2. Must be 0 unless negotiated otherwise. */\n  internal const val B0_FLAG_RSV2 = 32\n\n  /** Byte 0 reserved flag 3. Must be 0 unless negotiated otherwise. */\n  internal const val B0_FLAG_RSV3 = 16\n\n  /** Byte 0 mask for the frame opcode. */\n  internal const val B0_MASK_OPCODE = 15\n\n  /** Flag in the opcode which indicates a control frame. */\n  internal const val OPCODE_FLAG_CONTROL = 8\n\n  /**\n   * Byte 1 flag for whether the payload data is masked.\n   *\n   * If this flag is set, the next four\n   * bytes represent the mask key. These bytes appear after any additional bytes specified by [B1_MASK_LENGTH].\n   */\n  internal const val B1_FLAG_MASK = 128\n\n  /**\n   * Byte 1 mask for the payload length.\n   *\n   * If this value is [PAYLOAD_SHORT], the next two\n   * bytes represent the length. If this value is [PAYLOAD_LONG], the next eight bytes\n   * represent the length.\n   */\n  internal const val B1_MASK_LENGTH = 127\n\n  internal const val OPCODE_CONTINUATION = 0x0\n  internal const val OPCODE_TEXT = 0x1\n  internal const val OPCODE_BINARY = 0x2\n\n  internal const val OPCODE_CONTROL_CLOSE = 0x8\n  internal const val OPCODE_CONTROL_PING = 0x9\n  internal const val OPCODE_CONTROL_PONG = 0xa\n\n  /**\n   * Maximum length of frame payload. Larger payloads, if supported by the frame type, can use the\n   * special values [PAYLOAD_SHORT] or [PAYLOAD_LONG].\n   */\n  internal const val PAYLOAD_BYTE_MAX = 125L\n\n  /** Maximum length of close message in bytes. */\n  internal const val CLOSE_MESSAGE_MAX = PAYLOAD_BYTE_MAX - 2\n\n  /**\n   * Value for [B1_MASK_LENGTH] which indicates the next two bytes are the unsigned length.\n   */\n  internal const val PAYLOAD_SHORT = 126\n\n  /** Maximum length of a frame payload to be denoted as [PAYLOAD_SHORT]. */\n  internal const val PAYLOAD_SHORT_MAX = 0xffffL\n\n  /**\n   * Value for [B1_MASK_LENGTH] which indicates the next eight bytes are the unsigned\n   * length.\n   */\n  internal const val PAYLOAD_LONG = 127\n\n  /** Used when an unchecked exception was thrown in a listener. */\n  internal const val CLOSE_CLIENT_GOING_AWAY = 1001\n\n  /** Used when an empty close frame was received (i.e., without a status code). */\n  internal const val CLOSE_NO_STATUS_CODE = 1005\n\n  fun toggleMask(\n    cursor: Buffer.UnsafeCursor,\n    key: ByteArray,\n  ) {\n    var keyIndex = 0\n    val keyLength = key.size\n    do {\n      val buffer = cursor.data\n      var i = cursor.start\n      val end = cursor.end\n      if (buffer != null) {\n        while (i < end) {\n          keyIndex %= keyLength // Reassign to prevent overflow breaking counter.\n\n          // Byte xor is experimental in Kotlin so we coerce bytes to int, xor them\n          // and convert back to byte.\n          val bufferInt: Int = buffer[i].toInt()\n          val keyInt: Int = key[keyIndex].toInt()\n          buffer[i] = (bufferInt xor keyInt).toByte()\n\n          i++\n          keyIndex++\n        }\n      }\n    } while (cursor.next() != -1)\n  }\n\n  fun closeCodeExceptionMessage(code: Int): String? =\n    if (code < 1000 || code >= 5000) {\n      \"Code must be in range [1000,5000): $code\"\n    } else if (code in 1004..1006 || code in 1015..2999) {\n      \"Code $code is reserved and may not be used.\"\n    } else {\n      null\n    }\n\n  fun validateCloseCode(code: Int) {\n    val message = closeCodeExceptionMessage(code)\n    require(message == null) { message!! }\n  }\n\n  fun acceptHeader(key: String): String = (key + ACCEPT_MAGIC).encodeUtf8().sha1().base64()\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/ws/WebSocketReader.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.net.ProtocolException\nimport java.util.concurrent.TimeUnit\nimport okhttp3.internal.and\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.toHexString\nimport okhttp3.internal.ws.WebSocketProtocol.B0_FLAG_FIN\nimport okhttp3.internal.ws.WebSocketProtocol.B0_FLAG_RSV1\nimport okhttp3.internal.ws.WebSocketProtocol.B0_FLAG_RSV2\nimport okhttp3.internal.ws.WebSocketProtocol.B0_FLAG_RSV3\nimport okhttp3.internal.ws.WebSocketProtocol.B0_MASK_OPCODE\nimport okhttp3.internal.ws.WebSocketProtocol.B1_FLAG_MASK\nimport okhttp3.internal.ws.WebSocketProtocol.B1_MASK_LENGTH\nimport okhttp3.internal.ws.WebSocketProtocol.CLOSE_NO_STATUS_CODE\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_BINARY\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_CONTINUATION\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_CONTROL_CLOSE\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_CONTROL_PING\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_CONTROL_PONG\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_FLAG_CONTROL\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_TEXT\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_BYTE_MAX\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_LONG\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_SHORT\nimport okhttp3.internal.ws.WebSocketProtocol.toggleMask\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString\n\n/**\n * An [RFC 6455][rfc_6455]-compatible WebSocket frame reader.\n *\n * This class is not thread safe.\n *\n * [rfc_6455]: http://tools.ietf.org/html/rfc6455\n */\nclass WebSocketReader(\n  private val isClient: Boolean,\n  val source: BufferedSource,\n  private val frameCallback: FrameCallback,\n  private val perMessageDeflate: Boolean,\n  private val noContextTakeover: Boolean,\n) : Closeable {\n  private var closed = false\n  private var receivedCloseFrame = false\n\n  // Stateful data about the current frame.\n  private var opcode = 0\n  private var frameLength = 0L\n  private var isFinalFrame = false\n  private var isControlFrame = false\n  private var readingCompressedMessage = false\n\n  private val controlFrameBuffer = Buffer()\n  private val messageFrameBuffer = Buffer()\n\n  /** Lazily initialized on first use. */\n  private var messageInflater: MessageInflater? = null\n\n  // Masks are only a concern for server writers.\n  private val maskKey: ByteArray? = if (isClient) null else ByteArray(4)\n  private val maskCursor: Buffer.UnsafeCursor? = if (isClient) null else Buffer.UnsafeCursor()\n\n  interface FrameCallback {\n    @Throws(IOException::class)\n    fun onReadMessage(text: String)\n\n    @Throws(IOException::class)\n    fun onReadMessage(bytes: ByteString)\n\n    fun onReadPing(payload: ByteString)\n\n    fun onReadPong(payload: ByteString)\n\n    fun onReadClose(\n      code: Int,\n      reason: String,\n    )\n  }\n\n  /**\n   * Process the next protocol frame.\n   *\n   *  * If it is a control frame this will result in a single call to [FrameCallback].\n   *  * If it is a message frame this will result in a single call to [FrameCallback.onReadMessage].\n   *    If the message spans multiple frames, each interleaved control frame will result in a\n   *    corresponding call to [FrameCallback].\n   */\n  @Throws(IOException::class)\n  fun processNextFrame() {\n    check(!closed) { \"closed\" }\n\n    readHeader()\n    if (isControlFrame) {\n      readControlFrame()\n    } else {\n      readMessageFrame()\n    }\n  }\n\n  @Throws(IOException::class, ProtocolException::class)\n  private fun readHeader() {\n    if (receivedCloseFrame) throw IOException(\"closed\")\n\n    // Disable the timeout to read the first byte of a new frame.\n    val b0: Int\n    val timeoutBefore = source.timeout().timeoutNanos()\n    source.timeout().clearTimeout()\n    try {\n      b0 = source.readByte() and 0xff\n    } finally {\n      source.timeout().timeout(timeoutBefore, TimeUnit.NANOSECONDS)\n    }\n\n    opcode = b0 and B0_MASK_OPCODE\n    isFinalFrame = b0 and B0_FLAG_FIN != 0\n    isControlFrame = b0 and OPCODE_FLAG_CONTROL != 0\n\n    // Control frames must be final frames (cannot contain continuations).\n    if (isControlFrame && !isFinalFrame) {\n      throw ProtocolException(\"Control frames must be final.\")\n    }\n\n    val reservedFlag1 = b0 and B0_FLAG_RSV1 != 0\n    when (opcode) {\n      OPCODE_TEXT, OPCODE_BINARY -> {\n        readingCompressedMessage =\n          if (reservedFlag1) {\n            if (!perMessageDeflate) throw ProtocolException(\"Unexpected rsv1 flag\")\n            true\n          } else {\n            false\n          }\n      }\n\n      else -> {\n        if (reservedFlag1) throw ProtocolException(\"Unexpected rsv1 flag\")\n      }\n    }\n\n    val reservedFlag2 = b0 and B0_FLAG_RSV2 != 0\n    if (reservedFlag2) throw ProtocolException(\"Unexpected rsv2 flag\")\n\n    val reservedFlag3 = b0 and B0_FLAG_RSV3 != 0\n    if (reservedFlag3) throw ProtocolException(\"Unexpected rsv3 flag\")\n\n    val b1 = source.readByte() and 0xff\n\n    val isMasked = b1 and B1_FLAG_MASK != 0\n    if (isMasked == isClient) {\n      // Masked payloads must be read on the server. Unmasked payloads must be read on the client.\n      throw ProtocolException(\n        if (isClient) {\n          \"Server-sent frames must not be masked.\"\n        } else {\n          \"Client-sent frames must be masked.\"\n        },\n      )\n    }\n\n    // Get frame length, optionally reading from follow-up bytes if indicated by special values.\n    frameLength = (b1 and B1_MASK_LENGTH).toLong()\n    if (frameLength == PAYLOAD_SHORT.toLong()) {\n      frameLength = (source.readShort() and 0xffff).toLong() // Value is unsigned.\n    } else if (frameLength == PAYLOAD_LONG.toLong()) {\n      frameLength = source.readLong()\n      if (frameLength < 0L) {\n        throw ProtocolException(\n          \"Frame length 0x${frameLength.toHexString()} > 0x7FFFFFFFFFFFFFFF\",\n        )\n      }\n    }\n\n    if (isControlFrame && frameLength > PAYLOAD_BYTE_MAX) {\n      throw ProtocolException(\"Control frame must be less than ${PAYLOAD_BYTE_MAX}B.\")\n    }\n\n    if (isMasked) {\n      // Read the masking key as bytes so that they can be used directly for unmasking.\n      source.readFully(maskKey!!)\n    }\n  }\n\n  @Throws(IOException::class)\n  private fun readControlFrame() {\n    if (frameLength > 0L) {\n      source.readFully(controlFrameBuffer, frameLength)\n\n      if (!isClient) {\n        controlFrameBuffer.readAndWriteUnsafe(maskCursor!!)\n        maskCursor.seek(0)\n        toggleMask(maskCursor, maskKey!!)\n        maskCursor.close()\n      }\n    }\n\n    when (opcode) {\n      OPCODE_CONTROL_PING -> {\n        frameCallback.onReadPing(controlFrameBuffer.readByteString())\n      }\n\n      OPCODE_CONTROL_PONG -> {\n        frameCallback.onReadPong(controlFrameBuffer.readByteString())\n      }\n\n      OPCODE_CONTROL_CLOSE -> {\n        var code = CLOSE_NO_STATUS_CODE\n        var reason = \"\"\n        val bufferSize = controlFrameBuffer.size\n        if (bufferSize == 1L) {\n          throw ProtocolException(\"Malformed close payload length of 1.\")\n        } else if (bufferSize != 0L) {\n          code = controlFrameBuffer.readShort().toInt()\n          reason = controlFrameBuffer.readUtf8()\n          val codeExceptionMessage = WebSocketProtocol.closeCodeExceptionMessage(code)\n          if (codeExceptionMessage != null) throw ProtocolException(codeExceptionMessage)\n        }\n        frameCallback.onReadClose(code, reason)\n        receivedCloseFrame = true\n      }\n\n      else -> {\n        throw ProtocolException(\"Unknown control opcode: \" + opcode.toHexString())\n      }\n    }\n  }\n\n  @Throws(IOException::class)\n  private fun readMessageFrame() {\n    val opcode = this.opcode\n    if (opcode != OPCODE_TEXT && opcode != OPCODE_BINARY) {\n      throw ProtocolException(\"Unknown opcode: ${opcode.toHexString()}\")\n    }\n\n    readMessage()\n\n    if (readingCompressedMessage) {\n      val messageInflater =\n        this.messageInflater\n          ?: MessageInflater(noContextTakeover).also { this.messageInflater = it }\n      messageInflater.inflate(messageFrameBuffer)\n    }\n\n    if (opcode == OPCODE_TEXT) {\n      frameCallback.onReadMessage(messageFrameBuffer.readUtf8())\n    } else {\n      frameCallback.onReadMessage(messageFrameBuffer.readByteString())\n    }\n  }\n\n  /** Read headers and process any control frames until we reach a non-control frame. */\n  @Throws(IOException::class)\n  private fun readUntilNonControlFrame() {\n    while (!receivedCloseFrame) {\n      readHeader()\n      if (!isControlFrame) {\n        break\n      }\n      readControlFrame()\n    }\n  }\n\n  /**\n   * Reads a message body into across one or more frames. Control frames that occur between\n   * fragments will be processed. If the message payload is masked this will unmask as it's being\n   * processed.\n   */\n  @Throws(IOException::class)\n  private fun readMessage() {\n    while (true) {\n      if (receivedCloseFrame) throw IOException(\"closed\")\n\n      if (frameLength > 0L) {\n        source.readFully(messageFrameBuffer, frameLength)\n\n        if (!isClient) {\n          messageFrameBuffer.readAndWriteUnsafe(maskCursor!!)\n          maskCursor.seek(messageFrameBuffer.size - frameLength)\n          toggleMask(maskCursor, maskKey!!)\n          maskCursor.close()\n        }\n      }\n\n      if (isFinalFrame) break // We are exhausted and have no continuations.\n\n      readUntilNonControlFrame()\n      if (opcode != OPCODE_CONTINUATION) {\n        throw ProtocolException(\"Expected continuation opcode. Got: ${opcode.toHexString()}\")\n      }\n    }\n  }\n\n  @Throws(IOException::class)\n  override fun close() {\n    if (closed) return\n    closed = true\n    messageInflater?.closeQuietly()\n    source.closeQuietly()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/ws/WebSocketWriter.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.util.Random\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.ws.WebSocketProtocol.B0_FLAG_FIN\nimport okhttp3.internal.ws.WebSocketProtocol.B0_FLAG_RSV1\nimport okhttp3.internal.ws.WebSocketProtocol.B1_FLAG_MASK\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_CONTROL_CLOSE\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_CONTROL_PING\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_CONTROL_PONG\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_BYTE_MAX\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_LONG\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_SHORT\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_SHORT_MAX\nimport okhttp3.internal.ws.WebSocketProtocol.toggleMask\nimport okhttp3.internal.ws.WebSocketProtocol.validateCloseCode\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.ByteString\n\n/**\n * An [RFC 6455][rfc_6455]-compatible WebSocket frame writer.\n *\n * This class is not thread safe.\n *\n * [rfc_6455]: http://tools.ietf.org/html/rfc6455\n */\nclass WebSocketWriter(\n  private val isClient: Boolean,\n  val sink: BufferedSink,\n  val random: Random,\n  private val perMessageDeflate: Boolean,\n  private val noContextTakeover: Boolean,\n  private val minimumDeflateSize: Long,\n) : Closeable {\n  /** This holds outbound data for compression and masking. */\n  private val messageBuffer = Buffer()\n\n  /** The [Buffer] of [sink]. Write to this and then flush/emit [sink]. */\n  private val sinkBuffer: Buffer = sink.buffer\n  private var writerClosed = false\n\n  /** Lazily initialized on first use. */\n  private var messageDeflater: MessageDeflater? = null\n\n  // Masks are only a concern for client writers.\n  private val maskKey: ByteArray? = if (isClient) ByteArray(4) else null\n  private val maskCursor: Buffer.UnsafeCursor? = if (isClient) Buffer.UnsafeCursor() else null\n\n  /** Send a ping with the supplied [payload]. */\n  @Throws(IOException::class)\n  fun writePing(payload: ByteString) {\n    writeControlFrame(OPCODE_CONTROL_PING, payload)\n  }\n\n  /** Send a pong with the supplied [payload]. */\n  @Throws(IOException::class)\n  fun writePong(payload: ByteString) {\n    writeControlFrame(OPCODE_CONTROL_PONG, payload)\n  }\n\n  /**\n   * Send a close frame with optional code and reason.\n   *\n   * @param code Status code as defined by\n   *     [Section 7.4 of RFC 6455](http://tools.ietf.org/html/rfc6455#section-7.4) or `0`.\n   * @param reason Reason for shutting down or `null`.\n   */\n  @Throws(IOException::class)\n  fun writeClose(\n    code: Int,\n    reason: ByteString?,\n  ) {\n    var payload = ByteString.EMPTY\n    if (code != 0 || reason != null) {\n      if (code != 0) {\n        validateCloseCode(code)\n      }\n      payload =\n        Buffer().run {\n          writeShort(code)\n          if (reason != null) {\n            write(reason)\n          }\n          readByteString()\n        }\n    }\n\n    try {\n      writeControlFrame(OPCODE_CONTROL_CLOSE, payload)\n    } finally {\n      writerClosed = true\n    }\n  }\n\n  @Throws(IOException::class)\n  private fun writeControlFrame(\n    opcode: Int,\n    payload: ByteString,\n  ) {\n    if (writerClosed) throw IOException(\"closed\")\n\n    val length = payload.size\n    require(length <= PAYLOAD_BYTE_MAX) {\n      \"Payload size must be less than or equal to $PAYLOAD_BYTE_MAX\"\n    }\n\n    val b0 = B0_FLAG_FIN or opcode\n    sinkBuffer.writeByte(b0)\n\n    var b1 = length\n    if (isClient) {\n      b1 = b1 or B1_FLAG_MASK\n      sinkBuffer.writeByte(b1)\n\n      random.nextBytes(maskKey!!)\n      sinkBuffer.write(maskKey)\n\n      if (length > 0) {\n        val payloadStart = sinkBuffer.size\n        sinkBuffer.write(payload)\n\n        sinkBuffer.readAndWriteUnsafe(maskCursor!!)\n        maskCursor.seek(payloadStart)\n        toggleMask(maskCursor, maskKey)\n        maskCursor.close()\n      }\n    } else {\n      sinkBuffer.writeByte(b1)\n      sinkBuffer.write(payload)\n    }\n\n    sink.flush()\n  }\n\n  @Throws(IOException::class)\n  fun writeMessageFrame(\n    formatOpcode: Int,\n    data: ByteString,\n  ) {\n    if (writerClosed) throw IOException(\"closed\")\n\n    messageBuffer.write(data)\n\n    var b0 = formatOpcode or B0_FLAG_FIN\n    if (perMessageDeflate && data.size >= minimumDeflateSize) {\n      val messageDeflater =\n        this.messageDeflater\n          ?: MessageDeflater(noContextTakeover).also { this.messageDeflater = it }\n      messageDeflater.deflate(messageBuffer)\n      b0 = b0 or B0_FLAG_RSV1\n    }\n    val dataSize = messageBuffer.size\n    sinkBuffer.writeByte(b0)\n\n    var b1 = 0\n    if (isClient) {\n      b1 = b1 or B1_FLAG_MASK\n    }\n    when {\n      dataSize <= PAYLOAD_BYTE_MAX -> {\n        b1 = b1 or dataSize.toInt()\n        sinkBuffer.writeByte(b1)\n      }\n\n      dataSize <= PAYLOAD_SHORT_MAX -> {\n        b1 = b1 or PAYLOAD_SHORT\n        sinkBuffer.writeByte(b1)\n        sinkBuffer.writeShort(dataSize.toInt())\n      }\n\n      else -> {\n        b1 = b1 or PAYLOAD_LONG\n        sinkBuffer.writeByte(b1)\n        sinkBuffer.writeLong(dataSize)\n      }\n    }\n\n    if (isClient) {\n      random.nextBytes(maskKey!!)\n      sinkBuffer.write(maskKey)\n\n      if (dataSize > 0L) {\n        messageBuffer.readAndWriteUnsafe(maskCursor!!)\n        maskCursor.seek(0L)\n        toggleMask(maskCursor, maskKey)\n        maskCursor.close()\n      }\n    }\n\n    sinkBuffer.write(messageBuffer, dataSize)\n    sink.flush()\n  }\n\n  override fun close() {\n    messageDeflater?.closeQuietly()\n    sink.closeQuietly()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonJvmAndroid/kotlinTemplates/okhttp3/internal/-InternalVersion.kt",
    "content": "/*\n * Copyright (C) 2022 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3.internal\n\ninternal const val CONST_VERSION = \"$projectVersion\"\n"
  },
  {
    "path": "okhttp/src/commonTest/kotlin/okhttp3/CompressionInterceptorTest.kt",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.ResponseBody.Companion.asResponseBody\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okio.Buffer\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.GzipSink\nimport okio.Source\nimport okio.buffer\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass CompressionInterceptorTest {\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  val source =\n    Buffer().apply {\n      write(\"Hello World\".encodeUtf8())\n    } as Source\n\n  @Test\n  fun emptyDoesntChangeRequestOrResponse() {\n    val empty = CompressionInterceptor()\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .addInterceptor(empty)\n        .addInterceptor { chain ->\n          assertThat(chain.request().header(\"Accept-Encoding\")).isNull()\n          Response\n            .Builder()\n            .request(chain.request())\n            .protocol(Protocol.HTTP_1_1)\n            .code(200)\n            .message(\"OK\")\n            .body(\"Hello\".toResponseBody())\n            .header(\"Content-Encoding\", \"piedpiper\")\n            .build()\n        }.build()\n\n    val response = client.newCall(Request(\"https://google.com/robots.txt\".toHttpUrl())).execute()\n\n    assertThat(response.header(\"Content-Encoding\")).isEqualTo(\"piedpiper\")\n    assertThat(response.body.string()).isEqualTo(\"Hello\")\n  }\n\n  @Test\n  fun gzipThroughCall() {\n    val gzip = CompressionInterceptor(Gzip)\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .addInterceptor(gzip)\n        .addInterceptor { chain ->\n          assertThat(chain.request().header(\"Accept-Encoding\")).isEqualTo(\"gzip\")\n\n          Response\n            .Builder()\n            .request(chain.request())\n            .protocol(Protocol.HTTP_1_1)\n            .code(200)\n            .message(\"OK\")\n            .body(gzip(\"Hello\").asResponseBody())\n            .header(\"Content-Encoding\", \"gzip\")\n            .build()\n        }.build()\n\n    val response = client.newCall(Request(\"https://google.com/robots.txt\".toHttpUrl())).execute()\n\n    assertThat(response.header(\"Content-Encoding\")).isNull()\n    assertThat(response.body.string()).isEqualTo(\"Hello\")\n  }\n\n  private fun gzip(data: String): Buffer {\n    val result = Buffer()\n    val sink = GzipSink(result).buffer()\n    sink.writeUtf8(data)\n    sink.close()\n    return result\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonTest/kotlin/okhttp3/OkHttpTest.kt",
    "content": "package okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.matches\nimport org.junit.jupiter.api.Test\n\nclass OkHttpTest {\n  @Test\n  fun testVersion() {\n    assertThat(OkHttp.VERSION).matches(Regex(\"[0-9]+\\\\.[0-9]+\\\\.[0-9]+(-.+)?\"))\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonTest/kotlin/okhttp3/internal/IsProbablyUtf8Test.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isTrue\nimport okio.Buffer\nimport okio.Source\nimport okio.Timeout\nimport okio.buffer\nimport org.junit.jupiter.api.Test\n\nclass IsProbablyUtf8Test {\n  @Test fun isProbablyUtf8() {\n    assertThat(Buffer().isProbablyUtf8(16L)).isTrue()\n    assertThat(Buffer().writeUtf8(\"abc\").isProbablyUtf8(16L)).isTrue()\n    assertThat(Buffer().writeUtf8(\"new\\r\\nlines\").isProbablyUtf8(16L)).isTrue()\n    assertThat(Buffer().writeUtf8(\"white\\t space\").isProbablyUtf8(16L)).isTrue()\n    assertThat(Buffer().writeUtf8(\"Слава Україні!\").isProbablyUtf8(16L)).isTrue()\n    assertThat(Buffer().writeByte(0x80).isProbablyUtf8(16L)).isTrue()\n    assertThat(Buffer().writeByte(0x00).isProbablyUtf8(16L)).isFalse()\n    assertThat(Buffer().writeByte(0xc0).isProbablyUtf8(16L)).isFalse()\n  }\n\n  @Test fun doesNotConsumeBuffer() {\n    val buffer = Buffer()\n    buffer.writeUtf8(\"hello \".repeat(1024))\n    assertThat(buffer.isProbablyUtf8(100L)).isTrue()\n    assertThat(buffer.readUtf8()).isEqualTo(\"hello \".repeat(1024))\n  }\n\n  /** Confirm [isProbablyUtf8] doesn't attempt to read the entire stream. */\n  @Test fun doesNotReadEntireSource() {\n    val unlimitedSource =\n      object : Source {\n        override fun read(\n          sink: Buffer,\n          byteCount: Long,\n        ): Long {\n          sink.writeUtf8(\"a\".repeat(byteCount.toInt()))\n          return byteCount\n        }\n\n        override fun close() {\n        }\n\n        override fun timeout() = Timeout.NONE\n      }\n\n    assertThat(unlimitedSource.buffer().isProbablyUtf8(1L)).isTrue()\n    assertThat(unlimitedSource.buffer().isProbablyUtf8(1024L)).isTrue()\n    assertThat(unlimitedSource.buffer().isProbablyUtf8(1024L * 1024L)).isTrue()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonTest/kotlin/okhttp3/internal/publicsuffix/ConfiguredPublicSuffixDatabaseTest.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport okio.Buffer\nimport org.junit.jupiter.api.Test\n\nclass ConfiguredPublicSuffixDatabaseTest {\n  private val list = ConfiguredPublicSuffixList()\n  private val publicSuffixDatabase = PublicSuffixDatabase(list)\n\n  @Test fun longestMatchWins() {\n    list.bytes =\n      Buffer()\n        .writeUtf8(\"com\\n\")\n        .writeUtf8(\"my.square.com\\n\")\n        .writeUtf8(\"square.com\\n\")\n        .readByteString()\n\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"example.com\"))\n      .isEqualTo(\"example.com\")\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"foo.example.com\"))\n      .isEqualTo(\"example.com\")\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"foo.bar.square.com\"))\n      .isEqualTo(\"bar.square.com\")\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"foo.my.square.com\"))\n      .isEqualTo(\"foo.my.square.com\")\n  }\n\n  @Test fun wildcardMatch() {\n    list.bytes =\n      Buffer()\n        .writeUtf8(\"*.square.com\\n\")\n        .writeUtf8(\"com\\n\")\n        .writeUtf8(\"example.com\\n\")\n        .readByteString()\n\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"my.square.com\")).isNull()\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"foo.my.square.com\"))\n      .isEqualTo(\"foo.my.square.com\")\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"bar.foo.my.square.com\"))\n      .isEqualTo(\"foo.my.square.com\")\n  }\n\n  @Test fun boundarySearches() {\n    list.bytes =\n      Buffer()\n        .writeUtf8(\"bbb\\n\")\n        .writeUtf8(\"ddd\\n\")\n        .writeUtf8(\"fff\\n\")\n        .readByteString()\n\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"aaa\")).isNull()\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"ggg\")).isNull()\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"ccc\")).isNull()\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"eee\")).isNull()\n  }\n\n  @Test fun exceptionRule() {\n    list.bytes =\n      Buffer()\n        .writeUtf8(\"*.jp\\n\")\n        .writeUtf8(\"*.square.jp\\n\")\n        .writeUtf8(\"example.com\\n\")\n        .writeUtf8(\"square.com\\n\")\n        .readByteString()\n    list.exceptionBytes =\n      Buffer()\n        .writeUtf8(\"my.square.jp\\n\")\n        .readByteString()\n\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"my.square.jp\"))\n      .isEqualTo(\"my.square.jp\")\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"foo.my.square.jp\"))\n      .isEqualTo(\"my.square.jp\")\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"my1.square.jp\")).isNull()\n  }\n\n  @Test fun noEffectiveTldPlusOne() {\n    list.bytes =\n      Buffer()\n        .writeUtf8(\"*.jp\\n\")\n        .writeUtf8(\"*.square.jp\\n\")\n        .writeUtf8(\"example.com\\n\")\n        .writeUtf8(\"square.com\\n\")\n        .readByteString()\n    list.exceptionBytes =\n      Buffer()\n        .writeUtf8(\"my.square.jp\\n\")\n        .readByteString()\n\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"example.com\")).isNull()\n    assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(\"foo.square.jp\")).isNull()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonTest/kotlin/okhttp3/internal/publicsuffix/ConfiguredPublicSuffixList.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport okio.ByteString\n\n/**\n * An implementation of I/O for `PublicSuffixDatabase` by directly passing in ByteStrings.\n */\ninternal class ConfiguredPublicSuffixList : PublicSuffixList {\n  override fun ensureLoaded() {\n  }\n\n  override var bytes: ByteString = ByteString.EMPTY\n\n  override var exceptionBytes: ByteString = ByteString.EMPTY\n}\n"
  },
  {
    "path": "okhttp/src/commonTest/kotlin/okhttp3/internal/publicsuffix/PublicSuffixDatabaseTest.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\nimport okhttp3.internal.publicsuffix.ResourcePublicSuffixList.Companion.PUBLIC_SUFFIX_RESOURCE\nimport okhttp3.internal.toCanonicalHost\nimport okhttp3.okHttpRoot\nimport okio.Buffer\nimport okio.FileSystem\nimport okio.Path.Companion.toPath\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@RunWith(PublicSuffixTestRunner::class)\nclass PublicSuffixDatabaseTest {\n  private val publicSuffixDatabase = PublicSuffixDatabase.get()\n\n  val pathForTests = okHttpRoot / \"okhttp/src/jvmMain/resources\" / PUBLIC_SUFFIX_RESOURCE\n\n  @Before\n  fun setUp() {\n    beforePublicSuffixTest()\n  }\n\n  @Test\n  fun allPublicSuffixes() {\n    val buffer = Buffer()\n    FileSystem.SYSTEM.read(pathForTests) {\n      val length = readInt()\n      buffer.write(this, length.toLong())\n    }\n    while (!buffer.exhausted()) {\n      var publicSuffix = buffer.readUtf8LineStrict()\n      if (publicSuffix.contains(\"*\")) {\n        // A wildcard rule, let's replace the wildcard with a value.\n        publicSuffix = publicSuffix.replace(\"\\\\*\".toRegex(), \"square\")\n      }\n      assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(publicSuffix)).isNull()\n      val test = \"foobar.$publicSuffix\"\n      assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(test)).isEqualTo(test)\n    }\n  }\n\n  @Test\n  fun publicSuffixExceptions() {\n    val buffer = Buffer()\n    FileSystem.SYSTEM.read(pathForTests) {\n      var length = readInt()\n      skip(length.toLong())\n      length = readInt()\n      buffer.write(this, length.toLong())\n    }\n    while (!buffer.exhausted()) {\n      val exception = buffer.readUtf8LineStrict()\n      assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(exception)).isEqualTo(\n        exception,\n      )\n      val test = \"foobar.$exception\"\n      assertThat(publicSuffixDatabase.getEffectiveTldPlusOne(test)).isEqualTo(exception)\n    }\n  }\n\n  @Test\n  fun threadIsInterruptedOnFirstRead() {\n    Thread.currentThread().interrupt()\n    try {\n      val result = publicSuffixDatabase.getEffectiveTldPlusOne(\"squareup.com\")\n      assertThat(result).isEqualTo(\"squareup.com\")\n    } finally {\n      assertThat(Thread.interrupted()).isTrue()\n    }\n  }\n\n  @Test\n  fun secondReadFailsSameAsFirst() {\n    val badPublicSuffixDatabase =\n      PublicSuffixDatabase(\n        ResourcePublicSuffixList(\n          path = \"/xxx.gz\".toPath(),\n        ),\n      )\n    lateinit var firstFailure: Exception\n    assertFailsWith<Exception> {\n      badPublicSuffixDatabase.getEffectiveTldPlusOne(\"squareup.com\")\n    }.also { e ->\n      firstFailure = e\n    }\n    assertFailsWith<Exception> {\n      badPublicSuffixDatabase.getEffectiveTldPlusOne(\"squareup.com\")\n    }.also { e ->\n      assertEquals(firstFailure.toString(), e.toString())\n    }\n  }\n\n  /** These tests are provided by [publicsuffix.org](https://publicsuffix.org/list/). */\n  @Test\n  fun publicSuffixDotOrgTestCases() {\n    // Any copyright is dedicated to the Public Domain.\n    // https://creativecommons.org/publicdomain/zero/1.0/\n\n    // Mixed case.\n    checkPublicSuffix(\"COM\", null)\n    checkPublicSuffix(\"example.COM\", \"example.com\")\n    checkPublicSuffix(\"WwW.example.COM\", \"example.com\")\n    // Leading dot.\n    checkPublicSuffix(\".com\", null)\n    checkPublicSuffix(\".example\", null)\n    checkPublicSuffix(\".example.com\", null)\n    checkPublicSuffix(\".example.example\", null)\n    // Unlisted TLD.\n    checkPublicSuffix(\"example\", null)\n    checkPublicSuffix(\"example.example\", \"example.example\")\n    checkPublicSuffix(\"b.example.example\", \"example.example\")\n    checkPublicSuffix(\"a.b.example.example\", \"example.example\")\n    // Listed, but non-Internet, TLD.\n    // checkPublicSuffix(\"local\", null);\n    // checkPublicSuffix(\"example.local\", null);\n    // checkPublicSuffix(\"b.example.local\", null);\n    // checkPublicSuffix(\"a.b.example.local\", null);\n    // TLD with only 1 rule.\n    checkPublicSuffix(\"biz\", null)\n    checkPublicSuffix(\"domain.biz\", \"domain.biz\")\n    checkPublicSuffix(\"b.domain.biz\", \"domain.biz\")\n    checkPublicSuffix(\"a.b.domain.biz\", \"domain.biz\")\n    // TLD with some 2-level rules.\n    checkPublicSuffix(\"com\", null)\n    checkPublicSuffix(\"example.com\", \"example.com\")\n    checkPublicSuffix(\"b.example.com\", \"example.com\")\n    checkPublicSuffix(\"a.b.example.com\", \"example.com\")\n    checkPublicSuffix(\"uk.com\", null)\n    checkPublicSuffix(\"example.uk.com\", \"example.uk.com\")\n    checkPublicSuffix(\"b.example.uk.com\", \"example.uk.com\")\n    checkPublicSuffix(\"a.b.example.uk.com\", \"example.uk.com\")\n    checkPublicSuffix(\"test.ac\", \"test.ac\")\n    // TLD with only 1 (wildcard) rule.\n    checkPublicSuffix(\"mm\", null)\n    checkPublicSuffix(\"c.mm\", null)\n    checkPublicSuffix(\"b.c.mm\", \"b.c.mm\")\n    checkPublicSuffix(\"a.b.c.mm\", \"b.c.mm\")\n    // More complex TLD.\n    checkPublicSuffix(\"jp\", null)\n    checkPublicSuffix(\"test.jp\", \"test.jp\")\n    checkPublicSuffix(\"www.test.jp\", \"test.jp\")\n    checkPublicSuffix(\"ac.jp\", null)\n    checkPublicSuffix(\"test.ac.jp\", \"test.ac.jp\")\n    checkPublicSuffix(\"www.test.ac.jp\", \"test.ac.jp\")\n    checkPublicSuffix(\"kyoto.jp\", null)\n    checkPublicSuffix(\"test.kyoto.jp\", \"test.kyoto.jp\")\n    checkPublicSuffix(\"ide.kyoto.jp\", null)\n    checkPublicSuffix(\"b.ide.kyoto.jp\", \"b.ide.kyoto.jp\")\n    checkPublicSuffix(\"a.b.ide.kyoto.jp\", \"b.ide.kyoto.jp\")\n    checkPublicSuffix(\"c.kobe.jp\", null)\n    checkPublicSuffix(\"b.c.kobe.jp\", \"b.c.kobe.jp\")\n    checkPublicSuffix(\"a.b.c.kobe.jp\", \"b.c.kobe.jp\")\n    checkPublicSuffix(\"city.kobe.jp\", \"city.kobe.jp\")\n    checkPublicSuffix(\"www.city.kobe.jp\", \"city.kobe.jp\")\n    // TLD with a wildcard rule and exceptions.\n    checkPublicSuffix(\"ck\", null)\n    checkPublicSuffix(\"test.ck\", null)\n    checkPublicSuffix(\"b.test.ck\", \"b.test.ck\")\n    checkPublicSuffix(\"a.b.test.ck\", \"b.test.ck\")\n    checkPublicSuffix(\"www.ck\", \"www.ck\")\n    checkPublicSuffix(\"www.www.ck\", \"www.ck\")\n    // US K12.\n    checkPublicSuffix(\"us\", null)\n    checkPublicSuffix(\"test.us\", \"test.us\")\n    checkPublicSuffix(\"www.test.us\", \"test.us\")\n    checkPublicSuffix(\"ak.us\", null)\n    checkPublicSuffix(\"test.ak.us\", \"test.ak.us\")\n    checkPublicSuffix(\"www.test.ak.us\", \"test.ak.us\")\n    checkPublicSuffix(\"k12.ak.us\", null)\n    checkPublicSuffix(\"test.k12.ak.us\", \"test.k12.ak.us\")\n    checkPublicSuffix(\"www.test.k12.ak.us\", \"test.k12.ak.us\")\n    // IDN labels.\n    checkPublicSuffix(\"食狮.com.cn\", \"食狮.com.cn\")\n    checkPublicSuffix(\"食狮.公司.cn\", \"食狮.公司.cn\")\n    checkPublicSuffix(\"www.食狮.公司.cn\", \"食狮.公司.cn\")\n    checkPublicSuffix(\"shishi.公司.cn\", \"shishi.公司.cn\")\n    checkPublicSuffix(\"公司.cn\", null)\n    checkPublicSuffix(\"食狮.中国\", \"食狮.中国\")\n    checkPublicSuffix(\"www.食狮.中国\", \"食狮.中国\")\n    checkPublicSuffix(\"shishi.中国\", \"shishi.中国\")\n    checkPublicSuffix(\"中国\", null)\n    // Same as above, but punycoded.\n    checkPublicSuffix(\"xn--85x722f.com.cn\", \"xn--85x722f.com.cn\")\n    checkPublicSuffix(\"xn--85x722f.xn--55qx5d.cn\", \"xn--85x722f.xn--55qx5d.cn\")\n    checkPublicSuffix(\"www.xn--85x722f.xn--55qx5d.cn\", \"xn--85x722f.xn--55qx5d.cn\")\n    checkPublicSuffix(\"shishi.xn--55qx5d.cn\", \"shishi.xn--55qx5d.cn\")\n    checkPublicSuffix(\"xn--55qx5d.cn\", null)\n    checkPublicSuffix(\"xn--85x722f.xn--fiqs8s\", \"xn--85x722f.xn--fiqs8s\")\n    checkPublicSuffix(\"www.xn--85x722f.xn--fiqs8s\", \"xn--85x722f.xn--fiqs8s\")\n    checkPublicSuffix(\"shishi.xn--fiqs8s\", \"shishi.xn--fiqs8s\")\n    checkPublicSuffix(\"xn--fiqs8s\", null)\n  }\n\n  private fun checkPublicSuffix(\n    domain: String,\n    registrablePart: String?,\n  ) {\n    val canonicalDomain = domain.toCanonicalHost() ?: return\n    val result = publicSuffixDatabase.getEffectiveTldPlusOne(canonicalDomain)\n    if (registrablePart == null) {\n      assertThat(result).isNull()\n    } else {\n      assertThat(result).isEqualTo(registrablePart.toCanonicalHost())\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/commonTest/kotlin/okhttp3/internal/publicsuffix/PublicSuffixTesting.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport org.junit.runner.Runner\n\nexpect class PublicSuffixTestRunner : Runner\n\nexpect fun beforePublicSuffixTest()\n"
  },
  {
    "path": "okhttp/src/jvmMain/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3 {\n  requires transitive kotlin.stdlib;\n  requires transitive okio;\n  requires java.logging;\n  exports okhttp3;\n  exports okhttp3.internal to okhttp3.logging, okhttp3.sse, okhttp3.java.net.cookiejar, okhttp3.dnsoverhttps, mockwebserver3, okhttp3.mockwebserver, mockwebserver3.junit5, okhttp3.coroutines, okhttp3.tls;\n  exports okhttp3.internal.concurrent to mockwebserver3, okhttp3.mockwebserver;\n  exports okhttp3.internal.connection to mockwebserver3, okhttp3.mockwebserver, okhttp3.logging;\n  exports okhttp3.internal.http to okhttp3.logging, okhttp3.brotli, mockwebserver3;\n  exports okhttp3.internal.http2 to mockwebserver3, okhttp3.mockwebserver;\n  exports okhttp3.internal.platform to okhttp3.logging, okhttp3.java.net.cookiejar, okhttp3.dnsoverhttps, mockwebserver3, okhttp3.mockwebserver, okhttp3.tls;\n  exports okhttp3.internal.publicsuffix to okhttp3.dnsoverhttps;\n  exports okhttp3.internal.ws to mockwebserver3, okhttp3.mockwebserver;\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/OkHttp.jvm.kt",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okhttp3.internal.CONST_VERSION\n\nactual object OkHttp {\n  @JvmField\n  actual val VERSION: String = CONST_VERSION\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/internal/graal/GraalSvm.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.graal\n\nimport com.oracle.svm.core.annotate.Delete\nimport com.oracle.svm.core.annotate.Substitute\nimport com.oracle.svm.core.annotate.TargetClass\nimport okhttp3.internal.platform.BouncyCastlePlatform\nimport okhttp3.internal.platform.ConscryptPlatform\nimport okhttp3.internal.platform.Jdk8WithJettyBootPlatform\nimport okhttp3.internal.platform.Jdk9Platform\nimport okhttp3.internal.platform.OpenJSSEPlatform\nimport okhttp3.internal.platform.Platform\n\n@TargetClass(BouncyCastlePlatform::class)\n@Delete\nclass TargetBouncyCastlePlatform\n\n@TargetClass(ConscryptPlatform::class)\n@Delete\nclass TargetConscryptPlatform\n\n@TargetClass(Jdk8WithJettyBootPlatform::class)\n@Delete\nclass TargetJdk8WithJettyBootPlatform\n\n@TargetClass(OpenJSSEPlatform::class)\n@Delete\nclass TargetOpenJSSEPlatform\n\n@TargetClass(Platform.Companion::class)\nclass TargetPlatform {\n  @Substitute\n  fun findPlatform(): Platform = Jdk9Platform.buildIfSupported()!!\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/internal/graal/OkHttpFeature.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.internal.graal\n\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\nimport org.graalvm.nativeimage.hosted.Feature\n\n/**\n * Automatic configuration of OkHttp for native images.\n *\n * Currently, includes all necessary resources.\n */\nclass OkHttpFeature : Feature {\n  @IgnoreJRERequirement\n  override fun beforeAnalysis(access: Feature.BeforeAnalysisAccess?) = Unit\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/internal/platform/BouncyCastlePlatform.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport java.security.KeyStore\nimport java.security.Provider\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManagerFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Protocol\nimport org.bouncycastle.jsse.BCSSLSocket\nimport org.bouncycastle.jsse.provider.BouncyCastleJsseProvider\n\n/**\n * Platform using BouncyCastle if installed as the first Security Provider.\n *\n * Requires org.bouncycastle:bctls-jdk15on on the classpath.\n */\nclass BouncyCastlePlatform private constructor() : Platform() {\n  private val provider: Provider = BouncyCastleJsseProvider()\n\n  override fun newSSLContext(): SSLContext = SSLContext.getInstance(\"TLS\", provider)\n\n  override fun platformTrustManager(): X509TrustManager {\n    val factory =\n      TrustManagerFactory.getInstance(\n        \"PKIX\",\n        BouncyCastleJsseProvider.PROVIDER_NAME,\n      )\n    factory.init(null as KeyStore?)\n    val trustManagers = factory.trustManagers!!\n    check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) {\n      \"Unexpected default trust managers: ${trustManagers.contentToString()}\"\n    }\n    return trustManagers[0] as X509TrustManager\n  }\n\n  override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager =\n    throw UnsupportedOperationException(\n      \"clientBuilder.sslSocketFactory(SSLSocketFactory) not supported with BouncyCastle\",\n    )\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<@JvmSuppressWildcards Protocol>,\n  ) {\n    if (sslSocket is BCSSLSocket) {\n      val sslParameters = sslSocket.parameters\n\n      // Enable ALPN.\n      val names = alpnProtocolNames(protocols)\n      sslParameters.applicationProtocols = names.toTypedArray()\n\n      sslSocket.parameters = sslParameters\n    } else {\n      super.configureTlsExtensions(sslSocket, hostname, protocols)\n    }\n  }\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? =\n    if (sslSocket is BCSSLSocket) {\n      when (val protocol = (sslSocket as BCSSLSocket).applicationProtocol) {\n        // Handles both un-configured and none selected.\n        null, \"\" -> null\n\n        else -> protocol\n      }\n    } else {\n      super.getSelectedProtocol(sslSocket)\n    }\n\n  companion object {\n    val isSupported: Boolean =\n      try {\n        // Trigger an early exception over a fatal error, prefer a RuntimeException over Error.\n        Class.forName(\"org.bouncycastle.jsse.provider.BouncyCastleJsseProvider\", false, BouncyCastlePlatform::class.java.classLoader)\n\n        true\n      } catch (_: ClassNotFoundException) {\n        false\n      }\n\n    fun buildIfSupported(): BouncyCastlePlatform? = if (isSupported) BouncyCastlePlatform() else null\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/internal/platform/ConscryptPlatform.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport java.security.KeyStore\nimport java.security.Provider\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSession\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManager\nimport javax.net.ssl.TrustManagerFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Protocol\nimport org.conscrypt.Conscrypt\nimport org.conscrypt.ConscryptHostnameVerifier\n\n/**\n * Platform using Conscrypt (conscrypt.org) if installed as the first Security Provider.\n *\n * Requires org.conscrypt:conscrypt-openjdk-uber >= 2.1.0 on the classpath.\n */\nclass ConscryptPlatform private constructor() : Platform() {\n  private val provider: Provider = Conscrypt.newProvider()\n\n  // See release notes https://groups.google.com/forum/#!forum/conscrypt\n  // for version differences\n  override fun newSSLContext(): SSLContext =\n    // supports TLSv1.3 by default (version api is >= 1.4.0)\n    SSLContext.getInstance(\"TLS\", provider)\n\n  override fun platformTrustManager(): X509TrustManager {\n    val trustManagers =\n      TrustManagerFactory\n        .getInstance(TrustManagerFactory.getDefaultAlgorithm())\n        .apply {\n          init(null as KeyStore?)\n        }.trustManagers!!\n    check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) {\n      \"Unexpected default trust managers: ${trustManagers.contentToString()}\"\n    }\n    val x509TrustManager = trustManagers[0] as X509TrustManager\n    // Disabled because OkHttp will run anyway\n    Conscrypt.setHostnameVerifier(x509TrustManager, DisabledHostnameVerifier)\n    return x509TrustManager\n  }\n\n  internal object DisabledHostnameVerifier : ConscryptHostnameVerifier {\n    fun verify(\n      hostname: String?,\n      session: SSLSession?,\n    ): Boolean = true\n\n    override fun verify(\n      certs: Array<out X509Certificate>?,\n      hostname: String?,\n      session: SSLSession?,\n    ): Boolean = true\n  }\n\n  override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? = null\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<@JvmSuppressWildcards Protocol>,\n  ) {\n    if (Conscrypt.isConscrypt(sslSocket)) {\n      // Enable session tickets.\n      Conscrypt.setUseSessionTickets(sslSocket, true)\n\n      // Enable ALPN.\n      val names = alpnProtocolNames(protocols)\n      Conscrypt.setApplicationProtocols(sslSocket, names.toTypedArray())\n    } else {\n      super.configureTlsExtensions(sslSocket, hostname, protocols)\n    }\n  }\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? =\n    if (Conscrypt.isConscrypt(sslSocket)) {\n      Conscrypt.getApplicationProtocol(sslSocket)\n    } else {\n      super.getSelectedProtocol(sslSocket)\n    }\n\n  override fun newSslSocketFactory(trustManager: X509TrustManager): SSLSocketFactory =\n    newSSLContext()\n      .apply {\n        init(null, arrayOf<TrustManager>(trustManager), null)\n      }.socketFactory\n\n  companion object {\n    val isSupported: Boolean =\n      try {\n        // Trigger an early exception over a fatal error, prefer a RuntimeException over Error.\n        Class.forName(\"org.conscrypt.Conscrypt\\$Version\", false, ConscryptPlatform::class.java.classLoader)\n\n        when {\n          // Bump this version if we ever have a binary incompatibility\n          Conscrypt.isAvailable() && atLeastVersion(2, 1, 0) -> true\n\n          else -> false\n        }\n      } catch (e: NoClassDefFoundError) {\n        false\n      } catch (e: ClassNotFoundException) {\n        false\n      }\n\n    fun buildIfSupported(): ConscryptPlatform? = if (isSupported) ConscryptPlatform() else null\n\n    fun atLeastVersion(\n      major: Int,\n      minor: Int = 0,\n      patch: Int = 0,\n    ): Boolean {\n      val conscryptVersion = Conscrypt.version() ?: return false\n\n      if (conscryptVersion.major() != major) {\n        return conscryptVersion.major() > major\n      }\n\n      if (conscryptVersion.minor() != minor) {\n        return conscryptVersion.minor() > minor\n      }\n\n      return conscryptVersion.patch() >= patch\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/internal/platform/Jdk8WithJettyBootPlatform.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport java.lang.reflect.InvocationHandler\nimport java.lang.reflect.InvocationTargetException\nimport java.lang.reflect.Method\nimport java.lang.reflect.Proxy\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Protocol\n\n/** OpenJDK 8 with `org.mortbay.jetty.alpn:alpn-boot` in the boot class path. */\nclass Jdk8WithJettyBootPlatform(\n  private val putMethod: Method,\n  private val getMethod: Method,\n  private val removeMethod: Method,\n  private val clientProviderClass: Class<*>,\n  private val serverProviderClass: Class<*>,\n) : Platform() {\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<Protocol>,\n  ) {\n    val names = alpnProtocolNames(protocols)\n\n    try {\n      val alpnProvider =\n        Proxy.newProxyInstance(\n          Platform::class.java.classLoader,\n          arrayOf(clientProviderClass, serverProviderClass),\n          AlpnProvider(names),\n        )\n      putMethod.invoke(null, sslSocket, alpnProvider)\n    } catch (e: InvocationTargetException) {\n      throw AssertionError(\"failed to set ALPN\", e)\n    } catch (e: IllegalAccessException) {\n      throw AssertionError(\"failed to set ALPN\", e)\n    }\n  }\n\n  override fun afterHandshake(sslSocket: SSLSocket) {\n    try {\n      removeMethod.invoke(null, sslSocket)\n    } catch (e: IllegalAccessException) {\n      throw AssertionError(\"failed to remove ALPN\", e)\n    } catch (e: InvocationTargetException) {\n      throw AssertionError(\"failed to remove ALPN\", e)\n    }\n  }\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? {\n    try {\n      val provider = Proxy.getInvocationHandler(getMethod.invoke(null, sslSocket)) as AlpnProvider\n      if (!provider.unsupported && provider.selected == null) {\n        log(\"ALPN callback dropped: HTTP/2 is disabled. \" + \"Is alpn-boot on the boot class path?\")\n        return null\n      }\n      return if (provider.unsupported) null else provider.selected\n    } catch (e: InvocationTargetException) {\n      throw AssertionError(\"failed to get ALPN selected protocol\", e)\n    } catch (e: IllegalAccessException) {\n      throw AssertionError(\"failed to get ALPN selected protocol\", e)\n    }\n  }\n\n  /**\n   * Handle the methods of ALPN's ClientProvider and ServerProvider without a compile-time\n   * dependency on those interfaces.\n   */\n  private class AlpnProvider(\n    /** This peer's supported protocols. */\n    private val protocols: List<String>,\n  ) : InvocationHandler {\n    /** Set when remote peer notifies ALPN is unsupported. */\n    var unsupported: Boolean = false\n\n    /** The protocol the server selected. */\n    var selected: String? = null\n\n    @Throws(Throwable::class)\n    override fun invoke(\n      proxy: Any,\n      method: Method,\n      args: Array<Any>?,\n    ): Any? {\n      val callArgs = args ?: arrayOf<Any?>()\n      val methodName = method.name\n      val returnType = method.returnType\n      if (methodName == \"supports\" && Boolean::class.javaPrimitiveType == returnType) {\n        return true // ALPN is supported.\n      } else if (methodName == \"unsupported\" && Void.TYPE == returnType) {\n        this.unsupported = true // Peer doesn't support ALPN.\n        return null\n      } else if (methodName == \"protocols\" && callArgs.isEmpty()) {\n        return protocols // Client advertises these protocols.\n      } else if ((methodName == \"selectProtocol\" || methodName == \"select\") &&\n        String::class.java == returnType &&\n        callArgs.size == 1 &&\n        callArgs[0] is List<*>\n      ) {\n        val peerProtocols = callArgs[0] as List<*>\n        // Pick the first known protocol the peer advertises.\n        for (i in 0..peerProtocols.size) {\n          val protocol = peerProtocols[i] as String\n          if (protocol in protocols) {\n            selected = protocol\n            return selected\n          }\n        }\n        selected = protocols[0] // On no intersection, try peer's first protocol.\n        return selected\n      } else if ((methodName == \"protocolSelected\" || methodName == \"selected\") && callArgs.size == 1) {\n        this.selected = callArgs[0] as String // Server selected this protocol.\n        return null\n      } else {\n        return method.invoke(this, *callArgs)\n      }\n    }\n  }\n\n  companion object {\n    fun buildIfSupported(): Platform? {\n      val jvmVersion = System.getProperty(\"java.specification.version\", \"unknown\")\n      try {\n        // 1.8, 9, 10, 11, 12 etc\n        val version = jvmVersion.toInt()\n        if (version >= 9) return null\n      } catch (_: NumberFormatException) {\n        // expected on >= JDK 9\n      }\n\n      // Find Jetty's ALPN extension for OpenJDK.\n      try {\n        val alpnClassName = \"org.eclipse.jetty.alpn.ALPN\"\n        val alpnClass = Class.forName(alpnClassName, true, null)\n        val providerClass = Class.forName(\"$alpnClassName\\$Provider\", true, null)\n        val clientProviderClass = Class.forName(\"$alpnClassName\\$ClientProvider\", true, null)\n        val serverProviderClass = Class.forName(\"$alpnClassName\\$ServerProvider\", true, null)\n        val putMethod = alpnClass.getMethod(\"put\", SSLSocket::class.java, providerClass)\n        val getMethod = alpnClass.getMethod(\"get\", SSLSocket::class.java)\n        val removeMethod = alpnClass.getMethod(\"remove\", SSLSocket::class.java)\n        return Jdk8WithJettyBootPlatform(\n          putMethod,\n          getMethod,\n          removeMethod,\n          clientProviderClass,\n          serverProviderClass,\n        )\n      } catch (_: ClassNotFoundException) {\n      } catch (_: NoSuchMethodException) {\n      }\n\n      return null\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/internal/platform/OpenJSSEPlatform.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport java.security.KeyStore\nimport java.security.Provider\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManagerFactory\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.Protocol\n\n/**\n * Platform using OpenJSSE (https://github.com/openjsse/openjsse) if installed as the first\n * Security Provider.\n *\n * Requires org.openjsse:openjsse >= 1.1.0 on the classpath.\n */\nclass OpenJSSEPlatform private constructor() : Platform() {\n  private val provider: Provider =\n    org.openjsse.net.ssl\n      .OpenJSSE()\n\n  // Selects TLSv1.3 so we are specific about our intended version ranges (not just 1.3)\n  // and because it's a common pattern for VMs to have differences between supported and\n  // defaulted versions for TLS based on what is requested.\n  override fun newSSLContext(): SSLContext = SSLContext.getInstance(\"TLSv1.3\", provider)\n\n  override fun platformTrustManager(): X509TrustManager {\n    val factory =\n      TrustManagerFactory.getInstance(\n        TrustManagerFactory.getDefaultAlgorithm(),\n        provider,\n      )\n    factory.init(null as KeyStore?)\n    val trustManagers = factory.trustManagers!!\n    check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) {\n      \"Unexpected default trust managers: ${trustManagers.contentToString()}\"\n    }\n    return trustManagers[0] as X509TrustManager\n  }\n\n  override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager =\n    throw UnsupportedOperationException(\n      \"clientBuilder.sslSocketFactory(SSLSocketFactory) not supported with OpenJSSE\",\n    )\n\n  override fun configureTlsExtensions(\n    sslSocket: SSLSocket,\n    hostname: String?,\n    protocols: List<@JvmSuppressWildcards Protocol>,\n  ) {\n    if (sslSocket is org.openjsse.javax.net.ssl.SSLSocket) {\n      val sslParameters = sslSocket.sslParameters\n\n      if (sslParameters is org.openjsse.javax.net.ssl.SSLParameters) {\n        // Enable ALPN.\n        val names = alpnProtocolNames(protocols)\n        sslParameters.applicationProtocols = names.toTypedArray()\n\n        sslSocket.sslParameters = sslParameters\n      }\n    } else {\n      super.configureTlsExtensions(sslSocket, hostname, protocols)\n    }\n  }\n\n  override fun getSelectedProtocol(sslSocket: SSLSocket): String? =\n    if (sslSocket is org.openjsse.javax.net.ssl.SSLSocket) {\n      when (val protocol = sslSocket.applicationProtocol) {\n        // Handles both un-configured and none selected.\n        null, \"\" -> null\n\n        else -> protocol\n      }\n    } else {\n      super.getSelectedProtocol(sslSocket)\n    }\n\n  companion object {\n    val isSupported: Boolean =\n      try {\n        // Trigger an early exception over a fatal error, prefer a RuntimeException over Error.\n        Class.forName(\"org.openjsse.net.ssl.OpenJSSE\", false, OpenJSSEPlatform::class.java.classLoader)\n\n        true\n      } catch (_: ClassNotFoundException) {\n        false\n      }\n\n    fun buildIfSupported(): OpenJSSEPlatform? = if (isSupported) OpenJSSEPlatform() else null\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/internal/platform/PlatformRegistry.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport java.security.Security\n\nactual object PlatformRegistry {\n  private val isConscryptPreferred: Boolean\n    get() {\n      val preferredProvider = Security.getProviders()[0].name\n      return \"Conscrypt\" == preferredProvider\n    }\n\n  private val isOpenJSSEPreferred: Boolean\n    get() {\n      val preferredProvider = Security.getProviders()[0].name\n      return \"OpenJSSE\" == preferredProvider\n    }\n\n  private val isBouncyCastlePreferred: Boolean\n    get() {\n      val preferredProvider = Security.getProviders()[0].name\n      return \"BC\" == preferredProvider\n    }\n\n  actual fun findPlatform(): Platform {\n    if (isConscryptPreferred) {\n      val conscrypt = ConscryptPlatform.buildIfSupported()\n\n      if (conscrypt != null) {\n        return conscrypt\n      }\n    }\n\n    if (isBouncyCastlePreferred) {\n      val bc = BouncyCastlePlatform.buildIfSupported()\n\n      if (bc != null) {\n        return bc\n      }\n    }\n\n    if (isOpenJSSEPreferred) {\n      val openJSSE = OpenJSSEPlatform.buildIfSupported()\n\n      if (openJSSE != null) {\n        return openJSSE\n      }\n    }\n\n    // An Oracle JDK 9 like OpenJDK, or JDK 8 251+.\n    val jdk9 = Jdk9Platform.buildIfSupported()\n\n    if (jdk9 != null) {\n      return jdk9\n    }\n\n    // An Oracle JDK 8 like OpenJDK, pre 251.\n    val jdkWithJettyBoot = Jdk8WithJettyBootPlatform.buildIfSupported()\n\n    if (jdkWithJettyBoot != null) {\n      return jdkWithJettyBoot\n    }\n\n    return Platform()\n  }\n\n  actual val isAndroid: Boolean\n    get() = false\n}\n"
  },
  {
    "path": "okhttp/src/jvmMain/kotlin/okhttp3/internal/publicsuffix/PublicSuffixList.jvm.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\ninternal actual val PublicSuffixList.Companion.Default: PublicSuffixList\n  get() = ResourcePublicSuffixList()\n"
  },
  {
    "path": "okhttp/src/jvmMain/resources/META-INF/native-image/okhttp/okhttp/native-image.properties",
    "content": "Args = -H:+AddAllCharsets --enable-http --enable-https --features=okhttp3.internal.graal.OkHttpFeature --report-unsupported-elements-at-runtime\n"
  },
  {
    "path": "okhttp/src/jvmMain/resources/META-INF/native-image/okhttp/okhttp/reflect-config.json",
    "content": "[\n  {\n    \"name\": \"kotlin.internal.jdk8.JDK8PlatformImplementations\",\n    \"allDeclaredConstructors\":true\n  },\n  {\n    \"name\": \"kotlin.KotlinVersion\",\n    \"allPublicMethods\": true,\n    \"allDeclaredFields\":true,\n    \"allDeclaredMethods\":true,\n    \"allDeclaredConstructors\":true\n  },\n  {\n    \"name\": \"kotlin.KotlinVersion[]\"\n  },\n  {\n    \"name\": \"kotlin.KotlinVersion$Companion\"\n  },\n  {\n    \"name\": \"kotlin.KotlinVersion$Companion[]\"\n  }\n]\n"
  },
  {
    "path": "okhttp/src/jvmMain/resources/META-INF/native-image/okhttp/okhttp/resource-config.json",
    "content": "{\n  \"resources\": [\n    {\"pattern\": \"okhttp3/internal/publicsuffix/PublicSuffixDatabase.list\"}\n  ]\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/java/okhttp3/CallJavaTest.java",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\npublic class CallJavaTest {\n  @RegisterExtension\n  OkHttpClientTestRule clientTestRule = new OkHttpClientTestRule();\n\n  private OkHttpClient client = clientTestRule.newClient();\n\n  @Test\n  public void tagsSeededFromRequest() {\n    Request request = new Request.Builder()\n      .url(HttpUrl.get(\"https://square.com/\"))\n      .tag(Integer.class, 5)\n      .tag(String.class, \"hello\")\n      .build();\n    Call call = client.newCall(request);\n\n    assertEquals(5, call.tag(Integer.class));\n    assertEquals(\"hello\", call.tag(String.class));\n    assertEquals(null, call.tag(Boolean.class));\n    assertEquals(null, call.tag(Object.class));\n  }\n\n  @Test\n  public void tagsCanBeComputed() {\n    Request request = new Request.Builder()\n      .url(HttpUrl.get(\"https://square.com/\"))\n      .build();\n    Call call = client.newCall(request);\n\n    assertEquals(\"a\", call.tag(String.class, () -> \"a\"));\n    assertEquals(\"a\", call.tag(String.class, () -> \"b\"));\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/AddressTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotEqualTo\nimport java.net.Proxy\nimport okhttp3.internal.http.RecordingProxySelector\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\n\nclass AddressTest {\n  private val factory =\n    TestValueFactory().apply {\n      uriHost = \"example.com\"\n      uriPort = 80\n    }\n\n  @AfterEach fun tearDown() {\n    factory.close()\n  }\n\n  @Test fun equalsAndHashcode() {\n    val a = factory.newAddress()\n    val b = factory.newAddress()\n    assertThat(b).isEqualTo(a)\n    assertThat(b.hashCode()).isEqualTo(a.hashCode())\n  }\n\n  @Test fun differentProxySelectorsAreDifferent() {\n    val a = factory.newAddress(proxySelector = RecordingProxySelector())\n    val b = factory.newAddress(proxySelector = RecordingProxySelector())\n    assertThat(b).isNotEqualTo(a)\n  }\n\n  @Test fun addressToString() {\n    val address = factory.newAddress()\n    assertThat(address.toString())\n      .isEqualTo(\"Address{example.com:80, proxySelector=RecordingProxySelector}\")\n  }\n\n  @Test fun addressWithProxyToString() {\n    val address = factory.newAddress(proxy = Proxy.NO_PROXY)\n    assertThat(address.toString())\n      .isEqualTo(\"Address{example.com:80, proxy=${Proxy.NO_PROXY}}\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/AutobahnTester.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicLong\nimport java.util.concurrent.atomic.AtomicReference\nimport okhttp3.internal.USER_AGENT\nimport okio.ByteString\n\n/**\n * Exercises the web socket implementation against the\n * [Autobahn Testsuite](http://autobahn.ws/testsuite/).\n */\nclass AutobahnTester {\n  val client = OkHttpClient()\n\n  private fun newWebSocket(\n    path: String,\n    listener: WebSocketListener,\n  ): WebSocket {\n    val request =\n      Request\n        .Builder()\n        .url(HOST + path)\n        .build()\n    return client.newWebSocket(request, listener)\n  }\n\n  fun run() {\n    try {\n      val count = getTestCount()\n      println(\"Test count: $count\")\n      for (number in 1..count) {\n        runTest(number, count)\n      }\n      updateReports()\n    } finally {\n      client.dispatcher.executorService.shutdown()\n    }\n  }\n\n  private fun runTest(\n    number: Long,\n    count: Long,\n  ) {\n    val latch = CountDownLatch(1)\n    val startNanos = AtomicLong()\n    newWebSocket(\n      \"/runCase?case=$number&agent=okhttp\",\n      object : WebSocketListener() {\n        override fun onOpen(\n          webSocket: WebSocket,\n          response: Response,\n        ) {\n          println(\"Executing test case $number/$count\")\n          startNanos.set(System.nanoTime())\n        }\n\n        override fun onMessage(\n          webSocket: WebSocket,\n          bytes: ByteString,\n        ) {\n          webSocket.send(bytes)\n        }\n\n        override fun onMessage(\n          webSocket: WebSocket,\n          text: String,\n        ) {\n          webSocket.send(text)\n        }\n\n        override fun onClosing(\n          webSocket: WebSocket,\n          code: Int,\n          reason: String,\n        ) {\n          webSocket.close(1000, null)\n          latch.countDown()\n        }\n\n        override fun onFailure(\n          webSocket: WebSocket,\n          t: Throwable,\n          response: Response?,\n        ) {\n          t.printStackTrace(System.out)\n          latch.countDown()\n        }\n      },\n    )\n\n    check(latch.await(30, TimeUnit.SECONDS)) { \"Timed out waiting for test $number to finish.\" }\n    val endNanos = System.nanoTime()\n    val tookMs = TimeUnit.NANOSECONDS.toMillis(endNanos - startNanos.get())\n    println(\"Took ${tookMs}ms\")\n  }\n\n  private fun getTestCount(): Long {\n    val latch = CountDownLatch(1)\n    val countRef = AtomicLong()\n    val failureRef = AtomicReference<Throwable>()\n\n    newWebSocket(\n      \"/getCaseCount\",\n      object : WebSocketListener() {\n        override fun onMessage(\n          webSocket: WebSocket,\n          text: String,\n        ) {\n          countRef.set(text.toLong())\n        }\n\n        override fun onClosing(\n          webSocket: WebSocket,\n          code: Int,\n          reason: String,\n        ) {\n          webSocket.close(1000, null)\n          latch.countDown()\n        }\n\n        override fun onFailure(\n          webSocket: WebSocket,\n          t: Throwable,\n          response: Response?,\n        ) {\n          failureRef.set(t)\n          latch.countDown()\n        }\n      },\n    )\n\n    check(latch.await(10, TimeUnit.SECONDS)) { \"Timed out waiting for count.\" }\n\n    val failure = failureRef.get()\n    if (failure != null) {\n      throw RuntimeException(failure)\n    }\n\n    return countRef.get()\n  }\n\n  private fun updateReports() {\n    val latch = CountDownLatch(1)\n    newWebSocket(\n      \"/updateReports?agent=$USER_AGENT\",\n      object : WebSocketListener() {\n        override fun onClosing(\n          webSocket: WebSocket,\n          code: Int,\n          reason: String,\n        ) {\n          webSocket.close(1000, null)\n          latch.countDown()\n        }\n\n        override fun onFailure(\n          webSocket: WebSocket,\n          t: Throwable,\n          response: Response?,\n        ) {\n          latch.countDown()\n        }\n      },\n    )\n\n    check(latch.await(10, TimeUnit.SECONDS)) { \"Timed out waiting for count.\" }\n  }\n\n  companion object {\n    private const val HOST = \"ws://localhost:9099\"\n\n    @JvmStatic fun main(args: Array<String>) {\n      AutobahnTester().run()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/BouncyCastleTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.TestUtil.assumeNetwork\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass BouncyCastleTest {\n  @JvmField @RegisterExtension\n  var platform = PlatformRule()\n\n  @JvmField @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n  var client = clientTestRule.newClient()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    OkHttpDebugLogging.enable(\"org.bouncycastle.jsse\")\n    platform.assumeBouncyCastle()\n  }\n\n  @Test\n  fun testMozilla() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://mozilla.org/robots.txt\").build()\n\n    client.newCall(request).execute().use {\n      assertThat(it.protocol).isEqualTo(Protocol.HTTP_2)\n      assertThat(it.handshake!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CacheControlJvmTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isSameInstanceAs\nimport assertk.assertions.isTrue\nimport java.util.concurrent.TimeUnit\nimport kotlin.test.assertFailsWith\nimport okhttp3.CacheControl.Companion.parse\nimport okhttp3.Headers.Companion.headersOf\nimport org.junit.jupiter.api.Test\n\nclass CacheControlJvmTest {\n  @Test\n  @Throws(Exception::class)\n  fun completeBuilder() {\n    val cacheControl =\n      CacheControl\n        .Builder()\n        .noCache()\n        .noStore()\n        .maxAge(1, TimeUnit.SECONDS)\n        .maxStale(2, TimeUnit.SECONDS)\n        .minFresh(3, TimeUnit.SECONDS)\n        .onlyIfCached()\n        .noTransform()\n        .immutable()\n        .build()\n    assertThat(cacheControl.toString()).isEqualTo(\n      \"no-cache, no-store, max-age=1, max-stale=2, min-fresh=3, only-if-cached, \" +\n        \"no-transform, immutable\",\n    )\n    assertThat(cacheControl.noCache).isTrue()\n    assertThat(cacheControl.noStore).isTrue()\n    assertThat(cacheControl.maxAgeSeconds).isEqualTo(1)\n    assertThat(cacheControl.maxStaleSeconds).isEqualTo(2)\n    assertThat(cacheControl.minFreshSeconds).isEqualTo(3)\n    assertThat(cacheControl.onlyIfCached).isTrue()\n    assertThat(cacheControl.noTransform).isTrue()\n    assertThat(cacheControl.immutable).isTrue()\n\n    // These members are accessible to response headers only.\n    assertThat(cacheControl.sMaxAgeSeconds).isEqualTo(-1)\n    assertThat(cacheControl.isPrivate).isFalse()\n    assertThat(cacheControl.isPublic).isFalse()\n    assertThat(cacheControl.mustRevalidate).isFalse()\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun parseEmpty() {\n    val cacheControl =\n      parse(\n        Headers.Builder().set(\"Cache-Control\", \"\").build(),\n      )\n    assertThat(cacheControl.toString()).isEqualTo(\"\")\n    assertThat(cacheControl.noCache).isFalse()\n    assertThat(cacheControl.noStore).isFalse()\n    assertThat(cacheControl.maxAgeSeconds).isEqualTo(-1)\n    assertThat(cacheControl.sMaxAgeSeconds).isEqualTo(-1)\n    assertThat(cacheControl.isPublic).isFalse()\n    assertThat(cacheControl.mustRevalidate).isFalse()\n    assertThat(cacheControl.maxStaleSeconds).isEqualTo(-1)\n    assertThat(cacheControl.minFreshSeconds).isEqualTo(-1)\n    assertThat(cacheControl.onlyIfCached).isFalse()\n    assertThat(cacheControl.mustRevalidate).isFalse()\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun parse() {\n    val header = (\n      \"no-cache, no-store, max-age=1, s-maxage=2, private, public, must-revalidate, \" +\n        \"max-stale=3, min-fresh=4, only-if-cached, no-transform\"\n    )\n    val cacheControl =\n      parse(\n        Headers\n          .Builder()\n          .set(\"Cache-Control\", header)\n          .build(),\n      )\n    assertThat(cacheControl.noCache).isTrue()\n    assertThat(cacheControl.noStore).isTrue()\n    assertThat(cacheControl.maxAgeSeconds).isEqualTo(1)\n    assertThat(cacheControl.sMaxAgeSeconds).isEqualTo(2)\n    assertThat(cacheControl.isPrivate).isTrue()\n    assertThat(cacheControl.isPublic).isTrue()\n    assertThat(cacheControl.mustRevalidate).isTrue()\n    assertThat(cacheControl.maxStaleSeconds).isEqualTo(3)\n    assertThat(cacheControl.minFreshSeconds).isEqualTo(4)\n    assertThat(cacheControl.onlyIfCached).isTrue()\n    assertThat(cacheControl.noTransform).isTrue()\n    assertThat(cacheControl.toString()).isEqualTo(header)\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun parseIgnoreCacheControlExtensions() {\n    // Example from http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.6\n    val header = \"private, community=\\\"UCI\\\"\"\n    val cacheControl =\n      parse(\n        Headers\n          .Builder()\n          .set(\"Cache-Control\", header)\n          .build(),\n      )\n    assertThat(cacheControl.noCache).isFalse()\n    assertThat(cacheControl.noStore).isFalse()\n    assertThat(cacheControl.maxAgeSeconds).isEqualTo(-1)\n    assertThat(cacheControl.sMaxAgeSeconds).isEqualTo(-1)\n    assertThat(cacheControl.isPrivate).isTrue()\n    assertThat(cacheControl.isPublic).isFalse()\n    assertThat(cacheControl.mustRevalidate).isFalse()\n    assertThat(cacheControl.maxStaleSeconds).isEqualTo(-1)\n    assertThat(cacheControl.minFreshSeconds).isEqualTo(-1)\n    assertThat(cacheControl.onlyIfCached).isFalse()\n    assertThat(cacheControl.noTransform).isFalse()\n    assertThat(cacheControl.immutable).isFalse()\n    assertThat(cacheControl.toString()).isEqualTo(header)\n  }\n\n  @Test\n  fun parseCacheControlAndPragmaAreCombined() {\n    val headers = headersOf(\"Cache-Control\", \"max-age=12\", \"Pragma\", \"must-revalidate\", \"Pragma\", \"public\")\n    val cacheControl = parse(headers)\n    assertThat(cacheControl.toString()).isEqualTo(\"max-age=12, public, must-revalidate\")\n  }\n\n  @Test\n  fun parseCacheControlHeaderValueIsRetained() {\n    val value = \"max-age=12\"\n    val headers = headersOf(\"Cache-Control\", value)\n    val cacheControl = parse(headers)\n    assertThat(cacheControl.toString()).isSameInstanceAs(value)\n  }\n\n  @Test\n  fun parseCacheControlHeaderValueInvalidatedByPragma() {\n    val headers =\n      headersOf(\n        \"Cache-Control\",\n        \"max-age=12\",\n        \"Pragma\",\n        \"must-revalidate\",\n      )\n    val cacheControl = parse(headers)\n    assertThat(cacheControl.toString()).isEqualTo(\"max-age=12, must-revalidate\")\n  }\n\n  @Test\n  fun parseCacheControlHeaderValueInvalidatedByTwoValues() {\n    val headers =\n      headersOf(\n        \"Cache-Control\",\n        \"max-age=12\",\n        \"Cache-Control\",\n        \"must-revalidate\",\n      )\n    val cacheControl = parse(headers)\n    assertThat(cacheControl.toString()).isEqualTo(\"max-age=12, must-revalidate\")\n  }\n\n  @Test\n  fun parsePragmaHeaderValueIsNotRetained() {\n    val headers = headersOf(\"Pragma\", \"must-revalidate\")\n    val cacheControl = parse(headers)\n    assertThat(cacheControl.toString()).isEqualTo(\"must-revalidate\")\n  }\n\n  @Test\n  fun computedHeaderValueIsCached() {\n    val cacheControl =\n      CacheControl\n        .Builder()\n        .maxAge(2, TimeUnit.DAYS)\n        .build()\n    assertThat(cacheControl.toString()).isEqualTo(\"max-age=172800\")\n    assertThat(cacheControl.toString()).isSameInstanceAs(cacheControl.toString())\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun timeDurationTruncatedToMaxValue() {\n    val cacheControl =\n      CacheControl\n        .Builder()\n        .maxAge(365 * 100, TimeUnit.DAYS) // Longer than Integer.MAX_VALUE seconds.\n        .build()\n    assertThat(cacheControl.maxAgeSeconds).isEqualTo(Int.MAX_VALUE)\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun secondsMustBeNonNegative() {\n    val builder = CacheControl.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.maxAge(-1, TimeUnit.SECONDS)\n    }\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun timePrecisionIsTruncatedToSeconds() {\n    val cacheControl =\n      CacheControl\n        .Builder()\n        .maxAge(4999, TimeUnit.MILLISECONDS)\n        .build()\n    assertThat(cacheControl.maxAgeSeconds).isEqualTo(4)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CacheControlTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isTrue\nimport kotlin.test.Test\nimport kotlin.time.Duration.Companion.seconds\n\nclass CacheControlTest {\n  @Test\n  @Throws(Exception::class)\n  fun emptyBuilderIsEmpty() {\n    val cacheControl = CacheControl.Builder().build()\n    assertThat(cacheControl.toString()).isEqualTo(\"\")\n    assertThat(cacheControl.noCache).isFalse()\n    assertThat(cacheControl.noStore).isFalse()\n    assertThat(cacheControl.maxAgeSeconds).isEqualTo(-1)\n    assertThat(cacheControl.sMaxAgeSeconds).isEqualTo(-1)\n    assertThat(cacheControl.isPrivate).isFalse()\n    assertThat(cacheControl.isPublic).isFalse()\n    assertThat(cacheControl.mustRevalidate).isFalse()\n    assertThat(cacheControl.maxStaleSeconds).isEqualTo(-1)\n    assertThat(cacheControl.minFreshSeconds).isEqualTo(-1)\n    assertThat(cacheControl.onlyIfCached).isFalse()\n    assertThat(cacheControl.mustRevalidate).isFalse()\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun completeBuilder() {\n    val cacheControl =\n      CacheControl\n        .Builder()\n        .noCache()\n        .noStore()\n        .maxAge(1.seconds)\n        .maxStale(2.seconds)\n        .minFresh(3.seconds)\n        .onlyIfCached()\n        .noTransform()\n        .immutable()\n        .build()\n    assertThat(cacheControl.toString()).isEqualTo(\n      \"no-cache, no-store, max-age=1, max-stale=2, min-fresh=3, only-if-cached, no-transform, immutable\",\n    )\n    assertThat(cacheControl.noCache).isTrue()\n    assertThat(cacheControl.noStore).isTrue()\n    assertThat(cacheControl.maxAgeSeconds).isEqualTo(1)\n    assertThat(cacheControl.maxStaleSeconds).isEqualTo(2)\n    assertThat(cacheControl.minFreshSeconds).isEqualTo(3)\n    assertThat(cacheControl.onlyIfCached).isTrue()\n    assertThat(cacheControl.noTransform).isTrue()\n    assertThat(cacheControl.immutable).isTrue()\n\n    // These members are accessible to response headers only.\n    assertThat(cacheControl.sMaxAgeSeconds).isEqualTo(-1)\n    assertThat(cacheControl.isPrivate).isFalse()\n    assertThat(cacheControl.isPublic).isFalse()\n    assertThat(cacheControl.mustRevalidate).isFalse()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CacheCorruptionTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.startsWith\nimport java.net.CookieManager\nimport java.net.ResponseCache\nimport java.text.DateFormat\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.Locale\nimport java.util.TimeZone\nimport java.util.concurrent.TimeUnit\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLSession\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.internal.buildCache\nimport okhttp3.java.net.cookiejar.JavaNetCookieJar\nimport okhttp3.okio.LoggingFilesystem\nimport okhttp3.testing.PlatformRule\nimport okio.Path.Companion.toPath\nimport okio.fakefilesystem.FakeFileSystem\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass CacheCorruptionTest {\n  var fileSystem = FakeFileSystem()\n\n  @JvmField\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @JvmField\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private lateinit var client: OkHttpClient\n  private lateinit var cache: Cache\n  private val nullHostnameVerifier = HostnameVerifier { _: String?, _: SSLSession? -> true }\n  private val cookieManager = CookieManager()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n    server.protocolNegotiationEnabled = false\n    val loggingFileSystem = LoggingFilesystem(fileSystem)\n    cache = buildCache(\"/cache/\".toPath(), Int.MAX_VALUE.toLong(), loggingFileSystem)\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .cache(cache)\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n  }\n\n  @AfterEach\n  fun tearDown() {\n    ResponseCache.setDefault(null)\n    if (this::cache.isInitialized) {\n      cache.delete()\n    }\n  }\n\n  @Test\n  fun corruptedCipher() {\n    val response =\n      testCorruptingCache {\n        corruptMetadata {\n          // mess with cipher suite\n          it.replace(\"TLS_\", \"SLT_\")\n        }\n      }\n\n    assertThat(response.body.string()).isEqualTo(\"ABC.1\") // cached\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(1)\n\n    assertThat(response.handshake!!.cipherSuite.javaName).startsWith(\"SLT_\")\n  }\n\n  @Test\n  fun truncatedMetadataEntry() {\n    val response =\n      testCorruptingCache {\n        corruptMetadata {\n          // truncate metadata to 1/4 of length\n          it.substring(0, it.length / 4)\n        }\n      }\n\n    assertThat(response.body.string()).isEqualTo(\"ABC.2\") // not cached\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(2)\n    assertThat(cache.hitCount()).isEqualTo(0)\n  }\n\n  @Test fun corruptedUrl() {\n    val response =\n      testCorruptingCache {\n        corruptMetadata {\n          // strip https scheme\n          it.substring(5)\n        }\n      }\n\n    assertThat(response.body.string()).isEqualTo(\"ABC.2\") // not cached\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(2)\n    assertThat(cache.hitCount()).isEqualTo(0)\n  }\n\n  private fun corruptMetadata(corruptor: (String) -> String) {\n    val metadataFile =\n      fileSystem.allPaths.find {\n        it.name.endsWith(\".0\")\n      }\n\n    if (metadataFile != null) {\n      val contents =\n        fileSystem.read(metadataFile) {\n          readUtf8()\n        }\n\n      fileSystem.write(metadataFile) {\n        writeUtf8(corruptor(contents))\n      }\n    }\n  }\n\n  private fun testCorruptingCache(corruptor: () -> Unit): Response {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse(\n        headers =\n          headersOf(\n            \"Last-Modified\",\n            formatDate(-1, TimeUnit.HOURS)!!,\n            \"Expires\",\n            formatDate(1, TimeUnit.HOURS)!!,\n          ),\n        body = \"ABC.1\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        headers =\n          headersOf(\n            \"Last-Modified\",\n            formatDate(-1, TimeUnit.HOURS)!!,\n            \"Expires\",\n            formatDate(1, TimeUnit.HOURS)!!,\n          ),\n        body = \"ABC.2\",\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(nullHostnameVerifier)\n        .build()\n    val request = Request(server.url(\"/\"))\n    val response1: Response = client.newCall(request).execute()\n    val bodySource = response1.body.source()\n    assertThat(bodySource.readUtf8()).isEqualTo(\"ABC.1\")\n\n    corruptor()\n\n    return client.newCall(request).execute()\n  }\n\n  /**\n   * @param delta the offset from the current date to use. Negative values yield dates in the past;\n   *     positive values yield dates in the future.\n   */\n  private fun formatDate(\n    delta: Long,\n    timeUnit: TimeUnit,\n  ): String? = formatDate(Date(System.currentTimeMillis() + timeUnit.toMillis(delta)))\n\n  private fun formatDate(date: Date): String? {\n    val rfc1123: DateFormat = SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss zzz\", Locale.US)\n    rfc1123.timeZone = TimeZone.getTimeZone(\"GMT\")\n    return rfc1123.format(date)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CacheTest.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.net.CookieManager\nimport java.net.HttpURLConnection\nimport java.net.ResponseCache\nimport java.text.DateFormat\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.Locale\nimport java.util.TimeZone\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicReference\nimport java.util.stream.Stream\nimport javax.net.ssl.HostnameVerifier\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.RecordedRequest\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Cache.Companion.key\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.internal.addHeaderLenient\nimport okhttp3.internal.cacheGet\nimport okhttp3.internal.platform.Platform.Companion.get\nimport okhttp3.java.net.cookiejar.JavaNetCookieJar\nimport okhttp3.testing.PlatformRule\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.FileSystem\nimport okio.ForwardingFileSystem\nimport okio.GzipSink\nimport okio.Path\nimport okio.Path.Companion.toPath\nimport okio.buffer\nimport okio.fakefilesystem.FakeFileSystem\nimport okio.use\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Tag(\"Slow\")\nclass CacheTest {\n  val fileSystem = FakeFileSystem()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @StartStop\n  private val server2 = MockWebServer()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private lateinit var client: OkHttpClient\n  private lateinit var cache: Cache\n  private val cookieManager = CookieManager()\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n    server.protocolNegotiationEnabled = false\n    fileSystem.emulateUnix()\n    cache = Cache(fileSystem, \"/cache/\".toPath(), Long.MAX_VALUE)\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .cache(cache)\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n  }\n\n  @AfterEach\n  fun tearDown() {\n    ResponseCache.setDefault(null)\n    if (this::cache.isInitialized) {\n      cache.delete()\n    }\n  }\n\n  /**\n   * Test that response caching is consistent with the RI and the spec.\n   * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4\n   */\n  @Test\n  fun responseCachingByResponseCode() {\n    // Test each documented HTTP/1.1 code, plus the first unused value in each range.\n    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html\n\n    // We can't test 100 because it's not really a response.\n    // assertCached(false, 100);\n    assertCached(false, 101)\n    assertCached(true, 200)\n    assertCached(false, 201)\n    assertCached(false, 202)\n    assertCached(true, 203)\n    assertCached(true, 204)\n    assertCached(false, 205)\n    assertCached(false, 206) // Electing to not cache partial responses\n    assertCached(false, 207)\n    assertCached(true, 300)\n    assertCached(true, 301)\n    assertCached(true, 302)\n    assertCached(false, 303)\n    assertCached(false, 304)\n    assertCached(false, 305)\n    assertCached(false, 306)\n    assertCached(true, 307)\n    assertCached(true, 308)\n    assertCached(false, 400)\n    assertCached(false, 401)\n    assertCached(false, 402)\n    assertCached(false, 403)\n    assertCached(true, 404)\n    assertCached(true, 405)\n    assertCached(false, 406)\n    assertCached(false, 408)\n    assertCached(false, 409)\n    // the HTTP spec permits caching 410s, but the RI doesn't.\n    assertCached(true, 410)\n    assertCached(false, 411)\n    assertCached(false, 412)\n    assertCached(false, 413)\n    assertCached(true, 414)\n    assertCached(false, 415)\n    assertCached(false, 416)\n    assertCached(false, 417)\n    assertCached(false, 418)\n    assertCached(false, 500)\n    assertCached(true, 501)\n    assertCached(false, 502)\n    assertCached(false, 503)\n    assertCached(false, 504)\n    assertCached(false, 505)\n    assertCached(false, 506)\n  }\n\n  @Test\n  fun responseCachingWith1xxInformationalResponse() {\n    assertSubsequentResponseCached(102, 200)\n    assertSubsequentResponseCached(103, 200)\n  }\n\n  private fun assertCached(\n    shouldWriteToCache: Boolean,\n    responseCode: Int,\n    method: String = \"GET\",\n  ) {\n    var expectedResponseCode = responseCode\n    val server = MockWebServer()\n    val builder =\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .code(responseCode)\n        .body(\"ABCDE\")\n        .addHeader(\"WWW-Authenticate: challenge\")\n    when (responseCode) {\n      HttpURLConnection.HTTP_PROXY_AUTH -> {\n        builder.addHeader(\"Proxy-Authenticate: Basic realm=\\\"protected area\\\"\")\n      }\n\n      HttpURLConnection.HTTP_UNAUTHORIZED -> {\n        builder.addHeader(\"WWW-Authenticate: Basic realm=\\\"protected area\\\"\")\n      }\n\n      HttpURLConnection.HTTP_NO_CONTENT, HttpURLConnection.HTTP_RESET -> {\n        builder.body(\"\") // We forbid bodies for 204 and 205.\n      }\n    }\n    server.enqueue(builder.build())\n    if (responseCode == HttpURLConnection.HTTP_CLIENT_TIMEOUT) {\n      // 408's are a bit of an outlier because we may repeat the request if we encounter this\n      // response code. In this scenario, there are 2 responses: the initial 408 and then the 200\n      // because of the retry. We just want to ensure the initial 408 isn't cached.\n      expectedResponseCode = 200\n      server.enqueue(\n        MockResponse\n          .Builder()\n          .setHeader(\"Cache-Control\", \"no-store\")\n          .body(\"FGHIJ\")\n          .build(),\n      )\n    }\n    server.start()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(method, null)\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(expectedResponseCode)\n\n    // Exhaust the content stream.\n    response.body.string()\n    val cached = cacheGet(cache, request)\n    if (shouldWriteToCache) {\n      assertThat(cached).isNotNull()\n      cached!!.body.close()\n    } else {\n      assertThat(cached).isNull()\n    }\n    server.close() // tearDown() isn't sufficient; this test starts multiple servers\n  }\n\n  private fun assertSubsequentResponseCached(\n    initialResponseCode: Int,\n    finalResponseCode: Int,\n  ) {\n    val server = MockWebServer()\n    val builder =\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .code(finalResponseCode)\n        .body(\"ABCDE\")\n        .addInformationalResponse(MockResponse(initialResponseCode))\n    server.enqueue(builder.build())\n    server.start()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(finalResponseCode)\n\n    // Exhaust the content stream.\n    response.body.string()\n    val cached = cacheGet(cache, request)\n    assertThat(cached).isNotNull()\n    cached!!.body.close()\n    server.close() // tearDown() isn't sufficient; this test starts multiple servers\n  }\n\n  @Test\n  fun responseCachingAndInputStreamSkipWithFixedLength() {\n    testResponseCaching(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun responseCachingAndInputStreamSkipWithChunkedEncoding() {\n    testResponseCaching(TransferKind.CHUNKED)\n  }\n\n  @Test\n  fun responseCachingAndInputStreamSkipWithNoLengthHeaders() {\n    testResponseCaching(TransferKind.END_OF_STREAM)\n  }\n\n  /**\n   * Skipping bytes in the input stream caused ResponseCache corruption.\n   * http://code.google.com/p/android/issues/detail?id=8175\n   */\n  private fun testResponseCaching(transferKind: TransferKind) {\n    val mockResponse =\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .status(\"HTTP/1.1 200 Fantastic\")\n    transferKind.setBody(mockResponse, \"I love puppies but hate spiders\", 1)\n    server.enqueue(mockResponse.build())\n\n    // Make sure that calling skip() doesn't omit bytes from the cache.\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request).execute()\n    val in1 = response1.body.source()\n    assertThat(in1.readUtf8(\"I love \".length.toLong())).isEqualTo(\"I love \")\n    in1.skip(\"puppies but hate \".length.toLong())\n    assertThat(in1.readUtf8(\"spiders\".length.toLong())).isEqualTo(\"spiders\")\n    assertThat(in1.exhausted()).isTrue()\n    in1.close()\n    assertThat(cache.writeSuccessCount()).isEqualTo(1)\n    assertThat(cache.writeAbortCount()).isEqualTo(0)\n    val response2 = client.newCall(request).execute()\n    val in2 = response2.body.source()\n    assertThat(in2.readUtf8(\"I love puppies but hate spiders\".length.toLong()))\n      .isEqualTo(\n        \"I love puppies but hate spiders\",\n      )\n    assertThat(response2.code).isEqualTo(200)\n    assertThat(response2.message).isEqualTo(\"Fantastic\")\n    assertThat(in2.exhausted()).isTrue()\n    in2.close()\n    assertThat(cache.writeSuccessCount()).isEqualTo(1)\n    assertThat(cache.writeAbortCount()).isEqualTo(0)\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.hitCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun secureResponseCaching() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .body(\"ABC\")\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(NULL_HOSTNAME_VERIFIER)\n        .build()\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request).execute()\n    val source = response1.body.source()\n    assertThat(source.readUtf8()).isEqualTo(\"ABC\")\n\n    // OpenJDK 6 fails on this line, complaining that the connection isn't open yet\n    val cipherSuite = response1.handshake!!.cipherSuite\n    val localCerts = response1.handshake!!.localCertificates\n    val serverCerts = response1.handshake!!.peerCertificates\n    val peerPrincipal = response1.handshake!!.peerPrincipal\n    val localPrincipal = response1.handshake!!.localPrincipal\n    val response2 = client.newCall(request).execute() // Cached!\n    assertThat(response2.body.string()).isEqualTo(\"ABC\")\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(1)\n    assertThat(response2.handshake!!.cipherSuite).isEqualTo(cipherSuite)\n    assertThat(response2.handshake!!.localCertificates).isEqualTo(localCerts)\n    assertThat(response2.handshake!!.peerCertificates).isEqualTo(serverCerts)\n    assertThat(response2.handshake!!.peerPrincipal).isEqualTo(peerPrincipal)\n    assertThat(response2.handshake!!.localPrincipal).isEqualTo(localPrincipal)\n  }\n\n  @Test\n  fun secureResponseCachingWithCorruption() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .body(\"ABC\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-5, TimeUnit.MINUTES))\n        .addHeader(\"Expires: \" + formatDate(2, TimeUnit.HOURS))\n        .body(\"DEF\")\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(NULL_HOSTNAME_VERIFIER)\n        .build()\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request).execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n    val cacheEntry =\n      fileSystem.allPaths\n        .stream()\n        .filter { e: Path -> e.name.endsWith(\".0\") }\n        .findFirst()\n        .orElseThrow { NoSuchElementException() }\n    corruptCertificate(cacheEntry)\n    val response2 = client.newCall(request).execute() // Not Cached!\n    assertThat(response2.body.string()).isEqualTo(\"DEF\")\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(2)\n    assertThat(cache.hitCount()).isEqualTo(0)\n  }\n\n  private fun corruptCertificate(cacheEntry: Path) {\n    var content = fileSystem.source(cacheEntry).buffer().readUtf8()\n    content = content.replace(\"MII\", \"!!!\")\n    fileSystem\n      .sink(cacheEntry)\n      .buffer()\n      .writeUtf8(content)\n      .close()\n  }\n\n  @Test\n  fun responseCachingAndRedirects() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .code(HttpURLConnection.HTTP_MOVED_PERM)\n        .addHeader(\"Location: /foo\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .body(\"ABC\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEF\")\n        .build(),\n    )\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request).execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n    val response2 = client.newCall(request).execute() // Cached!\n    assertThat(response2.body.string()).isEqualTo(\"ABC\")\n\n    // 2 requests + 2 redirects\n    assertThat(cache.requestCount()).isEqualTo(4)\n    assertThat(cache.networkCount()).isEqualTo(2)\n    assertThat(cache.hitCount()).isEqualTo(2)\n  }\n\n  @Test\n  fun redirectToCachedResult() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"ABC\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_PERM)\n        .addHeader(\"Location: /foo\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEF\")\n        .build(),\n    )\n    val request1 = Request.Builder().url(server.url(\"/foo\")).build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n    val recordedRequest1 = server.takeRequest()\n    assertThat(recordedRequest1.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n    assertThat(recordedRequest1.connectionIndex).isEqualTo(0)\n    assertThat(recordedRequest1.exchangeIndex).isEqualTo(0)\n    val request2 = Request.Builder().url(server.url(\"/bar\")).build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"ABC\")\n    val recordedRequest2 = server.takeRequest()\n    assertThat(recordedRequest2.requestLine).isEqualTo(\"GET /bar HTTP/1.1\")\n    assertThat(recordedRequest2.connectionIndex).isEqualTo(0)\n    assertThat(recordedRequest2.exchangeIndex).isEqualTo(1)\n\n    // an unrelated request should reuse the pooled connection\n    val request3 = Request.Builder().url(server.url(\"/baz\")).build()\n    val response3 = client.newCall(request3).execute()\n    assertThat(response3.body.string()).isEqualTo(\"DEF\")\n    val recordedRequest3 = server.takeRequest()\n    assertThat(recordedRequest3.requestLine).isEqualTo(\"GET /baz HTTP/1.1\")\n    assertThat(recordedRequest3.connectionIndex).isEqualTo(0)\n    assertThat(recordedRequest3.exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun getAndQueryRedirectToCachedResultIndependently() {\n    // GET responses\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"ABC\")\n        .build(),\n    )\n    // QUERY responses\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"DEF\")\n        .build(),\n    )\n\n    val requestGet1 =\n      Request\n        .Builder()\n        .url(server.url(\"/foo\"))\n        .get()\n        .build()\n    val response1 = client.newCall(requestGet1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n    val recordedRequest1 = server.takeRequest()\n    assertThat(recordedRequest1.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n\n    val requestQuery1 =\n      Request\n        .Builder()\n        .url(server.url(\"/foo\"))\n        .query(RequestBody.EMPTY)\n        .build()\n    val response2 = client.newCall(requestQuery1).execute()\n    assertThat(response2.body.string()).isEqualTo(\"DEF\")\n    val recordedRequest2 = server.takeRequest()\n    assertThat(recordedRequest2.requestLine).isEqualTo(\"QUERY /foo HTTP/1.1\")\n  }\n\n  @Test\n  fun queryRequestsCacheTheBodyWithCacheUrlOverride() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"ABC\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"DEF\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"DEFa\")\n        .build(),\n    )\n\n    val url = server.url(\"/same\")\n\n    // First QUERY request with body \"foo\"\n    val request1 =\n      Request\n        .Builder()\n        .url(url)\n        .query(\"foo\".toRequestBody())\n        .cacheUrlOverride(url.newBuilder().addQueryParameter(\"body\", \"foo\").build())\n        .build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n\n    // Second QUERY request with body \"bar\"\n    val request2 =\n      Request\n        .Builder()\n        .url(url)\n        .query(\"bar\".toRequestBody())\n        .cacheUrlOverride(url.newBuilder().addQueryParameter(\"body\", \"bar\").build())\n        .build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"DEF\")\n\n    // Third QUERY request with body \"bar\" but not cached\n    val request3 =\n      Request\n        .Builder()\n        .url(url)\n        .query(\"bar\".toRequestBody())\n        .build()\n    val response3 = client.newCall(request3).execute()\n    assertThat(response3.body.string()).isEqualTo(\"DEFa\")\n\n    // Fourth QUERY request with body \"foo\" again, should be cached and return \"ABC\"\n    val response1a = client.newCall(request1).execute()\n    assertThat(response1a.body.string()).isEqualTo(\"ABC\")\n\n    // Fifth QUERY request with body \"bar\" again, should be cached and return \"DEF\"\n    val response2a = client.newCall(request2).execute()\n    assertThat(response2a.body.string()).isEqualTo(\"DEF\")\n  }\n\n  @Test\n  fun oneshotBodyIsNotCachedForQueryRequest() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"ABC1\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"ABC2\")\n        .build(),\n    )\n\n    val url = server.url(\"/same\")\n\n    // QUERY request with body \"foo\"\n    val body = \"foo\"\n\n    val request1 =\n      Request\n        .Builder()\n        .url(url)\n        .query(body.toOneShotRequestBody())\n        .build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC1\")\n\n    // QUERY request with body \"foo\" again, should not be cached\n    val request2 =\n      Request\n        .Builder()\n        .url(url)\n        .query(body.toOneShotRequestBody())\n        .build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"ABC2\")\n\n    // Check that the cache did not store the response\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.hitCount()).isEqualTo(0)\n  }\n\n  private fun String.toOneShotRequestBody(): RequestBody =\n    object : RequestBody() {\n      val internalBody = Stream.of(this)\n\n      override fun isOneShot(): Boolean = true\n\n      override fun contentType(): MediaType? = \"application/text-plain\".toMediaTypeOrNull()\n\n      override fun writeTo(sink: BufferedSink) {\n        internalBody.forEach { item ->\n          sink.writeUtf8(this@toOneShotRequestBody)\n        }\n      }\n    }\n\n  @Test\n  fun secureResponseCachingAndRedirects() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .code(HttpURLConnection.HTTP_MOVED_PERM)\n        .addHeader(\"Location: /foo\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .body(\"ABC\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEF\")\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(NULL_HOSTNAME_VERIFIER)\n        .build()\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n    assertThat(response1.handshake!!.cipherSuite).isNotNull()\n\n    // Cached!\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"ABC\")\n    assertThat(response2.handshake!!.cipherSuite).isNotNull()\n\n    // 2 direct + 2 redirect = 4\n    assertThat(cache.requestCount()).isEqualTo(4)\n    assertThat(cache.hitCount()).isEqualTo(2)\n    assertThat(response2.handshake!!.cipherSuite).isEqualTo(\n      response1.handshake!!.cipherSuite,\n    )\n  }\n\n  /**\n   * We've had bugs where caching and cross-protocol redirects yield class cast exceptions internal\n   * to the cache because we incorrectly assumed that HttpsURLConnection was always HTTPS and\n   * HttpURLConnection was always HTTP; in practice redirects mean that each can do either.\n   *\n   * https://github.com/square/okhttp/issues/214\n   */\n  @Test\n  fun secureResponseCachingAndProtocolRedirects() {\n    server2.useHttps(handshakeCertificates.sslSocketFactory())\n    server2.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .body(\"ABC\")\n        .build(),\n    )\n    server2.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEF\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .code(HttpURLConnection.HTTP_MOVED_PERM)\n        .addHeader(\"Location: \" + server2.url(\"/\"))\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(NULL_HOSTNAME_VERIFIER)\n        .build()\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n\n    // Cached!\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"ABC\")\n\n    // 2 direct + 2 redirect = 4\n    assertThat(cache.requestCount()).isEqualTo(4)\n    assertThat(cache.hitCount()).isEqualTo(2)\n  }\n\n  @Test\n  fun foundCachedWithExpiresHeader() {\n    temporaryRedirectCachedWithCachingHeader(302, \"Expires\", formatDate(1, TimeUnit.HOURS))\n  }\n\n  @Test\n  fun foundCachedWithCacheControlHeader() {\n    temporaryRedirectCachedWithCachingHeader(302, \"Cache-Control\", \"max-age=60\")\n  }\n\n  @Test\n  fun temporaryRedirectCachedWithExpiresHeader() {\n    temporaryRedirectCachedWithCachingHeader(307, \"Expires\", formatDate(1, TimeUnit.HOURS))\n  }\n\n  @Test\n  fun temporaryRedirectCachedWithCacheControlHeader() {\n    temporaryRedirectCachedWithCachingHeader(307, \"Cache-Control\", \"max-age=60\")\n  }\n\n  @Test\n  fun foundNotCachedWithoutCacheHeader() {\n    temporaryRedirectNotCachedWithoutCachingHeader(302)\n  }\n\n  @Test\n  fun temporaryRedirectNotCachedWithoutCacheHeader() {\n    temporaryRedirectNotCachedWithoutCachingHeader(307)\n  }\n\n  private fun temporaryRedirectCachedWithCachingHeader(\n    responseCode: Int,\n    headerName: String,\n    headerValue: String,\n  ) {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(responseCode)\n        .addHeader(headerName, headerValue)\n        .addHeader(\"Location\", \"/a\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(headerName, headerValue)\n        .body(\"a\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"b\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"c\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"a\")\n    assertThat(get(url).body.string()).isEqualTo(\"a\")\n  }\n\n  private fun temporaryRedirectNotCachedWithoutCachingHeader(responseCode: Int) {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(responseCode)\n        .addHeader(\"Location\", \"/a\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"b\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"a\")\n    assertThat(get(url).body.string()).isEqualTo(\"b\")\n  }\n\n  /** https://github.com/square/okhttp/issues/2198  */\n  @Test\n  fun cachedRedirect() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Location: /bar\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .build(),\n    )\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n    val request2 = Request.Builder().url(server.url(\"/\")).build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"ABC\")\n  }\n\n  @Test\n  fun serverDisconnectsPrematurelyWithContentLengthHeader() {\n    testServerPrematureDisconnect(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun serverDisconnectsPrematurelyWithChunkedEncoding() {\n    testServerPrematureDisconnect(TransferKind.CHUNKED)\n  }\n\n  @Test\n  fun serverDisconnectsPrematurelyWithNoLengthHeaders() {\n    // Intentionally empty. This case doesn't make sense because there's no\n    // such thing as a premature disconnect when the disconnect itself\n    // indicates the end of the data stream.\n  }\n\n  private fun testServerPrematureDisconnect(transferKind: TransferKind) {\n    val mockResponse = MockResponse.Builder()\n    transferKind.setBody(mockResponse, \"ABCDE\\nFGHIJKLMNOPQRSTUVWXYZ\", 16)\n    server.enqueue(truncateViolently(mockResponse, 16).build())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Request #2\")\n        .build(),\n    )\n    val bodySource = get(server.url(\"/\")).body.source()\n    assertThat(bodySource.readUtf8Line()).isEqualTo(\"ABCDE\")\n    bodySource.use {\n      assertFailsWith<IOException> {\n        bodySource.readUtf8(21)\n      }\n    }\n    assertThat(cache.writeAbortCount()).isEqualTo(1)\n    assertThat(cache.writeSuccessCount()).isEqualTo(0)\n    val response = get(server.url(\"/\"))\n    assertThat(response.body.string()).isEqualTo(\"Request #2\")\n    assertThat(cache.writeAbortCount()).isEqualTo(1)\n    assertThat(cache.writeSuccessCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun clientPrematureDisconnectWithContentLengthHeader() {\n    testClientPrematureDisconnect(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun clientPrematureDisconnectWithChunkedEncoding() {\n    testClientPrematureDisconnect(TransferKind.CHUNKED)\n  }\n\n  @Test\n  fun clientPrematureDisconnectWithNoLengthHeaders() {\n    testClientPrematureDisconnect(TransferKind.END_OF_STREAM)\n  }\n\n  private fun testClientPrematureDisconnect(transferKind: TransferKind) {\n    // Setting a low transfer speed ensures that stream discarding will time out.\n    val builder =\n      MockResponse\n        .Builder()\n        .throttleBody(6, 1, TimeUnit.SECONDS)\n    transferKind.setBody(builder, \"ABCDE\\nFGHIJKLMNOPQRSTUVWXYZ\", 1024)\n    server.enqueue(builder.build())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Request #2\")\n        .build(),\n    )\n    val response1 = get(server.url(\"/\"))\n    val source = response1.body.source()\n    assertThat(source.readUtf8(5)).isEqualTo(\"ABCDE\")\n    source.close()\n    assertFailsWith<IllegalStateException> {\n      source.readByte()\n    }\n    assertThat(cache.writeAbortCount()).isEqualTo(1)\n    assertThat(cache.writeSuccessCount()).isEqualTo(0)\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"Request #2\")\n    assertThat(cache.writeAbortCount()).isEqualTo(1)\n    assertThat(cache.writeSuccessCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun defaultExpirationDateFullyCachedForLessThan24Hours() {\n    //      last modified: 105 seconds ago\n    //             served:   5 seconds ago\n    //   default lifetime: (105 - 5) / 10 = 10 seconds\n    //            expires:  10 seconds from served date = 5 seconds from now\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-105, TimeUnit.SECONDS))\n        .addHeader(\"Date: \" + formatDate(-5, TimeUnit.SECONDS))\n        .body(\"A\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val response1 = get(url)\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    val response2 = get(url)\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    assertThat(response2.header(\"Warning\")).isNull()\n  }\n\n  @Test\n  fun defaultExpirationDateConditionallyCached() {\n    //      last modified: 115 seconds ago\n    //             served:  15 seconds ago\n    //   default lifetime: (115 - 15) / 10 = 10 seconds\n    //            expires:  10 seconds from served date = 5 seconds ago\n    val lastModifiedDate = formatDate(-115, TimeUnit.SECONDS)\n    val conditionalRequest =\n      assertConditionallyCached(\n        MockResponse\n          .Builder()\n          .addHeader(\"Last-Modified: $lastModifiedDate\")\n          .addHeader(\"Date: \" + formatDate(-15, TimeUnit.SECONDS))\n          .build(),\n      )\n    assertThat(conditionalRequest.headers[\"If-Modified-Since\"])\n      .isEqualTo(lastModifiedDate)\n  }\n\n  @Test\n  fun defaultExpirationDateFullyCachedForMoreThan24Hours() {\n    //      last modified: 105 days ago\n    //             served:   5 days ago\n    //   default lifetime: (105 - 5) / 10 = 10 days\n    //            expires:  10 days from served date = 5 days from now\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-105, TimeUnit.DAYS))\n        .addHeader(\"Date: \" + formatDate(-5, TimeUnit.DAYS))\n        .body(\"A\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val response = get(server.url(\"/\"))\n    assertThat(response.body.string()).isEqualTo(\"A\")\n    assertThat(response.header(\"Warning\")).isEqualTo(\n      \"113 HttpURLConnection \\\"Heuristic expiration\\\"\",\n    )\n  }\n\n  @Test\n  fun noDefaultExpirationForUrlsWithQueryString() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-105, TimeUnit.SECONDS))\n        .addHeader(\"Date: \" + formatDate(-5, TimeUnit.SECONDS))\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .addQueryParameter(\"foo\", \"bar\")\n        .build()\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun expirationDateInThePastWithLastModifiedHeader() {\n    val lastModifiedDate = formatDate(-2, TimeUnit.HOURS)\n    val conditionalRequest =\n      assertConditionallyCached(\n        MockResponse\n          .Builder()\n          .addHeader(\"Last-Modified: $lastModifiedDate\")\n          .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n          .build(),\n      )\n    assertThat(conditionalRequest.headers[\"If-Modified-Since\"])\n      .isEqualTo(lastModifiedDate)\n  }\n\n  @Test\n  fun expirationDateInThePastWithNoLastModifiedHeader() {\n    assertNotCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n        .build(),\n    )\n  }\n\n  @Test\n  fun expirationDateInTheFuture() {\n    assertFullyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .build(),\n    )\n  }\n\n  @Test\n  fun maxAgePreferredWithMaxAgeAndExpires() {\n    assertFullyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun maxAgeInThePastWithDateAndLastModifiedHeaders() {\n    val lastModifiedDate = formatDate(-2, TimeUnit.HOURS)\n    val conditionalRequest =\n      assertConditionallyCached(\n        MockResponse\n          .Builder()\n          .addHeader(\"Date: \" + formatDate(-120, TimeUnit.SECONDS))\n          .addHeader(\"Last-Modified: $lastModifiedDate\")\n          .addHeader(\"Cache-Control: max-age=60\")\n          .build(),\n      )\n    assertThat(conditionalRequest.headers[\"If-Modified-Since\"])\n      .isEqualTo(lastModifiedDate)\n  }\n\n  @Test\n  fun maxAgeInThePastWithDateHeaderButNoLastModifiedHeader() {\n    // Chrome interprets max-age relative to the local clock. Both our cache\n    // and Firefox both use the earlier of the local and server's clock.\n    assertNotCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Date: \" + formatDate(-120, TimeUnit.SECONDS))\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun maxAgeInTheFutureWithDateHeader() {\n    assertFullyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun maxAgeInTheFutureWithNoDateHeader() {\n    assertFullyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun maxAgeWithLastModifiedButNoServedDate() {\n    assertFullyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-120, TimeUnit.SECONDS))\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun maxAgeInTheFutureWithDateAndLastModifiedHeaders() {\n    assertFullyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-120, TimeUnit.SECONDS))\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.SECONDS))\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun maxAgePreferredOverLowerSharedMaxAge() {\n    assertFullyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Date: \" + formatDate(-2, TimeUnit.MINUTES))\n        .addHeader(\"Cache-Control: s-maxage=60\")\n        .addHeader(\"Cache-Control: max-age=180\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun maxAgePreferredOverHigherMaxAge() {\n    assertNotCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Date: \" + formatDate(-2, TimeUnit.MINUTES))\n        .addHeader(\"Cache-Control: s-maxage=180\")\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun requestMethodOptionsIsNotCached() {\n    testRequestMethod(\"OPTIONS\", false)\n  }\n\n  @Test\n  fun requestMethodGetIsCached() {\n    testRequestMethod(\"GET\", true)\n  }\n\n  @Test\n  fun requestMethodQueryIsCached() {\n    testRequestMethod(\"QUERY\", false)\n  }\n\n  @Test\n  fun requestMethodQueryIsCachedWithOverride() {\n    testRequestMethod(\"QUERY\", true, withOverride = true)\n  }\n\n  @Test\n  fun requestMethodHeadIsNotCached() {\n    // We could support this but choose not to for implementation simplicity\n    testRequestMethod(\"HEAD\", false)\n  }\n\n  @Test\n  fun requestMethodPostIsNotCached() {\n    // We could support this but choose not to for implementation simplicity\n    testRequestMethod(\"POST\", false)\n  }\n\n  @Test\n  fun requestMethodPostIsNotCachedUnlessOverridden() {\n    // Supported via cacheUrlOverride\n    testRequestMethod(\"POST\", true, withOverride = true)\n  }\n\n  @Test\n  fun requestMethodPutIsNotCached() {\n    testRequestMethod(\"PUT\", false)\n  }\n\n  @Test\n  fun requestMethodPutIsNotCachedEvenWithOverride() {\n    testRequestMethod(\"PUT\", false, withOverride = true)\n  }\n\n  @Test\n  fun requestMethodDeleteIsNotCached() {\n    testRequestMethod(\"DELETE\", false)\n  }\n\n  @Test\n  fun requestMethodTraceIsNotCached() {\n    testRequestMethod(\"TRACE\", false)\n  }\n\n  private fun testRequestMethod(\n    requestMethod: String,\n    expectCached: Boolean,\n    withOverride: Boolean = false,\n  ) {\n    // 1. Seed the cache (potentially).\n    // 2. Expect a cache hit or miss.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .addHeader(\"X-Response-ID: 1\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"X-Response-ID: 2\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .apply {\n          if (withOverride) {\n            cacheUrlOverride(url)\n          }\n        }.method(requestMethod, requestBodyOrNull(requestMethod))\n        .build()\n    val response1 = client.newCall(request).execute()\n    response1.body.close()\n    assertThat(response1.header(\"X-Response-ID\")).isEqualTo(\"1\")\n    val response2 = client.newCall(request).execute()\n    response2.body.close()\n    if (expectCached) {\n      assertThat(response2.header(\"X-Response-ID\")).isEqualTo(\"1\")\n    } else {\n      assertThat(response2.header(\"X-Response-ID\")).isEqualTo(\"2\")\n    }\n    if (!expectCached) {\n      server.enqueue(\n        MockResponse\n          .Builder()\n          .addHeader(\"X-Response-ID: 3\")\n          .build(),\n      )\n      val response3 = get(url)\n      response3.body.close()\n      assertThat(response3.header(\"X-Response-ID\")).isEqualTo(\"3\")\n    }\n  }\n\n  private fun requestBodyOrNull(requestMethod: String): RequestBody? =\n    if (requestMethod == \"POST\" ||\n      requestMethod == \"PUT\" ||\n      requestMethod == \"QUERY\"\n    ) {\n      \"foo\".toRequestBody(\"text/plain\".toMediaType())\n    } else {\n      null\n    }\n\n  @Test\n  fun postInvalidatesCache() {\n    testMethodInvalidates(\"POST\")\n  }\n\n  @Test\n  fun putInvalidatesCache() {\n    testMethodInvalidates(\"PUT\")\n  }\n\n  @Test\n  fun deleteMethodInvalidatesCache() {\n    testMethodInvalidates(\"DELETE\")\n  }\n\n  private fun testMethodInvalidates(requestMethod: String) {\n    // 1. Seed the cache.\n    // 2. Invalidate it.\n    // 3. Expect a cache miss.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"C\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .method(requestMethod, requestBodyOrNull(requestMethod))\n        .build()\n    val invalidate = client.newCall(request).execute()\n    assertThat(invalidate.body.string()).isEqualTo(\"B\")\n    assertThat(get(url).body.string()).isEqualTo(\"C\")\n  }\n\n  @Test\n  fun postInvalidatesCacheWithUncacheableResponse() {\n    // 1. Seed the cache.\n    // 2. Invalidate it with an uncacheable response.\n    // 3. Expect a cache miss.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .code(500)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"C\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .method(\"POST\", requestBodyOrNull(\"POST\"))\n        .build()\n    val invalidate = client.newCall(request).execute()\n    assertThat(invalidate.body.string()).isEqualTo(\"B\")\n    assertThat(get(url).body.string()).isEqualTo(\"C\")\n  }\n\n  @Test\n  fun putInvalidatesWithNoContentResponse() {\n    // 1. Seed the cache.\n    // 2. Invalidate it.\n    // 3. Expect a cache miss.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .code(HttpURLConnection.HTTP_NO_CONTENT)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"C\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .put(\"foo\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    val invalidate = client.newCall(request).execute()\n    assertThat(invalidate.body.string()).isEqualTo(\"\")\n    assertThat(get(url).body.string()).isEqualTo(\"C\")\n  }\n\n  @Test\n  fun etag() {\n    val conditionalRequest =\n      assertConditionallyCached(\n        MockResponse\n          .Builder()\n          .addHeader(\"ETag: v1\")\n          .build(),\n      )\n    assertThat(conditionalRequest.headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n  }\n\n  /** If both If-Modified-Since and If-None-Match conditions apply, send only If-None-Match.  */\n  @Test\n  fun etagAndExpirationDateInThePast() {\n    val lastModifiedDate = formatDate(-2, TimeUnit.HOURS)\n    val conditionalRequest =\n      assertConditionallyCached(\n        MockResponse\n          .Builder()\n          .addHeader(\"ETag: v1\")\n          .addHeader(\"Last-Modified: $lastModifiedDate\")\n          .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n          .build(),\n      )\n    assertThat(conditionalRequest.headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n    assertThat(conditionalRequest.headers[\"If-Modified-Since\"]).isNull()\n  }\n\n  @Test\n  fun etagAndExpirationDateInTheFuture() {\n    assertFullyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"ETag: v1\")\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .build(),\n    )\n  }\n\n  @Test\n  fun cacheControlNoCache() {\n    assertNotCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: no-cache\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun cacheControlNoCacheAndExpirationDateInTheFuture() {\n    val lastModifiedDate = formatDate(-2, TimeUnit.HOURS)\n    val conditionalRequest =\n      assertConditionallyCached(\n        MockResponse\n          .Builder()\n          .addHeader(\"Last-Modified: $lastModifiedDate\")\n          .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n          .addHeader(\"Cache-Control: no-cache\")\n          .build(),\n      )\n    assertThat(conditionalRequest.headers[\"If-Modified-Since\"])\n      .isEqualTo(lastModifiedDate)\n  }\n\n  @Test\n  fun pragmaNoCache() {\n    assertNotCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Pragma: no-cache\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun pragmaNoCacheAndExpirationDateInTheFuture() {\n    val lastModifiedDate = formatDate(-2, TimeUnit.HOURS)\n    val conditionalRequest =\n      assertConditionallyCached(\n        MockResponse\n          .Builder()\n          .addHeader(\"Last-Modified: $lastModifiedDate\")\n          .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n          .addHeader(\"Pragma: no-cache\")\n          .build(),\n      )\n    assertThat(conditionalRequest.headers[\"If-Modified-Since\"])\n      .isEqualTo(lastModifiedDate)\n  }\n\n  @Test\n  fun cacheControlNoStore() {\n    assertNotCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: no-store\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun cacheControlNoStoreAndExpirationDateInTheFuture() {\n    assertNotCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: no-store\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun partialRangeResponsesDoNotCorruptCache() {\n    // 1. Request a range.\n    // 2. Request a full document, expecting a cache miss.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"AA\")\n        .code(HttpURLConnection.HTTP_PARTIAL)\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .addHeader(\"Content-Range: bytes 1000-1001/2000\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"BB\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Range\", \"bytes=1000-1001\")\n        .build()\n    val range = client.newCall(request).execute()\n    assertThat(range.body.string()).isEqualTo(\"AA\")\n    assertThat(get(url).body.string()).isEqualTo(\"BB\")\n  }\n\n  /**\n   * When the server returns a full response body we will store it and return it regardless of what\n   * its Last-Modified date is. This behavior was different prior to OkHttp 3.5 when we would prefer\n   * the response with the later Last-Modified date.\n   *\n   * https://github.com/square/okhttp/issues/2886\n   */\n  @Test\n  fun serverReturnsDocumentOlderThanCache() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .addHeader(\"Last-Modified: \" + formatDate(-4, TimeUnit.HOURS))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun clientSideNoStore() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"B\")\n        .build(),\n    )\n    val request1 =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .cacheControl(CacheControl.Builder().noStore().build())\n        .build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    val request2 =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun nonIdentityEncodingAndConditionalCache() {\n    assertNonIdentityEncodingCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n        .build(),\n    )\n  }\n\n  @Test\n  fun nonIdentityEncodingAndFullCache() {\n    assertNonIdentityEncodingCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .build(),\n    )\n  }\n\n  private fun assertNonIdentityEncodingCached(response: MockResponse) {\n    server.enqueue(\n      response\n        .newBuilder()\n        .body(gzip(\"ABCABCABC\"))\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n\n    // At least three request/response pairs are required because after the first request is cached\n    // a different execution path might be taken. Thus modifications to the cache applied during\n    // the second request might not be visible until another request is performed.\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n  }\n\n  @Test\n  fun previouslyNotGzippedContentIsNotModifiedAndSpecifiesGzipEncoding() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABCABCABC\")\n        .addHeader(\"Content-Type: text/plain\")\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .addHeader(\"Content-Type: text/plain\")\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEFDEFDEF\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"DEFDEFDEF\")\n  }\n\n  @Test\n  fun changedGzippedContentIsNotModifiedAndSpecifiesNewEncoding() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(gzip(\"ABCABCABC\"))\n        .addHeader(\"Content-Type: text/plain\")\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .addHeader(\"Content-Type: text/plain\")\n        .addHeader(\"Content-Encoding: identity\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEFDEFDEF\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"DEFDEFDEF\")\n  }\n\n  @Test\n  fun notModifiedSpecifiesEncoding() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(gzip(\"ABCABCABC\"))\n        .addHeader(\"Content-Encoding: gzip\")\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(-1, TimeUnit.HOURS))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEFDEFDEF\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"DEFDEFDEF\")\n  }\n\n  /** https://github.com/square/okhttp/issues/947  */\n  @Test\n  fun gzipAndVaryOnAcceptEncoding() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(gzip(\"ABCABCABC\"))\n        .addHeader(\"Content-Encoding: gzip\")\n        .addHeader(\"Vary: Accept-Encoding\")\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"FAIL\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"ABCABCABC\")\n  }\n\n  @Test\n  fun conditionalCacheHitIsNotDoublePooled() {\n    clientTestRule.ensureAllConnectionsReleased()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"ETag: v1\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(client.connectionPool.idleConnectionCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun expiresDateBeforeModifiedDate() {\n    assertConditionallyCached(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(-2, TimeUnit.HOURS))\n        .build(),\n    )\n  }\n\n  @Test\n  fun requestMaxAge() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Last-Modified: \" + formatDate(-2, TimeUnit.HOURS))\n        .addHeader(\"Date: \" + formatDate(-1, TimeUnit.MINUTES))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"max-age=30\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun requestMinFresh() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.MINUTES))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"min-fresh=120\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun requestMaxStale() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=120\")\n        .addHeader(\"Date: \" + formatDate(-4, TimeUnit.MINUTES))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"max-stale=180\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"A\")\n    assertThat(response.header(\"Warning\")).isEqualTo(\n      \"110 HttpURLConnection \\\"Response is stale\\\"\",\n    )\n  }\n\n  @Test\n  fun requestMaxStaleDirectiveWithNoValue() {\n    // Add a stale response to the cache.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=120\")\n        .addHeader(\"Date: \" + formatDate(-4, TimeUnit.MINUTES))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n\n    // With max-stale, we'll return that stale response.\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"max-stale\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"A\")\n    assertThat(response.header(\"Warning\")).isEqualTo(\n      \"110 HttpURLConnection \\\"Response is stale\\\"\",\n    )\n  }\n\n  @Test\n  fun requestMaxStaleNotHonoredWithMustRevalidate() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=120, must-revalidate\")\n        .addHeader(\"Date: \" + formatDate(-4, TimeUnit.MINUTES))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"max-stale=180\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun requestOnlyIfCachedWithNoResponseCached() {\n    // (no responses enqueued)\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"only-if-cached\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.source().exhausted()).isTrue()\n    assertThat(response.code).isEqualTo(504)\n    assertThat(cache.requestCount()).isEqualTo(1)\n    assertThat(cache.networkCount()).isEqualTo(0)\n    assertThat(cache.hitCount()).isEqualTo(0)\n  }\n\n  @Test\n  fun requestOnlyIfCachedWithFullResponseCached() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=30\")\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.MINUTES))\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"only-if-cached\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun requestOnlyIfCachedWithConditionalResponseCached() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=30\")\n        .addHeader(\"Date: \" + formatDate(-1, TimeUnit.MINUTES))\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"only-if-cached\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.source().exhausted()).isTrue()\n    assertThat(response.code).isEqualTo(504)\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(0)\n  }\n\n  @Test\n  fun requestOnlyIfCachedWithUnhelpfulResponseCached() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"only-if-cached\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.source().exhausted()).isTrue()\n    assertThat(response.code).isEqualTo(504)\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(0)\n  }\n\n  @Test\n  fun requestCacheControlNoCache() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-120, TimeUnit.SECONDS))\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.SECONDS))\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Cache-Control\", \"no-cache\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun requestPragmaNoCache() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-120, TimeUnit.SECONDS))\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.SECONDS))\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Pragma\", \"no-cache\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun clientSuppliedIfModifiedSinceWithCachedResult() {\n    val response =\n      MockResponse\n        .Builder()\n        .addHeader(\"ETag: v3\")\n        .addHeader(\"Cache-Control: max-age=0\")\n        .build()\n    val ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS)\n    val request =\n      assertClientSuppliedCondition(response, \"If-Modified-Since\", ifModifiedSinceDate)\n    assertThat(request.headers[\"If-Modified-Since\"]).isEqualTo(ifModifiedSinceDate)\n    assertThat(request.headers[\"If-None-Match\"]).isNull()\n  }\n\n  @Test\n  fun clientSuppliedIfNoneMatchSinceWithCachedResult() {\n    val lastModifiedDate = formatDate(-3, TimeUnit.MINUTES)\n    val response =\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: $lastModifiedDate\")\n        .addHeader(\"Date: \" + formatDate(-2, TimeUnit.MINUTES))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .build()\n    val request = assertClientSuppliedCondition(response, \"If-None-Match\", \"v1\")\n    assertThat(request.headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n    assertThat(request.headers[\"If-Modified-Since\"]).isNull()\n  }\n\n  private fun assertClientSuppliedCondition(\n    seed: MockResponse,\n    conditionName: String,\n    conditionValue: String,\n  ): RecordedRequest {\n    server.enqueue(\n      seed\n        .newBuilder()\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .header(conditionName, conditionValue)\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(HttpURLConnection.HTTP_NOT_MODIFIED)\n    assertThat(response.body.string()).isEqualTo(\"\")\n    server.takeRequest() // seed\n    return server.takeRequest()\n  }\n\n  /**\n   * For Last-Modified and Date headers, we should echo the date back in the exact format we were\n   * served.\n   */\n  @Test\n  fun retainServedDateFormat() {\n    // Serve a response with a non-standard date format that OkHttp supports.\n    val lastModifiedDate = Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(-1))\n    val servedDate = Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(-2))\n    val dateFormat: DateFormat = SimpleDateFormat(\"EEE dd-MMM-yyyy HH:mm:ss z\", Locale.US)\n    dateFormat.timeZone = TimeZone.getTimeZone(\"America/New_York\")\n    val lastModifiedString = dateFormat.format(lastModifiedDate)\n    val servedString = dateFormat.format(servedDate)\n\n    // This response should be conditionally cached.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: $lastModifiedString\")\n        .addHeader(\"Expires: $servedString\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n\n    // The first request has no conditions.\n    val request1 = server.takeRequest()\n    assertThat(request1.headers[\"If-Modified-Since\"]).isNull()\n\n    // The 2nd request uses the server's date format.\n    val request2 = server.takeRequest()\n    assertThat(request2.headers[\"If-Modified-Since\"]).isEqualTo(lastModifiedString)\n  }\n\n  @Test\n  fun clientSuppliedConditionWithoutCachedResult() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"If-Modified-Since\", formatDate(-24, TimeUnit.HOURS))\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(HttpURLConnection.HTTP_NOT_MODIFIED)\n    assertThat(response.body.string()).isEqualTo(\"\")\n  }\n\n  @Test\n  fun authorizationRequestFullyCached() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Authorization\", \"password\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun contentLocationDoesNotPopulateCache() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Content-Location: /bar\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/foo\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/bar\")).body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun connectionIsReturnedToPoolAfterConditionalSuccess() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/a\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/a\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/b\")).body.string()).isEqualTo(\"B\")\n    val c0e0 = server.takeRequest()\n    assertThat(c0e0.connectionIndex).isEqualTo(0)\n    assertThat(c0e0.exchangeIndex).isEqualTo(0)\n    val c0e1 = server.takeRequest()\n    assertThat(c0e1.connectionIndex).isEqualTo(0)\n    assertThat(c0e1.exchangeIndex).isEqualTo(1)\n    val c0e2 = server.takeRequest()\n    assertThat(c0e2.connectionIndex).isEqualTo(0)\n    assertThat(c0e2.exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun statisticsConditionalCacheMiss() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"C\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(1)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(0)\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"B\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"C\")\n    assertThat(cache.requestCount()).isEqualTo(3)\n    assertThat(cache.networkCount()).isEqualTo(3)\n    assertThat(cache.hitCount()).isEqualTo(0)\n  }\n\n  @Test\n  fun statisticsConditionalCacheHit() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(1)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(0)\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(3)\n    assertThat(cache.networkCount()).isEqualTo(3)\n    assertThat(cache.hitCount()).isEqualTo(2)\n  }\n\n  @Test\n  fun statisticsFullCacheHit() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"A\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(1)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(0)\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(3)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(2)\n  }\n\n  @Test\n  fun varyMatchesChangedRequestHeaderField() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Accept-Language\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val frRequest =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"fr-CA\")\n        .build()\n    val frResponse = client.newCall(frRequest).execute()\n    assertThat(frResponse.body.string()).isEqualTo(\"A\")\n    val enRequest =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"en-US\")\n        .build()\n    val enResponse = client.newCall(enRequest).execute()\n    assertThat(enResponse.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun varyMatchesUnchangedRequestHeaderField() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Accept-Language\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"fr-CA\")\n        .build()\n    val response1 = client.newCall(request).execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    val request1 =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"fr-CA\")\n        .build()\n    val response2 = client.newCall(request1).execute()\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun varyMatchesAbsentRequestHeaderField() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Foo\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun varyMatchesAddedRequestHeaderField() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Foo\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Foo\", \"bar\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun varyMatchesRemovedRequestHeaderField() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Foo\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Foo\", \"bar\")\n        .build()\n    val fooresponse = client.newCall(request).execute()\n    assertThat(fooresponse.body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun varyFieldsAreCaseInsensitive() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: ACCEPT-LANGUAGE\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"fr-CA\")\n        .build()\n    val response1 = client.newCall(request).execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    val request1 =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"accept-language\", \"fr-CA\")\n        .build()\n    val response2 = client.newCall(request1).execute()\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun varyMultipleFieldsWithMatch() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Accept-Language, Accept-Charset\")\n        .addHeader(\"Vary: Accept-Encoding\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"fr-CA\")\n        .header(\"Accept-Charset\", \"UTF-8\")\n        .header(\"Accept-Encoding\", \"identity\")\n        .build()\n    val response1 = client.newCall(request).execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    val request1 =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"fr-CA\")\n        .header(\"Accept-Charset\", \"UTF-8\")\n        .header(\"Accept-Encoding\", \"identity\")\n        .build()\n    val response2 = client.newCall(request1).execute()\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun varyMultipleFieldsWithNoMatch() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Accept-Language, Accept-Charset\")\n        .addHeader(\"Vary: Accept-Encoding\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val frRequest =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"fr-CA\")\n        .header(\"Accept-Charset\", \"UTF-8\")\n        .header(\"Accept-Encoding\", \"identity\")\n        .build()\n    val frResponse = client.newCall(frRequest).execute()\n    assertThat(frResponse.body.string()).isEqualTo(\"A\")\n    val enRequest =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"en-CA\")\n        .header(\"Accept-Charset\", \"UTF-8\")\n        .header(\"Accept-Encoding\", \"identity\")\n        .build()\n    val enResponse = client.newCall(enRequest).execute()\n    assertThat(enResponse.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun varyMultipleFieldValuesWithMatch() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Accept-Language\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val request1 =\n      Request\n        .Builder()\n        .url(url)\n        .addHeader(\"Accept-Language\", \"fr-CA, fr-FR\")\n        .addHeader(\"Accept-Language\", \"en-US\")\n        .build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    val request2 =\n      Request\n        .Builder()\n        .url(url)\n        .addHeader(\"Accept-Language\", \"fr-CA, fr-FR\")\n        .addHeader(\"Accept-Language\", \"en-US\")\n        .build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun varyMultipleFieldValuesWithNoMatch() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Accept-Language\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    val request1 =\n      Request\n        .Builder()\n        .url(url)\n        .addHeader(\"Accept-Language\", \"fr-CA, fr-FR\")\n        .addHeader(\"Accept-Language\", \"en-US\")\n        .build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    val request2 =\n      Request\n        .Builder()\n        .url(url)\n        .addHeader(\"Accept-Language\", \"fr-CA\")\n        .addHeader(\"Accept-Language\", \"en-US\")\n        .build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun varyAsterisk() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: *\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun varyAndHttps() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .addHeader(\"Vary: Accept-Language\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(NULL_HOSTNAME_VERIFIER)\n        .build()\n    val url = server.url(\"/\")\n    val request1 =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"en-US\")\n        .build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    val request2 =\n      Request\n        .Builder()\n        .url(url)\n        .header(\"Accept-Language\", \"en-US\")\n        .build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun cachePlusCookies() {\n    val cookieJar = RecordingCookieJar()\n    client =\n      client\n        .newBuilder()\n        .cookieJar(cookieJar)\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Set-Cookie: a=FIRST\")\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Set-Cookie: a=SECOND\")\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    cookieJar.assertResponseCookies(\"a=FIRST; path=/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    cookieJar.assertResponseCookies(\"a=SECOND; path=/\")\n  }\n\n  @Test\n  fun getHeadersReturnsNetworkEndToEndHeaders() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Allow: GET, HEAD\")\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Allow: GET, HEAD, PUT\")\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertThat(response1.header(\"Allow\")).isEqualTo(\"GET, HEAD\")\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    assertThat(response2.header(\"Allow\")).isEqualTo(\"GET, HEAD, PUT\")\n  }\n\n  @Test\n  fun getHeadersReturnsCachedHopByHopHeaders() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Transfer-Encoding: identity\")\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Transfer-Encoding: none\")\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertThat(response1.header(\"Transfer-Encoding\")).isEqualTo(\"identity\")\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    assertThat(response2.header(\"Transfer-Encoding\")).isEqualTo(\"identity\")\n  }\n\n  @Test\n  fun getHeadersDeletesCached100LevelWarnings() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Warning: 199 test danger\")\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertThat(response1.header(\"Warning\")).isEqualTo(\"199 test danger\")\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    assertThat(response2.header(\"Warning\")).isNull()\n  }\n\n  @Test\n  fun getHeadersRetainsCached200LevelWarnings() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Warning: 299 test danger\")\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertThat(response1.header(\"Warning\")).isEqualTo(\"299 test danger\")\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    assertThat(response2.header(\"Warning\")).isEqualTo(\"299 test danger\")\n  }\n\n  @Test\n  fun doNotCachePartialResponse() {\n    assertNotCached(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_PARTIAL)\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.HOURS))\n        .addHeader(\"Content-Range: bytes 100-100/200\")\n        .addHeader(\"Cache-Control: max-age=60\")\n        .build(),\n    )\n  }\n\n  @Test\n  fun conditionalHitUpdatesCache() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(0, TimeUnit.SECONDS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=30\")\n        .addHeader(\"Allow: GET, HEAD\")\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n\n    // A cache miss writes the cache.\n    val t0 = System.currentTimeMillis()\n    val response1 = get(server.url(\"/a\"))\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertThat(response1.header(\"Allow\")).isNull()\n    assertThat((response1.receivedResponseAtMillis - t0).toDouble()).isCloseTo(0.0, 250.0)\n\n    // A conditional cache hit updates the cache.\n    Thread.sleep(500) // Make sure t0 and t1 are distinct.\n    val t1 = System.currentTimeMillis()\n    val response2 = get(server.url(\"/a\"))\n    assertThat(response2.code).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    assertThat(response2.header(\"Allow\")).isEqualTo(\"GET, HEAD\")\n    val updatedTimestamp = response2.receivedResponseAtMillis\n    assertThat((updatedTimestamp - t1).toDouble()).isCloseTo(0.0, 250.0)\n\n    // A full cache hit reads the cache.\n    Thread.sleep(10)\n    val response3 = get(server.url(\"/a\"))\n    assertThat(response3.body.string()).isEqualTo(\"A\")\n    assertThat(response3.header(\"Allow\")).isEqualTo(\"GET, HEAD\")\n    assertThat(response3.receivedResponseAtMillis).isEqualTo(updatedTimestamp)\n    assertThat(server.requestCount).isEqualTo(2)\n  }\n\n  @Test\n  fun responseSourceHeaderCached() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=30\")\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.MINUTES))\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Cache-Control\", \"only-if-cached\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun responseSourceHeaderConditionalCacheFetched() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=30\")\n        .addHeader(\"Date: \" + formatDate(-31, TimeUnit.MINUTES))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .addHeader(\"Cache-Control: max-age=30\")\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.MINUTES))\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val response = get(server.url(\"/\"))\n    assertThat(response.body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun responseSourceHeaderConditionalCacheNotFetched() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .addHeader(\"Cache-Control: max-age=0\")\n        .addHeader(\"Date: \" + formatDate(0, TimeUnit.MINUTES))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(304)\n        .build(),\n    )\n    assertThat(get(server.url(\"/\")).body.string()).isEqualTo(\"A\")\n    val response = get(server.url(\"/\"))\n    assertThat(response.body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun responseSourceHeaderFetched() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .build(),\n    )\n    val response = get(server.url(\"/\"))\n    assertThat(response.body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun emptyResponseHeaderNameFromCacheIsLenient() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"Cache-Control: max-age=120\")\n    addHeaderLenient(headers, \": A\")\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headers(headers.build())\n        .body(\"body\")\n        .build(),\n    )\n    val response = get(server.url(\"/\"))\n    assertThat(response.header(\"\")).isEqualTo(\"A\")\n    assertThat(response.body.string()).isEqualTo(\"body\")\n  }\n\n  /**\n   * Old implementations of OkHttp's response cache wrote header fields like \":status: 200 OK\". This\n   * broke our cached response parser because it split on the first colon. This regression test\n   * exists to help us read these old bad cache entries.\n   *\n   * https://github.com/square/okhttp/issues/227\n   */\n  @Test\n  fun testGoldenCacheResponse() {\n    cache.close()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    addFinalFailingResponse()\n    val url = server.url(\"/\")\n    val urlKey = key(url)\n    val entryMetadata =\n      \"\"\"\n      $url\n      GET\n      0\n      HTTP/1.1 200 OK\n      7\n      :status: 200 OK\n      :version: HTTP/1.1\n      etag: foo\n      content-length: 3\n      OkHttp-Received-Millis: ${System.currentTimeMillis()}\n      X-Android-Response-Source: NETWORK 200\n      OkHttp-Sent-Millis: ${System.currentTimeMillis()}\n\n      TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\n      1\n      MIIBpDCCAQ2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1qd2lsc29uLmxvY2FsMB4XDTEzMDgyOTA1MDE1OVoXDTEzMDgzMDA1MDE1OVowGDEWMBQGA1UEAxMNandpbHNvbi5sb2NhbDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlFW+rGo/YikCcRghOyKkJanmVmJSce/p2/jH1QvNIFKizZdh8AKNwojt3ywRWaDULA/RlCUcltF3HGNsCyjQI/+Lf40x7JpxXF8oim1E6EtDoYtGWAseelawus3IQ13nmo6nWzfyCA55KhAWf4VipelEy8DjcuFKv6L0xwXnI0ECAwEAATANBgkqhkiG9w0BAQsFAAOBgQAuluNyPo1HksU3+Mr/PyRQIQS4BI7pRXN8mcejXmqyscdP7S6J21FBFeRR8/XNjVOp4HT9uSc2hrRtTEHEZCmpyoxixbnM706ikTmC7SN/GgM+SmcoJ1ipJcNcl8N0X6zym4dmyFfXKHu2PkTo7QFdpOJFvP3lIigcSZXozfmEDg==\n      -1\n\n      \"\"\".trimIndent()\n    val entryBody = \"abc\"\n    val journalBody = \"\"\"libcore.io.DiskLruCache\n1\n201105\n2\n\nCLEAN $urlKey ${entryMetadata.length} ${entryBody.length}\n\"\"\"\n    fileSystem.createDirectory(cache.directoryPath)\n    writeFile(cache.directoryPath, \"$urlKey.0\", entryMetadata)\n    writeFile(cache.directoryPath, \"$urlKey.1\", entryBody)\n    writeFile(cache.directoryPath, \"journal\", journalBody)\n    cache = Cache(fileSystem, cache.directoryPath, Int.MAX_VALUE.toLong())\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    val response = get(url)\n    assertThat(response.body.string()).isEqualTo(entryBody)\n    assertThat(response.header(\"Content-Length\")).isEqualTo(\"3\")\n    assertThat(response.header(\"etag\")).isEqualTo(\"foo\")\n  }\n\n  /** Exercise the cache format in OkHttp 2.7 and all earlier releases.  */\n  @Test\n  fun testGoldenCacheHttpsResponseOkHttp27() {\n    addFinalFailingResponse()\n\n    val url = server.url(\"/\")\n    val urlKey = key(url)\n    val prefix = get().getPrefix()\n    val entryMetadata =\n      \"\"\"\n      $url\n      GET\n      0\n      HTTP/1.1 200 OK\n      4\n      Content-Length: 3\n      $prefix-Received-Millis: ${System.currentTimeMillis()}\n      $prefix-Sent-Millis: ${System.currentTimeMillis()}\n      Cache-Control: max-age=60\n\n      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n      1\n      MIIBnDCCAQWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTUxMjIyMDExMTQwWhcNMTUxMjIzMDExMTQwWjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJTn2Dh8xYmegvpOSmsKb2Os6Cxf1L4fYbnHr/turInUD5r1P7ZAuxurY880q3GT5bUDoirS3IfucddrT1AcAmUzEmk/FDjggiP8DlxFkY/XwXBlhRDVIp/mRuASPMGInckc0ZaixOkRFyrxADj+r1eaSmXCIvV5yTY6IaIokLj1AgMBAAEwDQYJKoZIhvcNAQELBQADgYEAFblnedqtfRqI9j2WDyPPoG0NTZf9xwjeUu+ju+Ktty8u9k7Lgrrd/DH2mQEtBD1Ctvp91MJfAClNg3faZzwClUyu5pd0QXRZEUwSwZQNen2QWDHRlVsItclBJ4t+AJLqTbwofWi4m4K8REOl593hD55E4+lY22JZiVQyjsQhe6I=\n      0\n\n      \"\"\".trimIndent()\n    val entryBody = \"abc\"\n    val journalBody = \"\"\"libcore.io.DiskLruCache\n1\n201105\n2\n\nDIRTY $urlKey\nCLEAN $urlKey ${entryMetadata.length} ${entryBody.length}\n\"\"\"\n    fileSystem.createDirectory(cache.directoryPath)\n    writeFile(cache.directoryPath, \"$urlKey.0\", entryMetadata)\n    writeFile(cache.directoryPath, \"$urlKey.1\", entryBody)\n    writeFile(cache.directoryPath, \"journal\", journalBody)\n    cache.close()\n    cache = Cache(fileSystem, cache.directoryPath, Int.MAX_VALUE.toLong())\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    val response = get(url)\n    assertThat(response.body.string()).isEqualTo(entryBody)\n    assertThat(response.header(\"Content-Length\")).isEqualTo(\"3\")\n  }\n\n  /** The TLS version is present in OkHttp 3.0 and beyond.  */\n  @Test\n  fun testGoldenCacheHttpsResponseOkHttp30() {\n    addFinalFailingResponse()\n\n    val url = server.url(\"/\")\n    val urlKey = key(url)\n    val prefix = get().getPrefix()\n    val entryMetadata =\n      \"\"\"\n      |$url\n      |GET\n      |0\n      |HTTP/1.1 200 OK\n      |4\n      |Content-Length: 3\n      |$prefix-Received-Millis: ${System.currentTimeMillis()}\n      |$prefix-Sent-Millis: ${System.currentTimeMillis()}\n      |Cache-Control: max-age=60\n      |\n      |TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n      |1\n      |MIIBnDCCAQWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTUxMjIyMDExMTQwWhcNMTUxMjIzMDExMTQwWjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJTn2Dh8xYmegvpOSmsKb2Os6Cxf1L4fYbnHr/turInUD5r1P7ZAuxurY880q3GT5bUDoirS3IfucddrT1AcAmUzEmk/FDjggiP8DlxFkY/XwXBlhRDVIp/mRuASPMGInckc0ZaixOkRFyrxADj+r1eaSmXCIvV5yTY6IaIokLj1AgMBAAEwDQYJKoZIhvcNAQELBQADgYEAFblnedqtfRqI9j2WDyPPoG0NTZf9xwjeUu+ju+Ktty8u9k7Lgrrd/DH2mQEtBD1Ctvp91MJfAClNg3faZzwClUyu5pd0QXRZEUwSwZQNen2QWDHRlVsItclBJ4t+AJLqTbwofWi4m4K8REOl593hD55E4+lY22JZiVQyjsQhe6I=\n      |0\n      |TLSv1.2\n      |\n      |\n      \"\"\".trimMargin()\n    val entryBody = \"abc\"\n    val journalBody =\n      \"\"\"\n      |libcore.io.DiskLruCache\n      |1\n      |201105\n      |2\n      |\n      |DIRTY $urlKey\n      |CLEAN $urlKey ${entryMetadata.length} ${entryBody.length}\n      |\n      \"\"\".trimMargin()\n    fileSystem.createDirectory(cache.directoryPath)\n    writeFile(cache.directoryPath, \"$urlKey.0\", entryMetadata)\n    writeFile(cache.directoryPath, \"$urlKey.1\", entryBody)\n    writeFile(cache.directoryPath, \"journal\", journalBody)\n    cache.close()\n    cache = Cache(fileSystem, cache.directoryPath, Int.MAX_VALUE.toLong())\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    val response = get(url)\n    assertThat(response.body.string()).isEqualTo(entryBody)\n    assertThat(response.header(\"Content-Length\")).isEqualTo(\"3\")\n  }\n\n  @Test\n  fun testGoldenCacheHttpResponseOkHttp30() {\n    addFinalFailingResponse()\n\n    val url = server.url(\"/\")\n    val urlKey = key(url)\n    val prefix = get().getPrefix()\n    val entryMetadata =\n      \"\"\"\n      |$url\n      |GET\n      |0\n      |HTTP/1.1 200 OK\n      |4\n      |Cache-Control: max-age=60\n      |Content-Length: 3\n      |$prefix-Received-Millis: ${System.currentTimeMillis()}\n      |$prefix-Sent-Millis: ${System.currentTimeMillis()}\n      |\n      \"\"\".trimMargin()\n    val entryBody = \"abc\"\n    val journalBody =\n      \"\"\"\n      |libcore.io.DiskLruCache\n      |1\n      |201105\n      |2\n      |\n      |DIRTY $urlKey\n      |CLEAN $urlKey ${entryMetadata.length} ${entryBody.length}\n      |\n      \"\"\".trimMargin()\n    fileSystem.createDirectory(cache.directoryPath)\n    writeFile(cache.directoryPath, \"$urlKey.0\", entryMetadata)\n    writeFile(cache.directoryPath, \"$urlKey.1\", entryBody)\n    writeFile(cache.directoryPath, \"journal\", journalBody)\n    cache.close()\n    cache = Cache(fileSystem, cache.directoryPath, Int.MAX_VALUE.toLong())\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    val response = get(url)\n    assertThat(response.body.string()).isEqualTo(entryBody)\n    assertThat(response.header(\"Content-Length\")).isEqualTo(\"3\")\n  }\n\n  private fun addFinalFailingResponse() {\n    // Should not get to this response, so fail if so.\n    // Avoids timeout on error\n    server.enqueue(MockResponse(code = 420, body = \"Enhance Your Calm\"))\n  }\n\n  @Test\n  fun evictAll() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    client.cache!!.evictAll()\n    assertThat(client.cache!!.size()).isEqualTo(0)\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun networkInterceptorInvokedForConditionalGet() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"ETag: v1\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n\n    // Seed the cache.\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    val ifNoneMatch = AtomicReference<String?>()\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            ifNoneMatch.compareAndSet(null, chain.request().header(\"If-None-Match\"))\n            chain.proceed(chain.request())\n          },\n        ).build()\n\n    // Confirm the value is cached and intercepted.\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(ifNoneMatch.get()).isEqualTo(\"v1\")\n  }\n\n  @Test\n  fun networkInterceptorNotInvokedForFullyCached() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"A\")\n        .build(),\n    )\n\n    // Seed the cache.\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n\n    // Confirm the interceptor isn't exercised.\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(Interceptor { chain: Interceptor.Chain? -> throw AssertionError() })\n        .build()\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun iterateCache() {\n    // Put some responses in the cache.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .build(),\n    )\n    val urlA = server.url(\"/a\")\n    assertThat(get(urlA).body.string()).isEqualTo(\"a\")\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"b\")\n        .build(),\n    )\n    val urlB = server.url(\"/b\")\n    assertThat(get(urlB).body.string()).isEqualTo(\"b\")\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"c\")\n        .build(),\n    )\n    val urlC = server.url(\"/c\")\n    assertThat(get(urlC).body.string()).isEqualTo(\"c\")\n\n    // Confirm the iterator returns those responses...\n    val i: Iterator<String> = cache.urls()\n    assertThat(i.hasNext()).isTrue()\n    assertThat(i.next()).isEqualTo(urlA.toString())\n    assertThat(i.hasNext()).isTrue()\n    assertThat(i.next()).isEqualTo(urlB.toString())\n    assertThat(i.hasNext()).isTrue()\n    assertThat(i.next()).isEqualTo(urlC.toString())\n\n    // ... and nothing else.\n    assertThat(i.hasNext()).isFalse()\n    assertFailsWith<NoSuchElementException> {\n      i.next()\n    }\n  }\n\n  @Test\n  fun iteratorRemoveFromCache() {\n    // Put a response in the cache.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control: max-age=60\")\n        .body(\"a\")\n        .build(),\n    )\n    val url = server.url(\"/a\")\n    assertThat(get(url).body.string()).isEqualTo(\"a\")\n\n    // Remove it with iteration.\n    val i = cache.urls()\n    assertThat(i.next()).isEqualTo(url.toString())\n    i.remove()\n\n    // Confirm that subsequent requests suffer a cache miss.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"b\")\n        .build(),\n    )\n    assertThat(get(url).body.string()).isEqualTo(\"b\")\n  }\n\n  @Test\n  fun iteratorRemoveWithoutNextThrows() {\n    // Put a response in the cache.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .build(),\n    )\n    val url = server.url(\"/a\")\n    assertThat(get(url).body.string()).isEqualTo(\"a\")\n    val i = cache.urls()\n    assertThat(i.hasNext()).isTrue()\n    assertFailsWith<IllegalStateException> {\n      i.remove()\n    }\n  }\n\n  @Test\n  fun iteratorRemoveOncePerCallToNext() {\n    // Put a response in the cache.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .build(),\n    )\n    val url = server.url(\"/a\")\n    assertThat(get(url).body.string()).isEqualTo(\"a\")\n    val i = cache.urls()\n    assertThat(i.next()).isEqualTo(url.toString())\n    i.remove()\n\n    // Too many calls to remove().\n    assertFailsWith<IllegalStateException> {\n      i.remove()\n    }\n  }\n\n  @Test\n  fun elementEvictedBetweenHasNextAndNext() {\n    // Put a response in the cache.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .build(),\n    )\n    val url = server.url(\"/a\")\n    assertThat(get(url).body.string()).isEqualTo(\"a\")\n\n    // The URL will remain available if hasNext() returned true...\n    val i = cache.urls()\n    assertThat(i.hasNext()).isTrue()\n\n    // ...so even when we evict the element, we still get something back.\n    cache.evictAll()\n    assertThat(i.next()).isEqualTo(url.toString())\n\n    // Remove does nothing. But most importantly, it doesn't throw!\n    i.remove()\n  }\n\n  @Test\n  fun elementEvictedBeforeHasNextIsOmitted() {\n    // Put a response in the cache.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .build(),\n    )\n    val url = server.url(\"/a\")\n    assertThat(get(url).body.string()).isEqualTo(\"a\")\n    val i: Iterator<String> = cache.urls()\n    cache.evictAll()\n\n    // The URL was evicted before hasNext() made any promises.\n    assertThat(i.hasNext()).isFalse()\n    assertFailsWith<NoSuchElementException> {\n      i.next()\n    }\n  }\n\n  /** Test https://github.com/square/okhttp/issues/1712.  */\n  @Test\n  fun conditionalMissUpdatesCache() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"ETag: v1\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"ETag: v2\")\n        .body(\"B\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isNull()\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isEqualTo(\"v2\")\n  }\n\n  @Test\n  fun combinedCacheHeadersCanBeNonAscii() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Cache-Control: max-age=0\")\n        .addHeaderLenient(\"Alpha\", \"α\")\n        .addHeaderLenient(\"β\", \"Beta\")\n        .body(\"abcd\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Transfer-Encoding: none\")\n        .addHeaderLenient(\"Gamma\", \"Γ\")\n        .addHeaderLenient(\"Δ\", \"Delta\")\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.header(\"Alpha\")).isEqualTo(\"α\")\n    assertThat(response1.header(\"β\")).isEqualTo(\"Beta\")\n    assertThat(response1.body.string()).isEqualTo(\"abcd\")\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.header(\"Alpha\")).isEqualTo(\"α\")\n    assertThat(response2.header(\"β\")).isEqualTo(\"Beta\")\n    assertThat(response2.header(\"Gamma\")).isEqualTo(\"Γ\")\n    assertThat(response2.header(\"Δ\")).isEqualTo(\"Delta\")\n    assertThat(response2.body.string()).isEqualTo(\"abcd\")\n  }\n\n  @Test\n  fun etagConditionCanBeNonAscii() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeaderLenient(\"Etag\", \"α\")\n        .addHeader(\"Cache-Control: max-age=0\")\n        .body(\"abcd\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.body.string()).isEqualTo(\"abcd\")\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"abcd\")\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isNull()\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isEqualTo(\"α\")\n  }\n\n  @Test\n  fun conditionalHitHeadersCombined() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Etag\", \"a\")\n        .addHeader(\"Cache-Control: max-age=0\")\n        .addHeader(\"A: a1\")\n        .addHeader(\"B: b2\")\n        .addHeader(\"B: b3\")\n        .body(\"abcd\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .addHeader(\"B: b4\")\n        .addHeader(\"B: b5\")\n        .addHeader(\"C: c6\")\n        .build(),\n    )\n    val response1 = get(server.url(\"/\"))\n    assertThat(response1.body.string()).isEqualTo(\"abcd\")\n    assertThat(response1.headers).isEqualTo(\n      headersOf(\n        \"Etag\",\n        \"a\",\n        \"Cache-Control\",\n        \"max-age=0\",\n        \"A\",\n        \"a1\",\n        \"B\",\n        \"b2\",\n        \"B\",\n        \"b3\",\n        \"Content-Length\",\n        \"4\",\n      ),\n    )\n\n    // The original 'A' header is retained because the network response doesn't have one.\n    // The original 'B' headers are replaced by the network response.\n    // The network's 'C' header is added.\n    val response2 = get(server.url(\"/\"))\n    assertThat(response2.body.string()).isEqualTo(\"abcd\")\n    assertThat(response2.headers).isEqualTo(\n      headersOf(\n        \"Etag\",\n        \"a\",\n        \"Cache-Control\",\n        \"max-age=0\",\n        \"A\",\n        \"a1\",\n        \"Content-Length\",\n        \"4\",\n        \"B\",\n        \"b4\",\n        \"B\",\n        \"b5\",\n        \"C\",\n        \"c6\",\n      ),\n    )\n  }\n\n  @Test\n  fun getHasCorrectResponse() {\n    val request = Request(server.url(\"/abc\"))\n\n    val response = testBasicCachingRules(request)\n\n    assertThat(response.request.url).isEqualTo(request.url)\n    assertThat(response.cacheResponse!!.request.url).isEqualTo(request.url)\n  }\n\n  @Test\n  fun postWithOverrideResponse() {\n    val url = server.url(\"/abc?token=123\")\n    val cacheUrlOverride = url.newBuilder().removeAllQueryParameters(\"token\").build()\n\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .method(\"POST\", \"XYZ\".toRequestBody())\n        .cacheUrlOverride(cacheUrlOverride)\n        .build()\n\n    val response = testBasicCachingRules(request)\n\n    assertThat(response.request.url).isEqualTo(request.url)\n    assertThat(response.cacheResponse!!.request.url).isEqualTo(cacheUrlOverride)\n  }\n\n  private fun testBasicCachingRules(request: Request): Response {\n    val mockResponse =\n      MockResponse\n        .Builder()\n        .addHeader(\"Last-Modified: \" + formatDate(-1, TimeUnit.HOURS))\n        .addHeader(\"Expires: \" + formatDate(1, TimeUnit.HOURS))\n        .status(\"HTTP/1.1 200 Fantastic\")\n    server.enqueue(mockResponse.build())\n\n    client.newCall(request).execute().use {\n      it.body.bytes()\n    }\n    return client.newCall(request).execute()\n  }\n\n  private operator fun get(url: HttpUrl): Response {\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .build()\n    return client.newCall(request).execute()\n  }\n\n  private fun writeFile(\n    directory: Path,\n    file: String,\n    content: String,\n  ) {\n    val sink = fileSystem.sink(directory / file).buffer()\n    sink.writeUtf8(content)\n    sink.close()\n  }\n\n  /**\n   * @param delta the offset from the current date to use. Negative values yield dates in the past;\n   * positive values yield dates in the future.\n   */\n  private fun formatDate(\n    delta: Long,\n    timeUnit: TimeUnit,\n  ): String = formatDate(Date(System.currentTimeMillis() + timeUnit.toMillis(delta)))\n\n  private fun formatDate(date: Date): String {\n    val rfc1123: DateFormat = SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss zzz\", Locale.US)\n    rfc1123.timeZone = TimeZone.getTimeZone(\"GMT\")\n    return rfc1123.format(date)\n  }\n\n  private fun assertNotCached(response: MockResponse) {\n    server.enqueue(\n      response\n        .newBuilder()\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n  }\n\n  /** @return the request with the conditional get headers. */\n  private fun assertConditionallyCached(response: MockResponse): RecordedRequest {\n    // scenario 1: condition succeeds\n    server.enqueue(\n      response\n        .newBuilder()\n        .body(\"A\")\n        .status(\"HTTP/1.1 200 A-OK\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n\n    // scenario 2: condition fails\n    server.enqueue(\n      response\n        .newBuilder()\n        .body(\"B\")\n        .status(\"HTTP/1.1 200 B-OK\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 200 C-OK\")\n        .body(\"C\")\n        .build(),\n    )\n    val valid = server.url(\"/valid\")\n    val response1 = get(valid)\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertThat(response1.code).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(response1.message).isEqualTo(\"A-OK\")\n    val response2 = get(valid)\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    assertThat(response2.code).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(response2.message).isEqualTo(\"A-OK\")\n    val invalid = server.url(\"/invalid\")\n    val response3 = get(invalid)\n    assertThat(response3.body.string()).isEqualTo(\"B\")\n    assertThat(response3.code).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(response3.message).isEqualTo(\"B-OK\")\n    val response4 = get(invalid)\n    assertThat(response4.body.string()).isEqualTo(\"C\")\n    assertThat(response4.code).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertThat(response4.message).isEqualTo(\"C-OK\")\n    server.takeRequest() // regular get\n    return server.takeRequest() // conditional get\n  }\n\n  @Test\n  fun immutableIsCached() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control\", \"immutable, max-age=10\")\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"B\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n  }\n\n  @Test\n  fun immutableIsCachedAfterMultipleCalls() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Cache-Control\", \"immutable, max-age=10\")\n        .body(\"B\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"C\")\n        .build(),\n    )\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n    assertThat(get(url).body.string()).isEqualTo(\"B\")\n  }\n\n  @Test\n  fun immutableIsNotCachedBeyondFreshnessLifetime() {\n    //      last modified: 115 seconds ago\n    //             served:  15 seconds ago\n    //   default lifetime: (115 - 15) / 10 = 10 seconds\n    //            expires:  10 seconds from served date = 5 seconds ago\n    val lastModifiedDate = formatDate(-115, TimeUnit.SECONDS)\n    val conditionalRequest =\n      assertConditionallyCached(\n        MockResponse\n          .Builder()\n          .addHeader(\"Cache-Control: immutable\")\n          .addHeader(\"Last-Modified: $lastModifiedDate\")\n          .addHeader(\"Date: \" + formatDate(-15, TimeUnit.SECONDS))\n          .build(),\n      )\n    assertThat(conditionalRequest.headers[\"If-Modified-Since\"])\n      .isEqualTo(lastModifiedDate)\n  }\n\n  @Test\n  fun testPublicPathConstructor() {\n    val events: MutableList<String> = ArrayList()\n    fileSystem.createDirectories(cache.directoryPath)\n    fileSystem.createDirectories(cache.directoryPath)\n    val loggingFileSystem: FileSystem =\n      object : ForwardingFileSystem(fileSystem) {\n        override fun onPathParameter(\n          path: Path,\n          functionName: String,\n          parameterName: String,\n        ): Path {\n          events.add(\"$functionName:$path\")\n          return path\n        }\n\n        override fun onPathResult(\n          path: Path,\n          functionName: String,\n        ): Path {\n          events.add(\"$functionName:$path\")\n          return path\n        }\n      }\n    val path: Path = \"/cache\".toPath()\n    val c = Cache(loggingFileSystem, path, 100000L)\n    assertThat(c.directoryPath).isEqualTo(path)\n    c.size()\n    assertThat(events).containsExactly(\n      \"metadataOrNull:/cache/journal.bkp\",\n      \"metadataOrNull:/cache\",\n      \"sink:/cache/journal.bkp\",\n      \"delete:/cache/journal.bkp\",\n      \"metadataOrNull:/cache/journal\",\n      \"metadataOrNull:/cache\",\n      \"sink:/cache/journal.tmp\",\n      \"metadataOrNull:/cache/journal\",\n      \"atomicMove:/cache/journal.tmp\",\n      \"atomicMove:/cache/journal\",\n      \"appendingSink:/cache/journal\",\n    )\n    events.clear()\n    c.size()\n    assertThat(events).isEmpty()\n  }\n\n  private fun assertFullyCached(response: MockResponse) {\n    server.enqueue(response.newBuilder().body(\"A\").build())\n    server.enqueue(response.newBuilder().body(\"B\").build())\n    val url = server.url(\"/\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n    assertThat(get(url).body.string()).isEqualTo(\"A\")\n  }\n\n  /**\n   * Shortens the body of `response` but not the corresponding headers. Only useful to test\n   * how clients respond to the premature conclusion of the HTTP body.\n   */\n  private fun truncateViolently(\n    builder: MockResponse.Builder,\n    numBytesToKeep: Int,\n  ): MockResponse.Builder {\n    val response = builder.build()\n    builder.onResponseEnd(ShutdownConnection)\n    val headers = response.headers\n    val fullBody = Buffer()\n    response.body!!.writeTo(fullBody)\n    val truncatedBody = Buffer()\n    truncatedBody.write(fullBody, numBytesToKeep.toLong())\n    builder.body(truncatedBody)\n    builder.headers(headers)\n    return builder\n  }\n\n  internal enum class TransferKind {\n    CHUNKED {\n      override fun setBody(\n        response: MockResponse.Builder,\n        content: Buffer,\n        chunkSize: Int,\n      ) {\n        response.chunkedBody(content, chunkSize)\n      }\n    },\n    FIXED_LENGTH {\n      override fun setBody(\n        response: MockResponse.Builder,\n        content: Buffer,\n        chunkSize: Int,\n      ) {\n        response.body(content)\n      }\n    },\n    END_OF_STREAM {\n      override fun setBody(\n        response: MockResponse.Builder,\n        content: Buffer,\n        chunkSize: Int,\n      ) {\n        response.body(content)\n        response.onResponseEnd(ShutdownConnection)\n        response.removeHeader(\"Content-Length\")\n      }\n    }, ;\n\n    abstract fun setBody(\n      response: MockResponse.Builder,\n      content: Buffer,\n      chunkSize: Int,\n    )\n\n    fun setBody(\n      response: MockResponse.Builder,\n      content: String,\n      chunkSize: Int,\n    ) {\n      setBody(response, Buffer().writeUtf8(content), chunkSize)\n    }\n  }\n\n  /** Returns a gzipped copy of `bytes`.  */\n  fun gzip(data: String): Buffer {\n    val result = Buffer()\n    val sink = GzipSink(result).buffer()\n    sink.writeUtf8(data)\n    sink.close()\n    return result\n  }\n\n  companion object {\n    private val NULL_HOSTNAME_VERIFIER = HostnameVerifier { hostname, session -> true }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CallHandshakeTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isIn\nimport javax.net.ssl.SSLSocket\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CipherSuite.Companion.TLS_AES_128_GCM_SHA256\nimport okhttp3.CipherSuite.Companion.TLS_AES_256_GCM_SHA384\nimport okhttp3.CipherSuite.Companion.TLS_CHACHA20_POLY1305_SHA256\nimport okhttp3.CipherSuite.Companion.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\nimport okhttp3.CipherSuite.Companion.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384\nimport okhttp3.CipherSuite.Companion.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\nimport okhttp3.CipherSuite.Companion.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\nimport okhttp3.CipherSuite.Companion.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\nimport okhttp3.CipherSuite.Companion.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\nimport okhttp3.CipherSuite.Companion.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\nimport okhttp3.internal.effectiveCipherSuites\nimport okhttp3.internal.platform.Platform\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass CallHandshakeTest {\n  private lateinit var client: OkHttpClient\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @RegisterExtension\n  @JvmField\n  val clientTestRule = OkHttpClientTestRule()\n\n  @RegisterExtension\n  @JvmField\n  var platform = PlatformRule()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n\n  /** Ciphers in order we observed directly on the socket. */\n  private lateinit var handshakeEnabledCipherSuites: List<String>\n\n  /** Ciphers in order we observed on sslSocketFactory defaults. */\n  private lateinit var defaultEnabledCipherSuites: List<String>\n\n  /** Ciphers in order we observed on sslSocketFactory supported. */\n  private lateinit var defaultSupportedCipherSuites: List<String>\n\n  val expectedModernTls12CipherSuites =\n    listOf(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384)\n  val expectedModernTls13CipherSuites = listOf(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384)\n\n  @BeforeEach\n  fun setup() {\n    server.enqueue(MockResponse())\n\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n\n    defaultEnabledCipherSuites =\n      handshakeCertificates.sslSocketFactory().defaultCipherSuites.toList()\n    defaultSupportedCipherSuites =\n      handshakeCertificates.sslSocketFactory().supportedCipherSuites.toList()\n  }\n\n  @Test\n  fun testDefaultHandshakeCipherSuiteOrderingTls12Restricted() {\n    // We are avoiding making guarantees on ordering of secondary Platforms.\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n\n    val client = makeClient(ConnectionSpec.RESTRICTED_TLS, TlsVersion.TLS_1_2)\n\n    val handshake = makeRequest(client)\n\n    assertThat(handshake.cipherSuite).isIn(*expectedModernTls12CipherSuites.toTypedArray())\n\n    // Probably something like\n    // TLS_AES_128_GCM_SHA256\n    // TLS_AES_256_GCM_SHA384\n    // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n    // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n    // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n    // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n    assertThat(handshakeEnabledCipherSuites).containsExactly(\n      *expectedConnectionCipherSuites(client).toTypedArray(),\n    )\n    assertThat(handshake.tlsVersion).isEqualTo(TlsVersion.TLS_1_2)\n  }\n\n  @Test\n  fun testDefaultHandshakeCipherSuiteOrderingTls12Modern() {\n    // We are avoiding making guarantees on ordering of secondary Platforms.\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n\n    val client = makeClient(ConnectionSpec.MODERN_TLS, TlsVersion.TLS_1_2)\n\n    val handshake = makeRequest(client)\n\n    assertThat(handshake.cipherSuite).isIn(*expectedModernTls12CipherSuites.toTypedArray())\n\n    // Probably something like\n    // TLS_AES_128_GCM_SHA256\n    // TLS_AES_256_GCM_SHA384\n    // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n    // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n    // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n    // TLS_RSA_WITH_AES_256_GCM_SHA384\n    // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n    // TLS_RSA_WITH_AES_128_GCM_SHA256\n    // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\n    // TLS_RSA_WITH_AES_256_CBC_SHA\n    // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\n    // TLS_RSA_WITH_AES_128_CBC_SHA\n    assertThat(handshakeEnabledCipherSuites).containsExactly(\n      *expectedConnectionCipherSuites(client).toTypedArray(),\n    )\n    assertThat(handshake.tlsVersion).isEqualTo(TlsVersion.TLS_1_2)\n  }\n\n  @Test\n  fun testDefaultHandshakeCipherSuiteOrderingTls13Modern() {\n    // We are avoiding making guarantees on ordering of secondary Platforms.\n    platform.assumeNotBouncyCastle()\n\n    val client = makeClient(ConnectionSpec.MODERN_TLS, TlsVersion.TLS_1_3)\n\n    val handshake = makeRequest(client)\n\n    assertThat(handshake.cipherSuite).isIn(*expectedModernTls13CipherSuites.toTypedArray())\n\n    // TODO: filter down to TLSv1.3 when only activated.\n    // Probably something like\n    // TLS_AES_128_GCM_SHA256\n    // TLS_AES_256_GCM_SHA384\n    // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n    // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n    // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n    // TLS_RSA_WITH_AES_256_GCM_SHA384\n    // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n    // TLS_RSA_WITH_AES_128_GCM_SHA256\n    // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\n    // TLS_RSA_WITH_AES_256_CBC_SHA\n    // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\n    // TLS_RSA_WITH_AES_128_CBC_SHA\n    assertThat(handshakeEnabledCipherSuites).containsExactly(\n      *expectedConnectionCipherSuites(client).toTypedArray(),\n    )\n    assertThat(handshake.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n  }\n\n  @Test\n  fun testHandshakeCipherSuiteOrderingWhenReversed() {\n    // We are avoiding making guarantees on ordering of secondary Platforms.\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n\n    val reversed = ConnectionSpec.COMPATIBLE_TLS.cipherSuites!!.reversed()\n    val client =\n      makeClient(\n        ConnectionSpec.COMPATIBLE_TLS,\n        TlsVersion.TLS_1_2,\n        reversed,\n      )\n\n    makeRequest(client)\n\n    val expectedConnectionCipherSuites = expectedConnectionCipherSuites(client)\n    // Will choose a poor cipher suite but not plaintext.\n//    assertThat(handshake.cipherSuite).isEqualTo(\"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\")\n    assertThat(handshakeEnabledCipherSuites).containsExactly(\n      *expectedConnectionCipherSuites.toTypedArray(),\n    )\n  }\n\n  @Test\n  fun clientOrderApplied() {\n//    // Flaky in CI\n//    // CallHandshakeTest[jvm] > defaultOrderMaintained()[jvm] FAILED\n//    //  org.bouncycastle.tls.TlsFatalAlertReceived: handshake_failure(40)\n//    platform.assumeNotBouncyCastle()\n\n    val client = makeClient()\n    makeRequest(client)\n\n    // As of OkHttp 5 we now apply the ordering from the OkHttpClient, which defaults to MODERN_TLS\n    // Clients might need a changed order, but can at least define a preferred order to override that default.\n    val socketOrderedByDefaults =\n      handshakeEnabledCipherSuites.sortedBy { ConnectionSpec.MODERN_TLS.cipherSuitesAsString!!.indexOf(it) }\n\n    assertThat(handshakeEnabledCipherSuites).containsExactly(\n      *socketOrderedByDefaults.toTypedArray(),\n    )\n  }\n\n  @Test\n  fun advertisedOrderInRestricted() {\n    assertThat(ConnectionSpec.RESTRICTED_TLS.cipherSuites!!).containsExactly(\n      TLS_AES_128_GCM_SHA256,\n      TLS_AES_256_GCM_SHA384,\n      TLS_CHACHA20_POLY1305_SHA256,\n      TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\n      TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\n      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n      TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,\n      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,\n    )\n  }\n\n  @Test\n  fun effectiveOrderInRestrictedJdk11() {\n    platform.assumeJdkVersion(11)\n    // We are avoiding making guarantees on ordering of secondary Platforms.\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n\n    val platform = Platform.get()\n    val platformDefaultCipherSuites =\n      platform.newSslSocketFactory(platform.platformTrustManager()).defaultCipherSuites\n    val cipherSuites =\n      ConnectionSpec.RESTRICTED_TLS.effectiveCipherSuites(platformDefaultCipherSuites)\n\n    if (cipherSuites.contains(TLS_CHACHA20_POLY1305_SHA256.javaName)) {\n      assertThat(cipherSuites).containsExactly(\n        TLS_AES_128_GCM_SHA256.javaName,\n        TLS_AES_256_GCM_SHA384.javaName,\n        TLS_CHACHA20_POLY1305_SHA256.javaName,\n        TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n        TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.javaName,\n        TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384.javaName,\n        TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.javaName,\n        TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256.javaName,\n        TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256.javaName,\n      )\n    } else {\n      assertThat(cipherSuites).containsExactly(\n        TLS_AES_128_GCM_SHA256.javaName,\n        TLS_AES_256_GCM_SHA384.javaName,\n        TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384.javaName,\n        TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n        TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.javaName,\n        TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.javaName,\n      )\n    }\n  }\n\n  private fun expectedConnectionCipherSuites(client: OkHttpClient): Set<String> =\n    client.connectionSpecs\n      .first()\n      .cipherSuites!!\n      .map {\n        it.javaName\n      }.intersect(defaultEnabledCipherSuites.toSet())\n\n  private fun makeClient(\n    connectionSpec: ConnectionSpec? = null,\n    tlsVersion: TlsVersion? = null,\n    cipherSuites: List<CipherSuite>? = null,\n  ): OkHttpClient =\n    this.client\n      .newBuilder()\n      .apply {\n        if (connectionSpec != null) {\n          connectionSpecs(\n            listOf(\n              ConnectionSpec\n                .Builder(connectionSpec)\n                .apply {\n                  if (tlsVersion != null) {\n                    tlsVersions(tlsVersion)\n                  }\n                  if (cipherSuites != null) {\n                    cipherSuites(*cipherSuites.toTypedArray())\n                  }\n                }.build(),\n            ),\n          )\n        }\n      }.addNetworkInterceptor {\n        val socket = it.connection()!!.socket() as SSLSocket\n\n        handshakeEnabledCipherSuites = socket.enabledCipherSuites.toList()\n\n        it.proceed(it.request())\n      }.build()\n\n  private fun makeRequest(client: OkHttpClient): Handshake {\n    val call = client.newCall(Request(server.url(\"/\")))\n    return call.execute().use { it.handshake!! }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CallKotlinTest.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isNotSameAs\nimport java.io.IOException\nimport java.net.Proxy\nimport java.security.cert.X509Certificate\nimport java.time.Duration\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.TestUtil.assertSuppressed\nimport okhttp3.internal.DoubleInetAddressDns\nimport okhttp3.internal.connection.RealConnection\nimport okhttp3.internal.connection.RealConnection.Companion.IDLE_CONNECTION_HEALTHY_NS\nimport okhttp3.internal.http.RecordingProxySelector\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule\nimport okio.BufferedSink\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junitpioneer.jupiter.RetryingTest\n\n@Timeout(30)\nclass CallKotlinTest {\n  @JvmField @RegisterExtension\n  val platform = PlatformRule()\n\n  @JvmField @RegisterExtension\n  val clientTestRule =\n    OkHttpClientTestRule().apply {\n      recordFrames = true\n      recordSslDebug = true\n    }\n\n  private var client = clientTestRule.newClient()\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @Test\n  fun legalToExecuteTwiceCloning() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n\n    val request = Request(server.url(\"/\"))\n\n    val call = client.newCall(request)\n    val response1 = call.execute()\n\n    val cloned = call.clone()\n    val response2 = cloned.execute()\n\n    assertThat(\"abc\").isEqualTo(response1.body.string())\n    assertThat(\"def\").isEqualTo(response2.body.string())\n  }\n\n  @Test\n  @Flaky\n  fun testMockWebserverRequest() {\n    enableTls()\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val request = Request.Builder().url(server.url(\"/\")).build()\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n      assertEquals(\n        \"CN=localhost\",\n        (response.handshake!!.peerCertificates.single() as X509Certificate).subjectDN.name,\n      )\n    }\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n\n  @RetryingTest(5)\n  @Flaky\n  fun testHeadAfterPut() {\n    class ErringRequestBody : RequestBody() {\n      override fun contentType(): MediaType = \"application/xml\".toMediaType()\n\n      override fun writeTo(sink: BufferedSink) {\n        sink.writeUtf8(\"<el\")\n        sink.flush()\n        throw IOException(\"failed to stream the XML\")\n      }\n    }\n\n    class ValidRequestBody : RequestBody() {\n      override fun contentType(): MediaType = \"application/xml\".toMediaType()\n\n      override fun writeTo(sink: BufferedSink) {\n        sink.writeUtf8(\"<element/>\")\n        sink.flush()\n      }\n    }\n\n    server.enqueue(MockResponse(code = 201))\n    server.enqueue(MockResponse(code = 204))\n    server.enqueue(MockResponse(code = 204))\n\n    val endpointUrl = server.url(\"/endpoint\")\n\n    var request =\n      Request\n        .Builder()\n        .url(endpointUrl)\n        .header(\"Content-Type\", \"application/xml\")\n        .put(ValidRequestBody())\n        .build()\n    client.newCall(request).execute().use {\n      assertEquals(201, it.code)\n    }\n\n    request =\n      Request\n        .Builder()\n        .url(endpointUrl)\n        .head()\n        .build()\n    client.newCall(request).execute().use {\n      assertEquals(204, it.code)\n    }\n\n    request =\n      Request\n        .Builder()\n        .url(endpointUrl)\n        .header(\"Content-Type\", \"application/xml\")\n        .put(ErringRequestBody())\n        .build()\n    assertFailsWith<IOException> {\n      client.newCall(request).execute()\n    }\n\n    request =\n      Request\n        .Builder()\n        .url(endpointUrl)\n        .head()\n        .build()\n\n    client.newCall(request).execute().use {\n      assertEquals(204, it.code)\n    }\n  }\n\n  @Test\n  fun staleConnectionNotReusedForNonIdempotentRequest() {\n    // Capture the connection so that we can later make it stale.\n    var connection: RealConnection? = null\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          Interceptor { chain ->\n            connection = chain.connection() as RealConnection\n            chain.proceed(chain.request())\n          },\n        ).build()\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .onResponseEnd(\n          CloseSocket(\n            closeSocket = false,\n            shutdownOutput = true,\n          ),\n        ).build(),\n    )\n    server.enqueue(MockResponse(body = \"b\"))\n\n    val requestA = Request(server.url(\"/\"))\n    val responseA = client.newCall(requestA).execute()\n\n    assertThat(responseA.body.string()).isEqualTo(\"a\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // Give the socket a chance to become stale.\n    connection!!.idleAtNs -= IDLE_CONNECTION_HEALTHY_NS\n    Thread.sleep(250)\n\n    val requestB =\n      Request(\n        url = server.url(\"/\"),\n        body = \"b\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    val responseB = client.newCall(requestB).execute()\n    assertThat(responseB.body.string()).isEqualTo(\"b\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  /** Confirm suppressed exceptions that occur while connecting are returned. */\n  @Test fun connectExceptionsAreReturnedAsSuppressed() {\n    val proxySelector = RecordingProxySelector()\n    proxySelector.proxies.add(Proxy(Proxy.Type.HTTP, TestUtil.UNREACHABLE_ADDRESS_IPV4))\n    proxySelector.proxies.add(Proxy.NO_PROXY)\n    server.close()\n\n    client =\n      client\n        .newBuilder()\n        .proxySelector(proxySelector)\n        .readTimeout(Duration.ofMillis(100))\n        .connectTimeout(Duration.ofMillis(100))\n        .build()\n\n    val request = Request(server.url(\"/\"))\n    assertFailsWith<IOException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      expected.assertSuppressed {\n        val suppressed = it.single()\n        assertThat(suppressed).isInstanceOf(IOException::class.java)\n        assertThat(suppressed).isNotSameAs(expected)\n      }\n    }\n  }\n\n  /** Confirm suppressed exceptions that occur after connecting are returned. */\n  @Test fun httpExceptionsAreReturnedAsSuppressed() {\n    server.enqueue(MockResponse.Builder().onRequestStart(CloseSocket()).build())\n    server.enqueue(MockResponse.Builder().onRequestStart(CloseSocket()).build())\n\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns()) // Two routes so we get two failures.\n        .build()\n\n    val request = Request(server.url(\"/\"))\n    assertFailsWith<IOException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      expected.assertSuppressed {\n        val suppressed = it.single()\n        assertThat(suppressed).isInstanceOf(IOException::class.java)\n        assertThat(suppressed).isNotSameAs(expected)\n      }\n    }\n  }\n\n  @Test\n  fun responseRequestIsLastRedirect() {\n    server.enqueue(\n      MockResponse(\n        code = 302,\n        headers = headersOf(\"Location\", \"/b\"),\n      ),\n    )\n    server.enqueue(MockResponse())\n\n    val request = Request(server.url(\"/\"))\n    val call = client.newCall(request)\n    val response = call.execute()\n\n    assertThat(response.request.url.encodedPath).isEqualTo(\"/b\")\n    assertThat(response.request.headers).isEqualTo(headersOf())\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CallLimitsTest.kt",
    "content": "/*\n * Copyright (c) 2026 OkHttp Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport app.cash.burst.Burst\nimport app.cash.burst.burstValues\nimport java.io.IOException\nimport java.util.Random\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.testing.PlatformRule\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Burst\nclass CallLimitsTest(\n  private val protocol: Protocol = burstValues(Protocol.H2_PRIOR_KNOWLEDGE, Protocol.HTTP_1_1),\n) {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server =\n    MockWebServer().apply {\n      protocols = listOf(protocol)\n    }\n\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .protocols(listOf(protocol))\n      .build()\n\n  @Test\n  fun largeStatusLine() {\n    assumeTrue(protocol == Protocol.HTTP_1_1)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 200 ${\"O\".repeat(256 * 1024)}K\")\n        .body(\"I'm not even supposed to be here today.\")\n        .build(),\n    )\n    val call = client.newCall(Request(url = server.url(\"/\")))\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n  }\n\n  /** Use a header that exceeds the limits on its own. */\n  @Test\n  fun largeResponseHeader() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Set-Cookie\", \"a=${\"A\".repeat(256 * 1024)}\")\n        .body(\"I'm not even supposed to be here today.\")\n        .build(),\n    )\n    val call = client.newCall(Request(url = server.url(\"/\")))\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n  }\n\n  /** Use a header that is large even when it is compressed. */\n  @Test\n  fun largeCompressedResponseHeader() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Set-Cookie\", \"a=${randomString(256 * 1024)}\")\n        .body(\"I'm not even supposed to be here today.\")\n        .build(),\n    )\n    val call = client.newCall(Request(url = server.url(\"/\")))\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n  }\n\n  /** A collection of headers that collectively exceed the limits. */\n  @Test\n  fun largeResponseHeadersList() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Set-Cookie\", \"a=${\"A\".repeat(255 * 1024)}\")\n        .addHeader(\"Set-Cookie\", \"b=${\"B\".repeat(1 * 1024)}\")\n        .body(\"I'm not even supposed to be here today.\")\n        .build(),\n    )\n    val call = client.newCall(Request(url = server.url(\"/\")))\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n  }\n\n  private fun randomString(length: Int): String {\n    val byteArray = ByteArray(length)\n    Random(0).nextBytes(byteArray)\n    return byteArray.toByteString().base64()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CallTagsTest.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass CallTagsTest {\n  @JvmField @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private var client = clientTestRule.newClient()\n\n  @Test\n  fun tagsSeededFromRequest() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com/\".toHttpUrl())\n        .tag(Integer::class, 5 as Integer)\n        .tag(String::class, \"hello\")\n        .build()\n    val call = client.newCall(request)\n\n    // Check the Kotlin-focused APIs.\n    assertThat(call.tag(String::class)).isEqualTo(\"hello\")\n    assertThat(call.tag(Integer::class)).isEqualTo(5)\n    assertThat(call.tag(Boolean::class)).isNull()\n    assertThat(call.tag(Any::class)).isNull()\n\n    // Check the Java APIs too.\n    assertThat(call.tag(String::class.java)).isEqualTo(\"hello\")\n    assertThat(call.tag(Integer::class.java)).isEqualTo(5)\n    assertThat(call.tag(Boolean::class.java)).isNull()\n    assertThat(call.tag(Any::class.java)).isNull()\n  }\n\n  @Test\n  fun tagsCanBeComputed() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .build()\n    val call = client.newCall(request)\n\n    // Check the Kotlin-focused APIs.\n    assertThat(call.tag(String::class) { \"a\" }).isEqualTo(\"a\")\n    assertThat(call.tag(String::class) { \"b\" }).isEqualTo(\"a\")\n    assertThat(call.tag(String::class)).isEqualTo(\"a\")\n\n    // Check the Java-focused APIs.\n    assertThat(call.tag(Integer::class) { 1 as Integer }).isEqualTo(1)\n    assertThat(call.tag(Integer::class) { 2 as Integer }).isEqualTo(1)\n    assertThat(call.tag(Integer::class)).isEqualTo(1)\n  }\n\n  @Test\n  fun computedTagsAreNotRetainedInClone() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .build()\n    val callA = client.newCall(request)\n    assertThat(callA.tag(String::class) { \"a\" }).isEqualTo(\"a\")\n    assertThat(callA.tag(String::class) { \"b\" }).isEqualTo(\"a\")\n\n    val callB = callA.clone()\n    assertThat(callB.tag(String::class) { \"c\" }).isEqualTo(\"c\")\n    assertThat(callB.tag(String::class) { \"d\" }).isEqualTo(\"c\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CallTest.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.all\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.doesNotContain\nimport assertk.assertions.hasMessage\nimport assertk.assertions.hasSize\nimport assertk.assertions.index\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isIn\nimport assertk.assertions.isLessThan\nimport assertk.assertions.isNotEmpty\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNotSameAs\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport assertk.assertions.matches\nimport assertk.assertions.prop\nimport assertk.assertions.startsWith\nimport assertk.fail\nimport java.io.FileNotFoundException\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.CookieManager\nimport java.net.CookiePolicy\nimport java.net.HttpCookie\nimport java.net.HttpURLConnection\nimport java.net.InetAddress\nimport java.net.ProtocolException\nimport java.net.Proxy\nimport java.net.SocketTimeoutException\nimport java.net.UnknownHostException\nimport java.net.UnknownServiceException\nimport java.time.Duration\nimport java.util.Arrays\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.Executors\nimport java.util.concurrent.SynchronousQueue\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicBoolean\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.concurrent.atomic.AtomicReference\nimport javax.net.ssl.SSLException\nimport javax.net.ssl.SSLHandshakeException\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLProtocolException\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.QueueDispatcher\nimport mockwebserver3.RecordedRequest\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.SocketEffect.Stall\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallFailed\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.FollowUpDecision\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.RequestBodyEnd\nimport okhttp3.CallEvent.RequestBodyStart\nimport okhttp3.CallEvent.RequestHeadersEnd\nimport okhttp3.CallEvent.RequestHeadersStart\nimport okhttp3.CallEvent.ResponseBodyEnd\nimport okhttp3.CallEvent.ResponseBodyStart\nimport okhttp3.CallEvent.ResponseFailed\nimport okhttp3.CallEvent.ResponseHeadersEnd\nimport okhttp3.CallEvent.ResponseHeadersStart\nimport okhttp3.CallEvent.RetryDecision\nimport okhttp3.CertificatePinner.Companion.pin\nimport okhttp3.Credentials.basic\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.ResponseBody.Companion.asResponseBody\nimport okhttp3.TestUtil.assumeNotWindows\nimport okhttp3.TestUtil.awaitGarbageCollection\nimport okhttp3.internal.DoubleInetAddressDns\nimport okhttp3.internal.RecordingOkAuthenticator\nimport okhttp3.internal.USER_AGENT\nimport okhttp3.internal.addHeaderLenient\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.http.HTTP_EARLY_HINTS\nimport okhttp3.internal.http.HTTP_PROCESSING\nimport okhttp3.internal.http.RecordingProxySelector\nimport okhttp3.java.net.cookiejar.JavaNetCookieJar\nimport okhttp3.okio.LoggingFilesystem\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.ByteString\nimport okio.ForwardingSource\nimport okio.GzipSink\nimport okio.Path.Companion.toPath\nimport okio.buffer\nimport okio.fakefilesystem.FakeFileSystem\nimport okio.use\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.assertArrayEquals\nimport org.junit.jupiter.api.Assumptions.assumeFalse\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junitpioneer.jupiter.RetryingTest\n\n@Timeout(30)\nopen class CallTest {\n  private val fileSystem = FakeFileSystem()\n\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @RegisterExtension\n  val testLogHandler = TestLogHandler(OkHttpClient::class.java)\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @StartStop\n  private val server2 = MockWebServer()\n\n  private var eventRecorder = EventRecorder()\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n      .build()\n  private val callback = RecordingCallback()\n  private val cache =\n    Cache(\n      fileSystem = LoggingFilesystem(fileSystem),\n      directory = \"/cache\".toPath(),\n      maxSize = Int.MAX_VALUE.toLong(),\n    )\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n  }\n\n  @AfterEach\n  fun tearDown() {\n    cache.close()\n    fileSystem.checkNoOpenFiles()\n  }\n\n  @Test\n  fun get() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .clearHeaders()\n        .addHeader(\"content-type: text/plain\")\n        .addHeader(\"content-length\", \"3\")\n        .build(),\n    )\n    val sentAt = System.currentTimeMillis()\n    val recordedResponse = executeSynchronously(\"/\", \"User-Agent\", \"SyncApiTest\")\n    val receivedAt = System.currentTimeMillis()\n    recordedResponse\n      .assertCode(200)\n      .assertSuccessful()\n      .assertHeaders(\n        Headers\n          .Builder()\n          .add(\"content-type\", \"text/plain\")\n          .add(\"content-length\", \"3\")\n          .build(),\n      ).assertBody(\"abc\")\n      .assertSentRequestAtMillis(sentAt, receivedAt)\n      .assertReceivedResponseAtMillis(sentAt, receivedAt)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"GET\")\n    assertThat(recordedRequest.headers[\"User-Agent\"]).isEqualTo(\"SyncApiTest\")\n    assertThat(recordedRequest.body).isNull()\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isNull()\n  }\n\n  @Test\n  fun buildRequestUsingHttpUrl() {\n    server.enqueue(MockResponse())\n    executeSynchronously(\"/\").assertSuccessful()\n  }\n\n  @Test\n  fun invalidScheme() {\n    val requestBuilder = Request.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      requestBuilder.url(\"ftp://hostname/path\")\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Expected URL scheme 'http' or 'https' but was 'ftp'\")\n    }\n  }\n\n  @Test\n  fun invalidPort() {\n    val requestBuilder = Request.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      requestBuilder.url(\"http://localhost:65536/\")\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Invalid URL port: \\\"65536\\\"\")\n    }\n  }\n\n  @Test\n  fun getReturns500() {\n    server.enqueue(MockResponse(code = 500))\n    executeSynchronously(\"/\")\n      .assertCode(500)\n      .assertNotSuccessful()\n  }\n\n  @Test\n  fun get_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    get()\n  }\n\n  @Test\n  fun get_HTTPS() {\n    enableTls()\n    get()\n  }\n\n  @Test\n  fun repeatedHeaderNames() {\n    server.enqueue(\n      MockResponse(\n        headers =\n          headersOf(\n            \"B\",\n            \"123\",\n            \"B\",\n            \"234\",\n          ),\n      ),\n    )\n    executeSynchronously(\"/\", \"A\", \"345\", \"A\", \"456\")\n      .assertCode(200)\n      .assertHeader(\"B\", \"123\", \"234\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers.values(\"A\")).containsExactly(\"345\", \"456\")\n  }\n\n  @Test\n  fun repeatedHeaderNames_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    repeatedHeaderNames()\n  }\n\n  @Test\n  fun getWithRequestBody() {\n    server.enqueue(MockResponse())\n    assertFailsWith<IllegalArgumentException> {\n      Request.Builder().method(\"GET\", \"abc\".toRequestBody(\"text/plain\".toMediaType()))\n    }\n  }\n\n  @Test\n  fun head() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Type\", \"text/plain\"),\n      ),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .head()\n        .header(\"User-Agent\", \"SyncApiTest\")\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertHeader(\"Content-Type\", \"text/plain\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"HEAD\")\n    assertThat(recordedRequest.headers[\"User-Agent\"]).isEqualTo(\"SyncApiTest\")\n    assertThat(recordedRequest.body).isNull()\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isNull()\n  }\n\n  @Test\n  fun headResponseContentLengthIsIgnored() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"Content-Length\", \"100\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    val headRequest =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .head()\n        .build()\n    val response = client.newCall(headRequest).execute()\n    assertThat(response.code).isEqualTo(200)\n    assertArrayEquals(ByteArray(0), response.body.bytes())\n    val getRequest = Request(server.url(\"/\"))\n    executeSynchronously(getRequest)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun headResponseContentEncodingIsIgnored() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"Content-Encoding\", \"chunked\")\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n    val headRequest =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .head()\n        .build()\n    executeSynchronously(headRequest)\n      .assertCode(200)\n      .assertHeader(\"Content-Encoding\", \"chunked\")\n      .assertBody(\"\")\n    val getRequest = Request(server.url(\"/\"))\n    executeSynchronously(getRequest)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun head_HTTPS() {\n    enableTls()\n    head()\n  }\n\n  @Test\n  fun head_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    head()\n  }\n\n  @Test\n  fun post() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"def\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"def\")\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isEqualTo(\"3\")\n    assertThat(recordedRequest.headers[\"Content-Type\"]).isEqualTo(\"text/plain; charset=utf-8\")\n  }\n\n  @Test\n  fun post_HTTPS() {\n    enableTls()\n    post()\n  }\n\n  @Test\n  fun post_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    post()\n  }\n\n  @Test\n  fun postZeroLength() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"POST\", ByteArray(0).toRequestBody(null))\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest.body?.size).isEqualTo(0)\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isEqualTo(\"0\")\n    assertThat(recordedRequest.headers[\"Content-Type\"]).isNull()\n  }\n\n  @Test\n  fun postZerolength_HTTPS() {\n    enableTls()\n    postZeroLength()\n  }\n\n  @Test\n  fun postZerolength_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    postZeroLength()\n  }\n\n  @Test\n  fun postBodyRetransmittedAfterAuthorizationFail() {\n    postBodyRetransmittedAfterAuthorizationFail(\"abc\")\n  }\n\n  @Test\n  fun postBodyRetransmittedAfterAuthorizationFail_HTTPS() {\n    enableTls()\n    postBodyRetransmittedAfterAuthorizationFail(\"abc\")\n  }\n\n  @Test\n  fun postBodyRetransmittedAfterAuthorizationFail_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    postBodyRetransmittedAfterAuthorizationFail(\"abc\")\n  }\n\n  /** Don't explode when resending an empty post. https://github.com/square/okhttp/issues/1131  */\n  @Test\n  fun postEmptyBodyRetransmittedAfterAuthorizationFail() {\n    postBodyRetransmittedAfterAuthorizationFail(\"\")\n  }\n\n  @Test\n  fun postEmptyBodyRetransmittedAfterAuthorizationFail_HTTPS() {\n    enableTls()\n    postBodyRetransmittedAfterAuthorizationFail(\"\")\n  }\n\n  @Test\n  fun postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    postBodyRetransmittedAfterAuthorizationFail(\"\")\n  }\n\n  private fun postBodyRetransmittedAfterAuthorizationFail(body: String) {\n    server.enqueue(MockResponse(code = 401))\n    server.enqueue(MockResponse())\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"POST\", body.toRequestBody(null))\n        .build()\n    val credential = basic(\"jesse\", \"secret\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(credential, null))\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val recordedRequest1 = server.takeRequest()\n    assertThat(recordedRequest1.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest1.body?.utf8()).isEqualTo(body)\n    assertThat(recordedRequest1.headers[\"Authorization\"]).isNull()\n    val recordedRequest2 = server.takeRequest()\n    assertThat(recordedRequest2.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest2.body?.utf8()).isEqualTo(body)\n    assertThat(recordedRequest2.headers[\"Authorization\"]).isEqualTo(credential)\n  }\n\n  @Test\n  fun attemptAuthorization20Times() {\n    for (i in 0..19) {\n      server.enqueue(MockResponse(code = 401))\n    }\n    server.enqueue(MockResponse(body = \"Success!\"))\n    val credential = basic(\"jesse\", \"secret\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(credential, null))\n        .build()\n    executeSynchronously(\"/\")\n      .assertCode(200)\n      .assertBody(\"Success!\")\n  }\n\n  @Test\n  fun doesNotAttemptAuthorization21Times() {\n    for (i in 0..20) {\n      server.enqueue(MockResponse(code = 401))\n    }\n    val credential = basic(\"jesse\", \"secret\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(credential, null))\n        .build()\n    assertFailsWith<IOException> {\n      client.newCall(Request.Builder().url(server.url(\"/0\")).build()).execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Too many follow-up requests: 21\")\n    }\n  }\n\n  /**\n   * We had a bug where we were passing a null route to the authenticator.\n   * https://github.com/square/okhttp/issues/3809\n   */\n  @Test\n  fun authenticateWithNoConnection() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(401)\n        .addHeader(\"Connection\", \"close\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val authenticator = RecordingOkAuthenticator(null, null)\n    client =\n      client\n        .newBuilder()\n        .authenticator(authenticator)\n        .build()\n    executeSynchronously(\"/\")\n      .assertCode(401)\n    assertThat(authenticator.onlyRoute()).isNotNull()\n  }\n\n  @Test\n  fun delete() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .delete()\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"DELETE\")\n    assertThat(recordedRequest.body?.size).isEqualTo(0)\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isEqualTo(\"0\")\n    assertThat(recordedRequest.headers[\"Content-Type\"]).isNull()\n  }\n\n  @Test\n  fun delete_HTTPS() {\n    enableTls()\n    delete()\n  }\n\n  @Test\n  fun delete_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    delete()\n  }\n\n  @Test\n  fun deleteWithRequestBody() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"DELETE\", \"def\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"DELETE\")\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"def\")\n  }\n\n  @Test\n  fun put() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .put(\"def\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"PUT\")\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"def\")\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isEqualTo(\"3\")\n    assertThat(recordedRequest.headers[\"Content-Type\"]).isEqualTo(\n      \"text/plain; charset=utf-8\",\n    )\n  }\n\n  @Test\n  fun put_HTTPS() {\n    enableTls()\n    put()\n  }\n\n  @Test\n  fun put_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    put()\n  }\n\n  @Test\n  fun patch() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .patch(\"def\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"PATCH\")\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"def\")\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isEqualTo(\"3\")\n    assertThat(recordedRequest.headers[\"Content-Type\"]).isEqualTo(\"text/plain; charset=utf-8\")\n  }\n\n  @Test\n  fun patch_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    patch()\n  }\n\n  @Test\n  fun patch_HTTPS() {\n    enableTls()\n    patch()\n  }\n\n  @Test\n  fun customMethodWithBody() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"CUSTOM\", \"def\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"CUSTOM\")\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"def\")\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isEqualTo(\"3\")\n    assertThat(recordedRequest.headers[\"Content-Type\"])\n      .isEqualTo(\"text/plain; charset=utf-8\")\n  }\n\n  @Test\n  fun unspecifiedRequestBodyContentTypeDoesNotGetDefault() {\n    server.enqueue(MockResponse())\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"POST\", \"abc\".toRequestBody(null))\n        .build()\n    executeSynchronously(request).assertCode(200)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers[\"Content-Type\"]).isNull()\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isEqualTo(\"3\")\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun illegalToExecuteTwice() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Type\", \"text/plain\"),\n        body = \"abc\",\n      ),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"User-Agent\", \"SyncApiTest\")\n        .build()\n    val call = client.newCall(request)\n    val response = call.execute()\n    response.body.close()\n    assertFailsWith<IllegalStateException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Already Executed\")\n    }\n    assertFailsWith<IllegalStateException> {\n      call.enqueue(callback)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Already Executed\")\n    }\n    assertThat(server.takeRequest().headers[\"User-Agent\"]).isEqualTo(\"SyncApiTest\")\n  }\n\n  @Test\n  fun illegalToExecuteTwice_Async() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Type\", \"text/plain\"),\n        body = \"abc\",\n      ),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"User-Agent\", \"SyncApiTest\")\n        .build()\n    val call = client.newCall(request)\n    call.enqueue(callback)\n    assertFailsWith<IllegalStateException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Already Executed\")\n    }\n    assertFailsWith<IllegalStateException> {\n      call.enqueue(callback)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Already Executed\")\n    }\n    assertThat(server.takeRequest().headers[\"User-Agent\"]).isEqualTo(\"SyncApiTest\")\n    callback.await(request.url).assertSuccessful()\n  }\n\n  @Test\n  fun legalToExecuteTwiceCloning() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    val request = Request(server.url(\"/\"))\n    val call = client.newCall(request)\n    val response1 = call.execute()\n    val cloned = call.clone()\n    val response2 = cloned.execute()\n    assertThat(\"abc\").isEqualTo(response1.body.string())\n    assertThat(\"def\").isEqualTo(response2.body.string())\n  }\n\n  @Test\n  fun legalToExecuteTwiceCloning_Async() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    val request = Request(server.url(\"/\"))\n    val call = client.newCall(request)\n    call.enqueue(callback)\n    val cloned = call.clone()\n    cloned.enqueue(callback)\n    val firstResponse = callback.await(request.url).assertSuccessful()\n    val secondResponse = callback.await(request.url).assertSuccessful()\n    val bodies: MutableSet<String?> = LinkedHashSet()\n    bodies.add(firstResponse.body)\n    bodies.add(secondResponse.body)\n    assertThat(bodies).contains(\"abc\")\n    assertThat(bodies).contains(\"def\")\n  }\n\n  @Test\n  fun get_Async() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Type\", \"text/plain\"),\n        body = \"abc\",\n      ),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"User-Agent\", \"AsyncApiTest\")\n        .build()\n    client.newCall(request).enqueue(callback)\n    callback\n      .await(request.url)\n      .assertCode(200)\n      .assertHeader(\"Content-Type\", \"text/plain\")\n      .assertBody(\"abc\")\n    assertThat(server.takeRequest().headers[\"User-Agent\"]).isEqualTo(\"AsyncApiTest\")\n  }\n\n  @Test\n  fun exceptionThrownByOnResponseIsRedactedAndLogged() {\n    server.enqueue(MockResponse())\n    val request = Request(server.url(\"/secret\"))\n    client.newCall(request).enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          fail(\"\")\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ): Unit = throw IOException(\"a\")\n      },\n    )\n    assertThat(testLogHandler.take())\n      .isEqualTo(\"INFO: Callback failure for call to \" + server.url(\"/\") + \"...\")\n  }\n\n  @Test\n  fun connectionPooling() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    server.enqueue(MockResponse(body = \"ghi\"))\n    executeSynchronously(\"/a\").assertBody(\"abc\")\n    executeSynchronously(\"/b\").assertBody(\"def\")\n    executeSynchronously(\"/c\").assertBody(\"ghi\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  /**\n   * Each OkHttpClient used to get its own instance of NullProxySelector, and because these weren't\n   * equal their connections weren't pooled. That's a nasty performance bug!\n   *\n   * https://github.com/square/okhttp/issues/5519\n   */\n  @Test\n  fun connectionPoolingWithFreshClientSamePool() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    server.enqueue(MockResponse(body = \"ghi\"))\n    client =\n      OkHttpClient\n        .Builder()\n        .connectionPool(client.connectionPool)\n        .proxy(server.proxyAddress)\n        .build()\n    executeSynchronously(\"/a\").assertBody(\"abc\")\n    client =\n      OkHttpClient\n        .Builder()\n        .connectionPool(client.connectionPool)\n        .proxy(server.proxyAddress)\n        .build()\n    executeSynchronously(\"/b\").assertBody(\"def\")\n    client =\n      OkHttpClient\n        .Builder()\n        .connectionPool(client.connectionPool)\n        .proxy(server.proxyAddress)\n        .build()\n    executeSynchronously(\"/c\").assertBody(\"ghi\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun connectionPoolingWithClientBuiltOffProxy() {\n    client =\n      OkHttpClient\n        .Builder()\n        .proxy(server.proxyAddress)\n        .build()\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    server.enqueue(MockResponse(body = \"ghi\"))\n    client = client.newBuilder().build()\n    executeSynchronously(\"/a\").assertBody(\"abc\")\n    client = client.newBuilder().build()\n    executeSynchronously(\"/b\").assertBody(\"def\")\n    client = client.newBuilder().build()\n    executeSynchronously(\"/c\").assertBody(\"ghi\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun connectionPooling_Async() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    server.enqueue(MockResponse(body = \"ghi\"))\n    client.newCall(Request.Builder().url(server.url(\"/a\")).build()).enqueue(callback)\n    callback.await(server.url(\"/a\")).assertBody(\"abc\")\n    client.newCall(Request.Builder().url(server.url(\"/b\")).build()).enqueue(callback)\n    callback.await(server.url(\"/b\")).assertBody(\"def\")\n    client.newCall(Request.Builder().url(server.url(\"/c\")).build()).enqueue(callback)\n    callback.await(server.url(\"/c\")).assertBody(\"ghi\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun connectionReuseWhenResponseBodyConsumed_Async() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    val request = Request.Builder().url(server.url(\"/a\")).build()\n    client.newCall(request).enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ): Unit = throw AssertionError()\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          val bytes = response.body.byteStream()\n          assertThat(bytes.read()).isEqualTo('a'.code)\n          assertThat(bytes.read()).isEqualTo('b'.code)\n          assertThat(bytes.read()).isEqualTo('c'.code)\n\n          // This request will share a connection with 'A' cause it's all done.\n          client.newCall(Request.Builder().url(server.url(\"/b\")).build()).enqueue(callback)\n        }\n      },\n    )\n    callback.await(server.url(\"/b\")).assertCode(200).assertBody(\"def\")\n    // New connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Connection reuse!\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun timeoutsUpdatedOnReusedConnections() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"def\")\n        .throttleBody(1, 750, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n\n    // First request: time out after 1s.\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofSeconds(1))\n        .build()\n    executeSynchronously(\"/a\").assertBody(\"abc\")\n\n    // Second request: time out after 250ms.\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(250))\n        .build()\n    val request = Request.Builder().url(server.url(\"/b\")).build()\n    val response = client.newCall(request).execute()\n    val bodySource = response.body.source()\n    assertThat(bodySource.readByte()).isEqualTo('d'.code.toByte())\n\n    // The second byte of this request will be delayed by 750ms so we should time out after 250ms.\n    val startNanos = System.nanoTime()\n    bodySource.use {\n      assertFailsWith<IOException> {\n        bodySource.readByte()\n      }.also { expected ->\n        // Timed out as expected.\n        val elapsedNanos = System.nanoTime() - startNanos\n        val elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos)\n        assertThat(elapsedMillis).isLessThan(500)\n      }\n    }\n  }\n\n  /** https://github.com/square/okhttp/issues/442  */\n  @Test\n  fun tlsTimeoutsNotRetried() {\n    enableTls()\n    server.enqueue(MockResponse.Builder().onResponseStart(Stall).build())\n    server.enqueue(MockResponse(body = \"unreachable!\"))\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(100))\n        .build()\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    assertFailsWith<InterruptedIOException> {\n      // If this succeeds, too many requests were made.\n      client.newCall(request).execute()\n    }\n  }\n\n  /**\n   * Make a request with two routes. The first route will time out because it's connecting to a\n   * special address that never connects. The automatic retry will succeed.\n   */\n  @Test\n  fun connectTimeoutsAttemptsAlternateRoute() {\n    val proxySelector = RecordingProxySelector()\n    proxySelector.proxies.add(Proxy(Proxy.Type.HTTP, TestUtil.UNREACHABLE_ADDRESS_IPV4))\n    proxySelector.proxies.add(server.proxyAddress)\n    server.enqueue(MockResponse(body = \"success!\"))\n    client =\n      client\n        .newBuilder()\n        .proxySelector(proxySelector)\n        .readTimeout(Duration.ofMillis(100))\n        .connectTimeout(Duration.ofMillis(100))\n        .build()\n    val request = Request.Builder().url(\"http://android.com/\").build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"success!\")\n\n    assertThat(proxySelector.failures)\n      .all {\n        hasSize(1)\n        index(0).matches(\".* Connect timed out\".toRegex(RegexOption.IGNORE_CASE))\n      }\n  }\n\n  /** https://github.com/square/okhttp/issues/4875  */\n  @Test\n  fun interceptorRecoversWhenRoutesExhausted() {\n    server.enqueue(MockResponse.Builder().onRequestStart(CloseSocket()).build())\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            try {\n              chain.proceed(chain.request())\n              throw AssertionError()\n            } catch (expected: IOException) {\n              chain.proceed(chain.request())\n            }\n          },\n        ).build()\n    val request = Request(server.url(\"/\"))\n    executeSynchronously(request)\n      .assertCode(200)\n  }\n\n  /** https://github.com/square/okhttp/issues/4761  */\n  @Test\n  fun interceptorCallsProceedWithoutClosingPriorResponse() {\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            val response =\n              chain.proceed(\n                chain.request(),\n              )\n            assertFailsWith<IllegalStateException> {\n              chain.proceed(chain.request())\n            }.also { expected ->\n              assertThat(expected.message!!).contains(\"please call response.close()\")\n            }\n            response\n          },\n        ).build()\n    val request = Request(server.url(\"/\"))\n    executeSynchronously(request).assertBody(\"abc\")\n  }\n\n  /**\n   * Make a request with two routes. The first route will fail because the null server connects but\n   * never responds. The manual retry will succeed.\n   */\n  @Test\n  fun readTimeoutFails() {\n    server.enqueue(MockResponse.Builder().onRequestStart(Stall).build())\n    server2.enqueue(\n      MockResponse(body = \"success!\"),\n    )\n    val proxySelector = RecordingProxySelector()\n    proxySelector.proxies.add(server.proxyAddress)\n    proxySelector.proxies.add(server2.proxyAddress)\n    client =\n      client\n        .newBuilder()\n        .proxySelector(proxySelector)\n        .readTimeout(Duration.ofMillis(100))\n        .build()\n    val request = Request.Builder().url(\"http://android.com/\").build()\n    executeSynchronously(request)\n      .assertFailure(SocketTimeoutException::class.java)\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertBody(\"success!\")\n  }\n\n  /** https://github.com/square/okhttp/issues/1801  */\n  @Test\n  fun asyncCallEngineInitialized() {\n    val c =\n      client\n        .newBuilder()\n        .addInterceptor(Interceptor { throw IOException() })\n        .build()\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    c.newCall(request).enqueue(callback)\n    val response = callback.await(request.url)\n    assertThat(response.request).isEqualTo(request)\n  }\n\n  @Test\n  fun reusedSinksGetIndependentTimeoutInstances() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n\n    // Call 1: set a deadline on the request body.\n    val requestBody1: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType = \"text/plain\".toMediaType()\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.writeUtf8(\"abc\")\n          sink.timeout().deadline(5, TimeUnit.SECONDS)\n        }\n      }\n    val request1 =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"POST\", requestBody1)\n        .build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.code).isEqualTo(200)\n\n    // Call 2: check for the absence of a deadline on the request body.\n    val requestBody2: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType = \"text/plain\".toMediaType()\n\n        override fun writeTo(sink: BufferedSink) {\n          assertThat(sink.timeout().hasDeadline()).isFalse()\n          sink.writeUtf8(\"def\")\n        }\n      }\n    val request2 =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"POST\", requestBody2)\n        .build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.code).isEqualTo(200)\n\n    // Use sequence numbers to confirm the connection was pooled.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun reusedSourcesGetIndependentTimeoutInstances() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n\n    // Call 1: set a deadline on the response body.\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request1).execute()\n    val body1 = response1.body.source()\n    assertThat(body1.readUtf8()).isEqualTo(\"abc\")\n    body1.timeout().deadline(5, TimeUnit.SECONDS)\n\n    // Call 2: check for the absence of a deadline on the request body.\n    val request2 = Request.Builder().url(server.url(\"/\")).build()\n    val response2 = client.newCall(request2).execute()\n    val body2 = response2.body.source()\n    assertThat(body2.readUtf8()).isEqualTo(\"def\")\n    assertThat(body2.timeout().hasDeadline()).isFalse()\n\n    // Use sequence numbers to confirm the connection was pooled.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun tls() {\n    enableTls()\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Type\", \"text/plain\"),\n        body = \"abc\",\n      ),\n    )\n    executeSynchronously(\"/\").assertHandshake()\n  }\n\n  @Test\n  fun tls_Async() {\n    enableTls()\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Type\", \"text/plain\"),\n        body = \"abc\",\n      ),\n    )\n    val request = Request(server.url(\"/\"))\n    client.newCall(request).enqueue(callback)\n    callback.await(request.url).assertHandshake()\n  }\n\n  @Test\n  fun recoverWhenRetryOnConnectionFailureIsTrue() {\n    // Set to 2 because the seeding request will count down before the retried request does.\n    val requestFinished = CountDownLatch(2)\n    val dispatcher: QueueDispatcher =\n      object : QueueDispatcher() {\n        override fun dispatch(request: RecordedRequest): MockResponse {\n          if (peek().onResponseStart is CloseSocket) {\n            requestFinished.await()\n          }\n          return super.dispatch(request)\n        }\n      }\n    dispatcher.enqueue(MockResponse(body = \"seed connection pool\"))\n    dispatcher.enqueue(MockResponse.Builder().onResponseStart(CloseSocket()).build())\n    dispatcher.enqueue(MockResponse(body = \"retry success\"))\n    server.dispatcher = dispatcher\n    val requestFinishedListener =\n      object : EventListener() {\n        override fun requestHeadersEnd(\n          call: Call,\n          request: Request,\n        ) {\n          requestFinished.countDown()\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .eventListenerFactory(\n          clientTestRule.wrap(eventRecorder.eventListener + requestFinishedListener),\n        ).build()\n    assertThat(client.retryOnConnectionFailure).isTrue()\n    executeSynchronously(\"/\").assertBody(\"seed connection pool\")\n    executeSynchronously(\"/\").assertBody(\"retry success\")\n\n    // The call that seeds the connection pool.\n    eventRecorder.removeUpToEvent(CallEnd::class.java)\n\n    // The ResponseFailed event is not necessarily fatal!\n    eventRecorder.removeUpToEvent(ConnectionAcquired::class.java)\n    eventRecorder.removeUpToEvent(ResponseFailed::class.java)\n    eventRecorder.removeUpToEvent(ConnectionReleased::class.java)\n    eventRecorder.removeUpToEvent(ConnectionAcquired::class.java)\n    eventRecorder.removeUpToEvent(ConnectionReleased::class.java)\n    eventRecorder.removeUpToEvent(CallEnd::class.java)\n  }\n\n  @Test\n  fun recoverWhenRetryOnConnectionFailureIsTrue_HTTP2() {\n    enableProtocol(Protocol.HTTP_2)\n    recoverWhenRetryOnConnectionFailureIsTrue()\n  }\n\n  @Test\n  fun noRecoverWhenRetryOnConnectionFailureIsFalse() {\n    server.enqueue(MockResponse(body = \"seed connection pool\"))\n    server.enqueue(MockResponse.Builder().onResponseStart(CloseSocket()).build())\n    server.enqueue(MockResponse(body = \"unreachable!\"))\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .retryOnConnectionFailure(false)\n        .build()\n    executeSynchronously(\"/\").assertBody(\"seed connection pool\")\n\n    // If this succeeds, too many requests were made.\n    executeSynchronously(\"/\")\n      .assertFailure(IOException::class.java)\n      .assertFailureMatches(\n        \"stream was reset: CANCEL\",\n        \"unexpected end of stream on \" + server.url(\"/\").redact(),\n      )\n  }\n\n  @RetryingTest(5)\n  @Flaky\n  fun recoverWhenRetryOnConnectionFailureIsFalse_HTTP2() {\n    enableProtocol(Protocol.HTTP_2)\n    noRecoverWhenRetryOnConnectionFailureIsFalse()\n  }\n\n  @Test\n  fun tlsHandshakeFailure_noFallbackByDefault() {\n    platform.assumeNotBouncyCastle()\n\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    server.enqueue(MockResponse(body = \"response that will never be received\"))\n    val response = executeSynchronously(\"/\")\n    response.assertFailure(\n      // JDK 11 response to the FAIL_HANDSHAKE:\n      SSLException::class.java,\n      // RI response to the FAIL_HANDSHAKE:\n      SSLProtocolException::class.java,\n      // Android's response to the FAIL_HANDSHAKE:\n      SSLHandshakeException::class.java,\n    )\n    assertThat(client.connectionSpecs).doesNotContain(ConnectionSpec.COMPATIBLE_TLS)\n  }\n\n  @Test\n  fun recoverFromTlsHandshakeFailure() {\n    platform.assumeNotBouncyCastle()\n\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    server.enqueue(MockResponse(body = \"abc\"))\n    client =\n      client\n        .newBuilder()\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .connectionSpecs(\n          // Attempt RESTRICTED_TLS then fall back to MODERN_TLS.\n          listOf(\n            ConnectionSpec.RESTRICTED_TLS,\n            ConnectionSpec.MODERN_TLS,\n          ),\n        ).sslSocketFactory(\n          suppressTlsFallbackClientSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    executeSynchronously(\"/\").assertBody(\"abc\")\n  }\n\n  @Test\n  fun recoverFromTlsHandshakeFailure_tlsFallbackScsvEnabled() {\n    platform.assumeNotConscrypt()\n    val tlsFallbackScsv = \"TLS_FALLBACK_SCSV\"\n    val supportedCiphers = listOf(*handshakeCertificates.sslSocketFactory().supportedCipherSuites)\n    if (!supportedCiphers.contains(tlsFallbackScsv)) {\n      // This only works if the client socket supports TLS_FALLBACK_SCSV.\n      return\n    }\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    val clientSocketFactory =\n      RecordingSSLSocketFactory(\n        handshakeCertificates.sslSocketFactory(),\n      )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          clientSocketFactory,\n          handshakeCertificates.trustManager,\n        ) // Attempt RESTRICTED_TLS then fall back to MODERN_TLS.\n        .connectionSpecs(listOf(ConnectionSpec.RESTRICTED_TLS, ConnectionSpec.MODERN_TLS))\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    assertFailsWith<SSLHandshakeException> {\n      client.newCall(request).execute()\n    }\n    val firstSocket = clientSocketFactory.socketsCreated[0]\n    assertThat(firstSocket.enabledCipherSuites)\n      .doesNotContain(tlsFallbackScsv)\n    val secondSocket = clientSocketFactory.socketsCreated[1]\n    assertThat(secondSocket.enabledCipherSuites)\n      .contains(tlsFallbackScsv)\n  }\n\n  @Test\n  fun recoverFromTlsHandshakeFailure_Async() {\n    platform.assumeNotBouncyCastle()\n\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    server.enqueue(MockResponse(body = \"abc\"))\n    client =\n      client\n        .newBuilder()\n        .hostnameVerifier(\n          RecordingHostnameVerifier(),\n        ) // Attempt RESTRICTED_TLS then fall back to MODERN_TLS.\n        .connectionSpecs(listOf(ConnectionSpec.RESTRICTED_TLS, ConnectionSpec.MODERN_TLS))\n        .sslSocketFactory(\n          suppressTlsFallbackClientSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    val request = Request(server.url(\"/\"))\n    client.newCall(request).enqueue(callback)\n    callback.await(request.url).assertBody(\"abc\")\n  }\n\n  @Test\n  fun noRecoveryFromTlsHandshakeFailureWhenTlsFallbackIsDisabled() {\n    platform.assumeNotBouncyCastle()\n\n    client =\n      client\n        .newBuilder()\n        .connectionSpecs(listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT))\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .sslSocketFactory(\n          suppressTlsFallbackClientSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    assertFailsWith<IOException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      when (expected) {\n        is SSLProtocolException -> {\n          // RI response to the FAIL_HANDSHAKE\n        }\n\n        is SSLHandshakeException -> {\n          // Android's response to the FAIL_HANDSHAKE\n        }\n\n        is SSLException -> {\n          // JDK 11 response to the FAIL_HANDSHAKE\n          val jvmVersion = System.getProperty(\"java.specification.version\")\n          assertThat(jvmVersion).isEqualTo(\"11\")\n        }\n\n        else -> {\n          throw expected\n        }\n      }\n    }\n  }\n\n  @Test\n  fun tlsHostnameVerificationFailure() {\n    assumeNotWindows()\n    platform.assumeNotBouncyCastle()\n\n    server.enqueue(MockResponse())\n    val serverCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"localhost\") // Unusued for hostname verification.\n        .addSubjectAlternativeName(\"wronghostname\")\n        .build()\n    val serverCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(serverCertificate)\n        .build()\n    val clientCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(serverCertificate.certificate)\n        .build()\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n        .build()\n    server.useHttps(serverCertificates.sslSocketFactory())\n    executeSynchronously(\"/\")\n      .assertFailureMatches(\"(?s)Hostname localhost not verified.*\")\n  }\n\n  /**\n   * Anonymous cipher suites were disabled in OpenJDK because they're rarely used and permit\n   * man-in-the-middle attacks. https://bugs.openjdk.java.net/browse/JDK-8212823\n   */\n  @Test\n  fun anonCipherSuiteUnsupported() {\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n\n    // The _anon_ suites became unsupported in \"1.8.0_201\" and \"11.0.2\".\n    assumeFalse(\n      System.getProperty(\"java.version\", \"unknown\").matches(Regex(\"1\\\\.8\\\\.0_1\\\\d\\\\d\")),\n    )\n    server.enqueue(MockResponse())\n    val cipherSuite = CipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256\n    val clientCertificates =\n      HandshakeCertificates\n        .Builder()\n        .build()\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          socketFactoryWithCipherSuite(clientCertificates.sslSocketFactory(), cipherSuite),\n          clientCertificates.trustManager,\n        ).connectionSpecs(\n          listOf(\n            ConnectionSpec\n              .Builder(ConnectionSpec.MODERN_TLS)\n              .cipherSuites(cipherSuite)\n              .build(),\n          ),\n        ).build()\n    val serverCertificates =\n      HandshakeCertificates\n        .Builder()\n        .build()\n    server.useHttps(\n      socketFactoryWithCipherSuite(serverCertificates.sslSocketFactory(), cipherSuite),\n    )\n    executeSynchronously(\"/\")\n      .assertFailure(SSLHandshakeException::class.java)\n  }\n\n  @Test\n  fun cleartextCallsFailWhenCleartextIsDisabled() {\n    // Configure the client with only TLS configurations. No cleartext!\n    client =\n      client\n        .newBuilder()\n        .connectionSpecs(listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))\n        .build()\n    server.enqueue(MockResponse())\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    assertFailsWith<UnknownServiceException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"CLEARTEXT communication not enabled for client\",\n      )\n    }\n  }\n\n  @Test\n  fun httpsCallsFailWhenProtocolIsH2PriorKnowledge() {\n    client =\n      client\n        .newBuilder()\n        .protocols(listOf(Protocol.H2_PRIOR_KNOWLEDGE))\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse())\n    val call = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<UnknownServiceException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"H2_PRIOR_KNOWLEDGE cannot be used with HTTPS\")\n    }\n  }\n\n  @Test\n  fun setFollowSslRedirectsFalse() {\n    enableTls()\n    server.enqueue(\n      MockResponse(\n        code = 301,\n        headers = headersOf(\"Location\", \"http://square.com\"),\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .followSslRedirects(false)\n        .build()\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(301)\n    response.body.close()\n  }\n\n  @Test\n  fun matchingPinnedCertificate() {\n    // Fails on 11.0.1 https://github.com/square/okhttp/issues/4703\n    enableTls()\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n\n    // Make a first request without certificate pinning. Use it to collect certificates to pin.\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request1).execute()\n    val certificatePinnerBuilder = CertificatePinner.Builder()\n    for (certificate in response1.handshake!!.peerCertificates) {\n      certificatePinnerBuilder.add(\n        server.hostName,\n        pin(certificate),\n      )\n    }\n    response1.body.close()\n\n    // Make another request with certificate pinning. It should complete normally.\n    client =\n      client\n        .newBuilder()\n        .certificatePinner(certificatePinnerBuilder.build())\n        .build()\n    val request2 = Request.Builder().url(server.url(\"/\")).build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response1.handshake).isNotSameAs(\n      response2.handshake,\n    )\n    response2.body.close()\n  }\n\n  @Test\n  fun unmatchingPinnedCertificate() {\n    enableTls()\n    server.enqueue(MockResponse())\n\n    // Pin publicobject.com's cert.\n    client =\n      client\n        .newBuilder()\n        .certificatePinner(\n          CertificatePinner\n            .Builder()\n            .add(server.hostName, \"sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=\")\n            .build(),\n        ).build()\n\n    // When we pin the wrong certificate, connectivity fails.\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    assertFailsWith<SSLPeerUnverifiedException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.message!!).startsWith(\"Certificate pinning failure!\")\n    }\n  }\n\n  @Test\n  fun post_Async() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"def\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    client.newCall(request).enqueue(callback)\n    callback\n      .await(request.url)\n      .assertCode(200)\n      .assertBody(\"abc\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"def\")\n    assertThat(recordedRequest.headers[\"Content-Length\"]).isEqualTo(\"3\")\n    assertThat(recordedRequest.headers[\"Content-Type\"]).isEqualTo(\"text/plain; charset=utf-8\")\n  }\n\n  @Test\n  fun serverHalfClosingBeforeResponse() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onResponseStart(\n          CloseSocket(\n            closeSocket = false,\n            shutdownOutput = true,\n          ),\n        ).build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val client =\n      client\n        .newBuilder()\n        .retryOnConnectionFailure(false)\n        .build()\n\n    // Seed the connection pool so we have something that can fail.\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"abc\")\n\n    eventRecorder.clearAllEvents()\n\n    val request2 =\n      Request(\n        url = server.url(\"/\"),\n        body = \"body!\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    assertFailsWith<IOException> {\n      client.newCall(request2).execute()\n    }.also { expected ->\n      assertThat(expected.message!!).startsWith(\"unexpected end of stream on http://\")\n    }\n\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      RequestBodyStart::class,\n      RequestBodyEnd::class,\n      ResponseFailed::class,\n      RetryDecision::class,\n      ConnectionReleased::class,\n      CallFailed::class,\n    )\n    assertThat(eventRecorder.findEvent<RetryDecision>()).all {\n      prop(RetryDecision::retry).isFalse()\n    }\n    eventRecorder.clearAllEvents()\n\n    val response3 = client.newCall(request1).execute()\n    assertThat(response3.body.string()).isEqualTo(\"abc\")\n\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n\n    val get = server.takeRequest()\n    assertThat(get.exchangeIndex).isEqualTo(0)\n\n    val post1 = server.takeRequest()\n    assertThat(post1.body?.utf8()).isEqualTo(\"body!\")\n    assertThat(post1.exchangeIndex).isEqualTo(1)\n\n    val get2 = server.takeRequest()\n    assertThat(get2.exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun postBodyRetransmittedOnFailureRecovery() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse.Builder().onResponseStart(CloseSocket()).build())\n    server.enqueue(MockResponse(body = \"def\"))\n\n    // Seed the connection pool so we have something that can fail.\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"abc\")\n    val request2 =\n      Request(\n        server.url(\"/\"),\n        body = \"body!\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"def\")\n    val get = server.takeRequest()\n    assertThat(get.exchangeIndex).isEqualTo(0)\n    val post1 = server.takeRequest()\n    assertThat(post1.body?.utf8()).isEqualTo(\"body!\")\n    assertThat(post1.exchangeIndex).isEqualTo(1)\n    val post2 = server.takeRequest()\n    assertThat(post2.body?.utf8()).isEqualTo(\"body!\")\n    assertThat(post2.exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun postBodyRetransmittedOnFailureRecovery_HTTP2() {\n    enableProtocol(Protocol.HTTP_2)\n    postBodyRetransmittedOnFailureRecovery()\n  }\n\n  @Test\n  fun cacheHit() {\n    server.enqueue(\n      MockResponse(\n        headers =\n          headersOf(\n            \"ETag\",\n            \"v1\",\n            \"Cache-Control\",\n            \"max-age=60\",\n            \"Vary\",\n            \"Accept-Charset\",\n          ),\n        body = \"A\",\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n\n    // Store a response in the cache.\n    val url = server.url(\"/\")\n    val request1SentAt = System.currentTimeMillis()\n    executeSynchronously(\"/\", \"Accept-Language\", \"fr-CA\", \"Accept-Charset\", \"UTF-8\")\n      .assertCode(200)\n      .assertBody(\"A\")\n    val request1ReceivedAt = System.currentTimeMillis()\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isNull()\n\n    // Hit that stored response. It's different, but Vary says it doesn't matter.\n    Thread.sleep(10) // Make sure the timestamps are unique.\n    val cacheHit =\n      executeSynchronously(\n        \"/\",\n        \"Accept-Language\",\n        \"en-US\",\n        \"Accept-Charset\",\n        \"UTF-8\",\n      )\n\n    // Check the merged response. The request is the application's original request.\n    cacheHit\n      .assertCode(200)\n      .assertBody(\"A\")\n      .assertHeaders(\n        Headers\n          .Builder()\n          .add(\"ETag\", \"v1\")\n          .add(\"Cache-Control\", \"max-age=60\")\n          .add(\"Vary\", \"Accept-Charset\")\n          .add(\"Content-Length\", \"1\")\n          .build(),\n      ).assertRequestUrl(url)\n      .assertRequestHeader(\"Accept-Language\", \"en-US\")\n      .assertRequestHeader(\"Accept-Charset\", \"UTF-8\")\n      .assertSentRequestAtMillis(request1SentAt, request1ReceivedAt)\n      .assertReceivedResponseAtMillis(request1SentAt, request1ReceivedAt)\n\n    // Check the cached response. Its request contains only the saved Vary headers.\n    cacheHit\n      .cacheResponse()\n      .assertCode(200)\n      .assertHeaders(\n        Headers\n          .Builder()\n          .add(\"ETag\", \"v1\")\n          .add(\"Cache-Control\", \"max-age=60\")\n          .add(\"Vary\", \"Accept-Charset\")\n          .add(\"Content-Length\", \"1\")\n          .build(),\n      ).assertRequestMethod(\"GET\")\n      .assertRequestUrl(url)\n      .assertRequestHeader(\"Accept-Language\")\n      .assertRequestHeader(\"Accept-Charset\", \"UTF-8\")\n      .assertSentRequestAtMillis(request1SentAt, request1ReceivedAt)\n      .assertReceivedResponseAtMillis(request1SentAt, request1ReceivedAt)\n    cacheHit.assertNoNetworkResponse()\n  }\n\n  @Test\n  fun conditionalCacheHit() {\n    server.enqueue(\n      MockResponse(\n        headers =\n          headersOf(\n            \"ETag\",\n            \"v1\",\n            \"Vary\",\n            \"Accept-Charset\",\n            \"Donut\",\n            \"a\",\n          ),\n        body = \"A\",\n      ),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"Donut: b\")\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n\n    // Store a response in the cache.\n    val request1SentAt = System.currentTimeMillis()\n    executeSynchronously(\"/\", \"Accept-Language\", \"fr-CA\", \"Accept-Charset\", \"UTF-8\")\n      .assertCode(200)\n      .assertHeader(\"Donut\", \"a\")\n      .assertBody(\"A\")\n    val request1ReceivedAt = System.currentTimeMillis()\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isNull()\n\n    // Hit that stored response. It's different, but Vary says it doesn't matter.\n    Thread.sleep(10) // Make sure the timestamps are unique.\n    val request2SentAt = System.currentTimeMillis()\n    val cacheHit =\n      executeSynchronously(\n        \"/\",\n        \"Accept-Language\",\n        \"en-US\",\n        \"Accept-Charset\",\n        \"UTF-8\",\n      )\n    val request2ReceivedAt = System.currentTimeMillis()\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n\n    // Check the merged response. The request is the application's original request.\n    cacheHit\n      .assertCode(200)\n      .assertBody(\"A\")\n      .assertHeader(\"Donut\", \"b\")\n      .assertRequestUrl(server.url(\"/\"))\n      .assertRequestHeader(\"Accept-Language\", \"en-US\")\n      .assertRequestHeader(\"Accept-Charset\", \"UTF-8\")\n      .assertRequestHeader(\"If-None-Match\") // No If-None-Match on the user's request.\n      .assertSentRequestAtMillis(request2SentAt, request2ReceivedAt)\n      .assertReceivedResponseAtMillis(request2SentAt, request2ReceivedAt)\n\n    // Check the cached response. Its request contains only the saved Vary headers.\n    cacheHit\n      .cacheResponse()\n      .assertCode(200)\n      .assertHeader(\"Donut\", \"a\")\n      .assertHeader(\"ETag\", \"v1\")\n      .assertRequestUrl(server.url(\"/\"))\n      .assertRequestHeader(\"Accept-Language\") // No Vary on Accept-Language.\n      .assertRequestHeader(\"Accept-Charset\", \"UTF-8\") // Because of Vary on Accept-Charset.\n      .assertRequestHeader(\"If-None-Match\") // This wasn't present in the original request.\n      .assertSentRequestAtMillis(request1SentAt, request1ReceivedAt)\n      .assertReceivedResponseAtMillis(request1SentAt, request1ReceivedAt)\n\n    // Check the network response. It has the caller's request, plus some caching headers.\n    cacheHit\n      .networkResponse()\n      .assertCode(304)\n      .assertHeader(\"Donut\", \"b\")\n      .assertRequestHeader(\"Accept-Language\", \"en-US\")\n      .assertRequestHeader(\"Accept-Charset\", \"UTF-8\")\n      .assertRequestHeader(\"If-None-Match\", \"v1\") // If-None-Match in the validation request.\n      .assertSentRequestAtMillis(request2SentAt, request2ReceivedAt)\n      .assertReceivedResponseAtMillis(request2SentAt, request2ReceivedAt)\n  }\n\n  @Test\n  fun conditionalCacheHit_Async() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"ETag\", \"v1\"),\n        body = \"A\",\n      ),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    val request1 = Request(server.url(\"/\"))\n    client.newCall(request1).enqueue(callback)\n    callback.await(request1.url).assertCode(200).assertBody(\"A\")\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isNull()\n    val request2 = Request(server.url(\"/\"))\n    client.newCall(request2).enqueue(callback)\n    callback.await(request2.url).assertCode(200).assertBody(\"A\")\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n  }\n\n  @Test\n  fun conditionalCacheMiss() {\n    server.enqueue(\n      MockResponse(\n        headers =\n          headersOf(\n            \"ETag\",\n            \"v1\",\n            \"Vary\",\n            \"Accept-Charset\",\n            \"Donut\",\n            \"a\",\n          ),\n        body = \"A\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Donut\", \"b\"),\n        body = \"B\",\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    val request1SentAt = System.currentTimeMillis()\n    executeSynchronously(\"/\", \"Accept-Language\", \"fr-CA\", \"Accept-Charset\", \"UTF-8\")\n      .assertCode(200)\n      .assertBody(\"A\")\n    val request1ReceivedAt = System.currentTimeMillis()\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isNull()\n\n    // Different request, but Vary says it doesn't matter.\n    Thread.sleep(10) // Make sure the timestamps are unique.\n    val request2SentAt = System.currentTimeMillis()\n    val cacheMiss =\n      executeSynchronously(\n        \"/\",\n        \"Accept-Language\",\n        \"en-US\",\n        \"Accept-Charset\",\n        \"UTF-8\",\n      )\n    val request2ReceivedAt = System.currentTimeMillis()\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n\n    // Check the user response. It has the application's original request.\n    cacheMiss\n      .assertCode(200)\n      .assertBody(\"B\")\n      .assertHeader(\"Donut\", \"b\")\n      .assertRequestUrl(server.url(\"/\"))\n      .assertSentRequestAtMillis(request2SentAt, request2ReceivedAt)\n      .assertReceivedResponseAtMillis(request2SentAt, request2ReceivedAt)\n\n    // Check the cache response. Even though it's a miss, we used the cache.\n    cacheMiss\n      .cacheResponse()\n      .assertCode(200)\n      .assertHeader(\"Donut\", \"a\")\n      .assertHeader(\"ETag\", \"v1\")\n      .assertRequestUrl(server.url(\"/\"))\n      .assertSentRequestAtMillis(request1SentAt, request1ReceivedAt)\n      .assertReceivedResponseAtMillis(request1SentAt, request1ReceivedAt)\n\n    // Check the network response. It has the network request, plus caching headers.\n    cacheMiss\n      .networkResponse()\n      .assertCode(200)\n      .assertHeader(\"Donut\", \"b\")\n      .assertRequestHeader(\"If-None-Match\", \"v1\") // If-None-Match in the validation request.\n      .assertRequestUrl(server.url(\"/\"))\n      .assertSentRequestAtMillis(request2SentAt, request2ReceivedAt)\n      .assertReceivedResponseAtMillis(request2SentAt, request2ReceivedAt)\n  }\n\n  @Test\n  fun conditionalCacheMiss_Async() {\n    server.enqueue(\n      MockResponse(\n        body = \"A\",\n        headers = headersOf(\"ETag\", \"v1\"),\n      ),\n    )\n    server.enqueue(MockResponse(body = \"B\"))\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    val request1 = Request(server.url(\"/\"))\n    client.newCall(request1).enqueue(callback)\n    callback.await(request1.url).assertCode(200).assertBody(\"A\")\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isNull()\n    val request2 = Request(server.url(\"/\"))\n    client.newCall(request2).enqueue(callback)\n    callback.await(request2.url).assertCode(200).assertBody(\"B\")\n    assertThat(server.takeRequest().headers[\"If-None-Match\"]).isEqualTo(\"v1\")\n  }\n\n  @Test\n  fun onlyIfCachedReturns504WhenNotCached() {\n    executeSynchronously(\"/\", \"Cache-Control\", \"only-if-cached\")\n      .assertCode(504)\n      .assertBody(\"\")\n      .assertNoNetworkResponse()\n      .assertNoCacheResponse()\n  }\n\n  @Test\n  fun networkDropsOnConditionalGet() {\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n\n    // Seed the cache.\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"ETag\", \"v1\"),\n        body = \"A\",\n      ),\n    )\n    executeSynchronously(\"/\")\n      .assertCode(200)\n      .assertBody(\"A\")\n\n    // Attempt conditional cache validation and a DNS miss.\n    client =\n      client\n        .newBuilder()\n        .dns(FakeDns())\n        .build()\n    executeSynchronously(\"/\").assertFailure(UnknownHostException::class.java)\n  }\n\n  @Test\n  fun redirect() {\n    server.enqueue(\n      MockResponse(\n        code = 301,\n        headers =\n          headersOf(\n            \"Location\",\n            \"/b\",\n            \"Test\",\n            \"Redirect from /a to /b\",\n          ),\n        body = \"/a has moved!\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        code = 302,\n        headers =\n          headersOf(\n            \"Location\",\n            \"/c\",\n            \"Test\",\n            \"Redirect from /b to /c\",\n          ),\n        body = \"/b has moved!\",\n      ),\n    )\n    server.enqueue(MockResponse(body = \"C\"))\n    executeSynchronously(\"/a\")\n      .assertCode(200)\n      .assertBody(\"C\")\n      .priorResponse()\n      .assertCode(302)\n      .assertHeader(\"Test\", \"Redirect from /b to /c\")\n      .priorResponse()\n      .assertCode(301)\n      .assertHeader(\"Test\", \"Redirect from /a to /b\")\n\n    // New connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Connection reused.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    // Connection reused again!\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun postRedirectsToGet() {\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", \"/page2\"),\n        body = \"This page has moved!\",\n      ),\n    )\n    server.enqueue(MockResponse(body = \"Page 2\"))\n    val response =\n      client\n        .newCall(\n          Request(\n            url = server.url(\"/page1\"),\n            body = \"Request Body\".toRequestBody(\"text/plain\".toMediaType()),\n          ),\n        ).execute()\n    assertThat(response.body.string()).isEqualTo(\"Page 2\")\n    val page1 = server.takeRequest()\n    assertThat(page1.requestLine).isEqualTo(\"POST /page1 HTTP/1.1\")\n    assertThat(page1.body?.utf8()).isEqualTo(\"Request Body\")\n    val page2 = server.takeRequest()\n    assertThat(page2.requestLine).isEqualTo(\"GET /page2 HTTP/1.1\")\n  }\n\n  @Test\n  fun getClientRequestTimeout() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(408)\n        .addHeader(\"Connection\", \"Close\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"Body\"))\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"Body\")\n  }\n\n  @Test\n  fun getClientRequestTimeoutWithBackPressure() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(408)\n        .addHeader(\"Connection\", \"Close\")\n        .addHeader(\"Retry-After\", \"1\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"You took too long!\")\n  }\n\n  @Test\n  fun requestBodyRetransmittedOnClientRequestTimeout() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(408)\n        .addHeader(\"Connection\", \"Close\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"Body\"))\n    val request =\n      Request(\n        server.url(\"/\"),\n        body = \"Hello\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"Body\")\n    val request1 = server.takeRequest()\n    assertThat(request1.body?.utf8()).isEqualTo(\"Hello\")\n    val request2 = server.takeRequest()\n    assertThat(request2.body?.utf8()).isEqualTo(\"Hello\")\n  }\n\n  @Test\n  fun disableClientRequestTimeoutRetry() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(408)\n        .addHeader(\"Connection\", \"Close\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .retryOnConnectionFailure(false)\n        .build()\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(408)\n    assertThat(response.body.string()).isEqualTo(\"You took too long!\")\n  }\n\n  @Test\n  fun maxClientRequestTimeoutRetries() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(408)\n        .addHeader(\"Connection\", \"Close\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(408)\n        .addHeader(\"Connection\", \"Close\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(408)\n    assertThat(response.body.string()).isEqualTo(\"You took too long!\")\n    assertThat(server.requestCount).isEqualTo(2)\n  }\n\n  @Test\n  fun maxUnavailableTimeoutRetries() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(503)\n        .addHeader(\"Connection\", \"Close\")\n        .addHeader(\"Retry-After\", \"0\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(503)\n        .addHeader(\"Connection\", \"Close\")\n        .addHeader(\"Retry-After\", \"0\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(503)\n    assertThat(response.body.string()).isEqualTo(\"You took too long!\")\n    assertThat(server.requestCount).isEqualTo(2)\n  }\n\n  @Test\n  fun retryOnUnavailableWith0RetryAfter() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(503)\n        .addHeader(\"Connection\", \"Close\")\n        .addHeader(\"Retry-After\", \"0\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"Body\"))\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"Body\")\n  }\n\n  @Test\n  fun canRetryNormalRequestBody() {\n    server.enqueue(\n      MockResponse(\n        code = 503,\n        headers = headersOf(\"Retry-After\", \"0\"),\n        body = \"please retry\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"thank you for retrying\"),\n    )\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body =\n          object : RequestBody() {\n            var attempt = 0\n\n            override fun contentType(): MediaType? = null\n\n            override fun writeTo(sink: BufferedSink) {\n              sink.writeUtf8(\"attempt \" + attempt++)\n            }\n          },\n      )\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"thank you for retrying\")\n    assertThat(server.takeRequest().body?.utf8()).isEqualTo(\"attempt 0\")\n    assertThat(server.takeRequest().body?.utf8()).isEqualTo(\"attempt 1\")\n    assertThat(server.requestCount).isEqualTo(2)\n  }\n\n  @Test\n  fun cannotRetryOneShotRequestBody() {\n    server.enqueue(\n      MockResponse(\n        code = 503,\n        headers = headersOf(\"Retry-After\", \"0\"),\n        body = \"please retry\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"thank you for retrying\"),\n    )\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body =\n          object : RequestBody() {\n            var attempt = 0\n\n            override fun contentType(): MediaType? = null\n\n            override fun writeTo(sink: BufferedSink) {\n              sink.writeUtf8(\"attempt \" + attempt++)\n            }\n\n            override fun isOneShot(): Boolean = true\n          },\n      )\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(503)\n    assertThat(response.body.string()).isEqualTo(\"please retry\")\n    assertThat(server.takeRequest().body?.utf8()).isEqualTo(\"attempt 0\")\n    assertThat(server.requestCount).isEqualTo(1)\n  }\n\n  @Test\n  fun propfindRedirectsToPropfindAndMaintainsRequestBody() {\n    // given\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", \"/page2\"),\n        body = \"This page has moved!\",\n      ),\n    )\n    server.enqueue(MockResponse(body = \"Page 2\"))\n\n    // when\n    val response =\n      client\n        .newCall(\n          Request\n            .Builder()\n            .url(server.url(\"/page1\"))\n            .method(\"PROPFIND\", \"Request Body\".toRequestBody(\"text/plain\".toMediaType()))\n            .build(),\n        ).execute()\n\n    // then\n    assertThat(response.body.string()).isEqualTo(\"Page 2\")\n    val page1 = server.takeRequest()\n    assertThat(page1.requestLine).isEqualTo(\"PROPFIND /page1 HTTP/1.1\")\n    assertThat(page1.body?.utf8()).isEqualTo(\"Request Body\")\n    val page2 = server.takeRequest()\n    assertThat(page2.requestLine).isEqualTo(\"PROPFIND /page2 HTTP/1.1\")\n    assertThat(page2.body?.utf8()).isEqualTo(\"Request Body\")\n  }\n\n  @Test\n  fun responseCookies() {\n    server.enqueue(\n      MockResponse(\n        headers =\n          headersOf(\n            \"Set-Cookie\",\n            \"a=b; Expires=Thu, 01 Jan 1970 00:00:00 GMT\",\n            \"Set-Cookie\",\n            \"c=d; Expires=Fri, 02 Jan 1970 23:59:59 GMT; path=/bar; secure\",\n          ),\n      ),\n    )\n    val cookieJar = RecordingCookieJar()\n    client =\n      client\n        .newBuilder()\n        .cookieJar(cookieJar)\n        .build()\n    executeSynchronously(\"/\").assertCode(200)\n    val responseCookies = cookieJar.takeResponseCookies()\n    assertThat(responseCookies.size).isEqualTo(2)\n    assertThat(responseCookies[0].toString())\n      .isEqualTo(\"a=b; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/\")\n    assertThat(responseCookies[1].toString())\n      .isEqualTo(\"c=d; expires=Fri, 02 Jan 1970 23:59:59 GMT; path=/bar; secure\")\n  }\n\n  @Test\n  fun requestCookies() {\n    server.enqueue(MockResponse())\n    val cookieJar = RecordingCookieJar()\n    cookieJar.enqueueRequestCookies(\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .domain(server.hostName)\n        .build(),\n      Cookie\n        .Builder()\n        .name(\"c\")\n        .value(\"d\")\n        .domain(server.hostName)\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .cookieJar(cookieJar)\n        .build()\n    executeSynchronously(\"/\").assertCode(200)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers[\"Cookie\"]).isEqualTo(\"a=b; c=d\")\n  }\n\n  @Test\n  fun redirectsDoNotIncludeTooManyCookies() {\n    server2.enqueue(MockResponse(body = \"Page 2\"))\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", server2.url(\"/\").toString()),\n      ),\n    )\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    val cookie = HttpCookie(\"c\", \"cookie\")\n    cookie.domain = server.hostName\n    cookie.path = \"/\"\n    val portList = server.port.toString()\n    cookie.portlist = portList\n    cookieManager.cookieStore.add(server.url(\"/\").toUri(), cookie)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n    val response = client.newCall(Request(server.url(\"/page1\"))).execute()\n    assertThat(response.body.string()).isEqualTo(\"Page 2\")\n    val request1 = server.takeRequest()\n    assertThat(request1.headers[\"Cookie\"]).isEqualTo(\"c=cookie\")\n    val request2 = server2.takeRequest()\n    assertThat(request2.headers[\"Cookie\"]).isNull()\n  }\n\n  @Test\n  fun redirectsDoNotIncludeTooManyAuthHeaders() {\n    server2.enqueue(MockResponse(body = \"Page 2\"))\n    server.enqueue(MockResponse(code = 401))\n    server.enqueue(\n      MockResponse(\n        code = 302,\n        headers = headersOf(\"Location\", server2.url(\"/b\").toString()),\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(basic(\"jesse\", \"secret\"), null))\n        .build()\n    val request = Request.Builder().url(server.url(\"/a\")).build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"Page 2\")\n    val redirectRequest = server2.takeRequest()\n    assertThat(redirectRequest.headers[\"Authorization\"]).isNull()\n    assertThat(redirectRequest.url.encodedPath).isEqualTo(\"/b\")\n  }\n\n  @Test\n  fun redirect_Async() {\n    server.enqueue(\n      MockResponse(\n        code = 301,\n        headers =\n          headersOf(\n            \"Location\",\n            \"/b\",\n            \"Test\",\n            \"Redirect from /a to /b\",\n          ),\n        body = \"/a has moved!\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        code = 302,\n        headers =\n          headersOf(\n            \"Location\",\n            \"/c\",\n            \"Test\",\n            \"Redirect from /b to /c\",\n          ),\n        body = \"/b has moved!\",\n      ),\n    )\n    server.enqueue(MockResponse(body = \"C\"))\n    val request = Request.Builder().url(server.url(\"/a\")).build()\n    client.newCall(request).enqueue(callback)\n    callback\n      .await(server.url(\"/a\"))\n      .assertCode(200)\n      .assertBody(\"C\")\n      .priorResponse()\n      .assertCode(302)\n      .assertHeader(\"Test\", \"Redirect from /b to /c\")\n      .priorResponse()\n      .assertCode(301)\n      .assertHeader(\"Test\", \"Redirect from /a to /b\")\n\n    // New connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Connection reused.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    // Connection reused again!\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  @Tag(\"Slow\")\n  @Test\n  fun follow20Redirects() {\n    for (i in 0..19) {\n      server.enqueue(\n        MockResponse(\n          code = 301,\n          headers = headersOf(\"Location\", \"/${i + 1}\"),\n          body = \"Redirecting to /\" + (i + 1),\n        ),\n      )\n    }\n    server.enqueue(MockResponse(body = \"Success!\"))\n    executeSynchronously(\"/0\")\n      .assertCode(200)\n      .assertBody(\"Success!\")\n  }\n\n  @Tag(\"Slow\")\n  @Test\n  fun follow20Redirects_Async() {\n    for (i in 0..19) {\n      server.enqueue(\n        MockResponse(\n          code = 301,\n          headers = headersOf(\"Location\", \"/\" + (i + 1)),\n          body = \"Redirecting to /\" + (i + 1),\n        ),\n      )\n    }\n    server.enqueue(MockResponse(body = \"Success!\"))\n    val request = Request.Builder().url(server.url(\"/0\")).build()\n    client.newCall(request).enqueue(callback)\n    callback\n      .await(server.url(\"/0\"))\n      .assertCode(200)\n      .assertBody(\"Success!\")\n  }\n\n  @Tag(\"Slow\")\n  @Test\n  fun doesNotFollow21Redirects() {\n    for (i in 0..20) {\n      server.enqueue(\n        MockResponse(\n          code = 301,\n          headers = headersOf(\"Location\", \"/${i + 1}\"),\n          body = \"Redirecting to /${i + 1}\",\n        ),\n      )\n    }\n    assertFailsWith<IOException> {\n      client.newCall(Request.Builder().url(server.url(\"/0\")).build()).execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Too many follow-up requests: 21\")\n    }\n  }\n\n  @Tag(\"Slow\")\n  @Test\n  fun doesNotFollow21Redirects_Async() {\n    for (i in 0..20) {\n      server.enqueue(\n        MockResponse(\n          code = 301,\n          headers = headersOf(\"Location\", \"/${i + 1}\"),\n          body = \"Redirecting to /${i + 1}\",\n        ),\n      )\n    }\n    val request = Request.Builder().url(server.url(\"/0\")).build()\n    client.newCall(request).enqueue(callback)\n    callback.await(server.url(\"/0\")).assertFailure(\"Too many follow-up requests: 21\")\n  }\n\n  @Test\n  fun http204WithBodyDisallowed() {\n    server.enqueue(\n      MockResponse(\n        code = 204,\n        body = \"I'm not even supposed to be here today.\",\n      ),\n    )\n    executeSynchronously(\"/\")\n      .assertFailure(\"HTTP 204 had non-zero Content-Length: 39\")\n  }\n\n  @Test\n  fun http205WithBodyDisallowed() {\n    server.enqueue(\n      MockResponse(\n        code = 205,\n        body = \"I'm not even supposed to be here today.\",\n      ),\n    )\n    executeSynchronously(\"/\")\n      .assertFailure(\"HTTP 205 had non-zero Content-Length: 39\")\n  }\n\n  @Test\n  fun canceledBeforeExecute() {\n    val call = client.newCall(Request.Builder().url(server.url(\"/a\")).build())\n    call.cancel()\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    assertThat(server.requestCount).isEqualTo(0)\n  }\n\n  @Tag(\"Slowish\")\n  @Test\n  fun cancelDuringHttpConnect() {\n    cancelDuringConnect(\"http\")\n  }\n\n  @Tag(\"Slowish\")\n  @Test\n  fun cancelDuringHttpsConnect() {\n    cancelDuringConnect(\"https\")\n  }\n\n  /** Cancel a call that's waiting for connect to complete.  */\n  private fun cancelDuringConnect(scheme: String?) {\n    server.enqueue(MockResponse.Builder().onRequestStart(Stall).build())\n    val cancelDelayMillis = 300L\n    val call =\n      client.newCall(\n        Request(\n          server\n            .url(\"/\")\n            .newBuilder()\n            .scheme(scheme!!)\n            .build(),\n        ),\n      )\n    cancelLater(call, cancelDelayMillis)\n    val startNanos = System.nanoTime()\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    val elapsedNanos = System.nanoTime() - startNanos\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedNanos).toFloat())\n      .isCloseTo(cancelDelayMillis.toFloat(), 100f)\n  }\n\n  @Test\n  fun cancelImmediatelyAfterEnqueue() {\n    server.enqueue(MockResponse())\n    val latch = CountDownLatch(1)\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            try {\n              latch.await()\n            } catch (e: InterruptedException) {\n              throw AssertionError(e)\n            }\n            chain.proceed(chain.request())\n          },\n        ).build()\n    val call = client.newCall(Request(server.url(\"/a\")))\n    call.enqueue(callback)\n    call.cancel()\n    latch.countDown()\n    callback\n      .await(server.url(\"/a\"))\n      .assertFailure(\"canceled\", \"Canceled\", \"Socket closed\", \"Socket is closed\")\n  }\n\n  @Test\n  fun cancelAll() {\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.enqueue(callback)\n    client.dispatcher.cancelAll()\n    callback\n      .await(server.url(\"/\"))\n      .assertFailure(\"canceled\", \"Canceled\", \"Socket closed\", \"Socket is closed\")\n  }\n\n  @Test\n  fun cancelWhileRequestHeadersAreSent() {\n    server.enqueue(MockResponse(body = \"A\"))\n    val listener: EventListener =\n      object : EventListener() {\n        override fun requestHeadersStart(call: Call) {\n          try {\n            // Cancel call from another thread to avoid reentrance.\n            cancelLater(call, 0).join()\n          } catch (e: InterruptedException) {\n            throw AssertionError()\n          }\n        }\n      }\n    client = client.newBuilder().eventListener(listener).build()\n    val call = client.newCall(Request(server.url(\"/a\")))\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n  }\n\n  @Test\n  fun cancelWhileRequestHeadersAreSent_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    cancelWhileRequestHeadersAreSent()\n  }\n\n  @Test\n  fun cancelBeforeBodyIsRead() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"def\")\n        .throttleBody(1, 750, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/a\")))\n    val executor = Executors.newSingleThreadExecutor()\n    val result = executor.submit<Response?> { call.execute() }\n    Thread.sleep(100) // wait for it to go in flight.\n    call.cancel()\n    assertFailsWith<IOException> {\n      result.get()!!.body.bytes()\n    }\n    assertThat(server.requestCount).isEqualTo(1)\n  }\n\n  @Test\n  fun cancelInFlightBeforeResponseReadThrowsIOE() {\n    val request = Request(server.url(\"/a\"))\n    val call = client.newCall(request)\n    server.dispatcher =\n      object : mockwebserver3.Dispatcher() {\n        override fun dispatch(request: RecordedRequest): MockResponse {\n          call.cancel()\n          return MockResponse(body = \"A\")\n        }\n      }\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    assertThat(server.takeRequest().url.encodedPath).isEqualTo(\"/a\")\n  }\n\n  @Test\n  fun cancelInFlightBeforeResponseReadThrowsIOE_HTTPS() {\n    enableTls()\n    cancelInFlightBeforeResponseReadThrowsIOE()\n  }\n\n  @Test\n  fun cancelInFlightBeforeResponseReadThrowsIOE_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    cancelInFlightBeforeResponseReadThrowsIOE()\n  }\n\n  /**\n   * This test puts a request in front of one that is to be canceled, so that it is canceled before\n   * I/O takes place.\n   */\n  @Test\n  fun canceledBeforeIOSignalsOnFailure() {\n    // Force requests to be executed serially.\n    val dispatcher = Dispatcher(client.dispatcher.executorService)\n    dispatcher.maxRequests = 1\n    client =\n      client\n        .newBuilder()\n        .dispatcher(dispatcher)\n        .build()\n    val requestA = Request(server.url(\"/a\"))\n    val requestB = Request(server.url(\"/b\"))\n    val callA = client.newCall(requestA)\n    val callB = client.newCall(requestB)\n    server.dispatcher =\n      object : mockwebserver3.Dispatcher() {\n        var nextResponse = 'A'\n\n        override fun dispatch(request: RecordedRequest): MockResponse {\n          callB.cancel()\n          return MockResponse(body = (nextResponse++).toString())\n        }\n      }\n    callA.enqueue(callback)\n    callB.enqueue(callback)\n    assertThat(server.takeRequest().url.encodedPath).isEqualTo(\"/a\")\n    callback.await(requestA.url).assertBody(\"A\")\n    // At this point we know the callback is ready, and that it will receive a cancel failure.\n    callback\n      .await(requestB.url)\n      .assertFailure(\"canceled\", \"Canceled\", \"Socket closed\", \"Socket is closed\")\n  }\n\n  @Test\n  fun canceledBeforeIOSignalsOnFailure_HTTPS() {\n    enableTls()\n    canceledBeforeIOSignalsOnFailure()\n  }\n\n  @Test\n  fun canceledBeforeIOSignalsOnFailure_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    canceledBeforeIOSignalsOnFailure()\n  }\n\n  @Test\n  fun canceledBeforeResponseReadSignalsOnFailure() {\n    val requestA = Request(server.url(\"/a\"))\n    val call = client.newCall(requestA)\n    server.dispatcher =\n      object : mockwebserver3.Dispatcher() {\n        override fun dispatch(request: RecordedRequest): MockResponse {\n          call.cancel()\n          return MockResponse(body = \"A\")\n        }\n      }\n    call.enqueue(callback)\n    assertThat(server.takeRequest().url.encodedPath).isEqualTo(\"/a\")\n    callback.await(requestA.url).assertFailure(\n      \"Canceled\",\n      \"stream was reset: CANCEL\",\n      \"Socket closed\",\n      \"Socket is closed\",\n    )\n  }\n\n  @Test\n  fun canceledBeforeResponseReadSignalsOnFailure_HTTPS() {\n    enableTls()\n    canceledBeforeResponseReadSignalsOnFailure()\n  }\n\n  @Test\n  fun canceledBeforeResponseReadSignalsOnFailure_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    canceledBeforeResponseReadSignalsOnFailure()\n  }\n\n  /**\n   * There's a race condition where the cancel may apply after the stream has already been\n   * processed.\n   */\n  @Test\n  fun canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce() {\n    server.enqueue(MockResponse(body = \"A\"))\n    val latch = CountDownLatch(1)\n    val bodyRef = AtomicReference<String?>()\n    val failureRef = AtomicBoolean()\n    val request = Request(server.url(\"/a\"))\n    val call = client.newCall(request)\n    call.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          failureRef.set(true)\n          latch.countDown()\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          call.cancel()\n          try {\n            bodyRef.set(response.body.string())\n          } catch (e: IOException) {\n            // It is ok if this broke the stream.\n            bodyRef.set(\"A\")\n            throw e // We expect to not loop into onFailure in this case.\n          } finally {\n            latch.countDown()\n          }\n        }\n      },\n    )\n    latch.await()\n    assertThat(bodyRef.get()).isEqualTo(\"A\")\n    assertThat(failureRef.get()).isFalse()\n  }\n\n  @Test\n  fun canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTPS() {\n    enableTls()\n    canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce()\n  }\n\n  @Test\n  fun canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce()\n  }\n\n  @Test\n  fun cancelWithInterceptor() {\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            chain.proceed(chain.request())\n            throw AssertionError() // We expect an exception.\n          },\n        ).build()\n    val call = client.newCall(Request(server.url(\"/a\")))\n    call.cancel()\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    assertThat(server.requestCount).isEqualTo(0)\n  }\n\n  @Test\n  fun gzip() {\n    val gzippedBody = gzip(\"abcabcabc\")\n    val bodySize = java.lang.Long.toString(gzippedBody.size)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(gzippedBody)\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n\n    // Confirm that the user request doesn't have Accept-Encoding, and the user\n    // response doesn't have a Content-Encoding or Content-Length.\n    val userResponse = executeSynchronously(\"/\")\n    userResponse\n      .assertCode(200)\n      .assertRequestHeader(\"Accept-Encoding\")\n      .assertHeader(\"Content-Encoding\")\n      .assertHeader(\"Content-Length\")\n      .assertBody(\"abcabcabc\")\n\n    // But the network request doesn't lie. OkHttp used gzip for this call.\n    userResponse\n      .networkResponse()\n      .assertHeader(\"Content-Encoding\", \"gzip\")\n      .assertHeader(\"Content-Length\", bodySize)\n      .assertRequestHeader(\"Accept-Encoding\", \"gzip\")\n  }\n\n  /** https://github.com/square/okhttp/issues/1927  */\n  @Test\n  fun gzipResponseAfterAuthenticationChallenge() {\n    server.enqueue(MockResponse(code = 401))\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(gzip(\"abcabcabc\"))\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(\"password\", null))\n        .build()\n    executeSynchronously(\"/\").assertBody(\"abcabcabc\")\n  }\n\n  @Test\n  fun rangeHeaderPreventsAutomaticGzip() {\n    val gzippedBody = gzip(\"abcabcabc\")\n\n    // Enqueue a gzipped response. Our request isn't expecting it, but that's okay.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_PARTIAL)\n        .body(gzippedBody)\n        .addHeader(\"Content-Encoding: gzip\")\n        .addHeader(\"Content-Range: bytes 0-\" + (gzippedBody.size - 1))\n        .build(),\n    )\n\n    // Make a range request.\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Range\", \"bytes=0-\")\n        .build()\n    val call = client.newCall(request)\n\n    // The response is not decompressed.\n    val response = call.execute()\n    assertThat(response.header(\"Content-Encoding\")).isEqualTo(\"gzip\")\n    assertThat(response.body.source().readByteString()).isEqualTo(\n      gzippedBody.snapshot(),\n    )\n\n    // The request did not offer gzip support.\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers[\"Accept-Encoding\"]).isNull()\n  }\n\n  @Test\n  fun asyncResponseCanBeConsumedLater() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"User-Agent\", \"SyncApiTest\")\n        .build()\n    val responseRef: BlockingQueue<Response> = SynchronousQueue()\n    client.newCall(request).enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ): Unit = throw AssertionError()\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          try {\n            responseRef.put(response)\n          } catch (e: InterruptedException) {\n            throw AssertionError()\n          }\n        }\n      },\n    )\n    val response = responseRef.take()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n\n    // Make another request just to confirm that that connection can be reused...\n    executeSynchronously(\"/\").assertBody(\"def\")\n    // New connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Connection reused.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n\n    // ... even before we close the response body!\n    response.body.close()\n  }\n\n  @Test\n  fun userAgentIsIncludedByDefault() {\n    server.enqueue(MockResponse())\n    executeSynchronously(\"/\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers[\"User-Agent\"]!!).isEqualTo(USER_AGENT)\n  }\n\n  @Test\n  fun setFollowRedirectsFalse() {\n    server.enqueue(\n      MockResponse(\n        code = 302,\n        headers = headersOf(\"Location\", \"/b\"),\n        body = \"A\",\n      ),\n    )\n    server.enqueue(MockResponse(body = \"B\"))\n    client =\n      client\n        .newBuilder()\n        .followRedirects(false)\n        .build()\n    executeSynchronously(\"/a\")\n      .assertBody(\"A\")\n      .assertCode(302)\n  }\n\n  @Test\n  fun expect100ContinueNonEmptyRequestBody() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .add100Continue()\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Expect\", \"100-continue\")\n        .post(\"abc\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertSuccessful()\n    assertThat(server.takeRequest().body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun expect100ContinueEmptyRequestBody() {\n    server.enqueue(MockResponse())\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Expect\", \"100-continue\")\n        .post(\"\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertSuccessful()\n  }\n\n  @Test\n  fun expect100ContinueEmptyRequestBody_HTTP2() {\n    enableProtocol(Protocol.HTTP_2)\n    expect100ContinueEmptyRequestBody()\n  }\n\n  @Tag(\"Slowish\")\n  @Test\n  fun expect100ContinueTimesOutWithoutContinue() {\n    server.enqueue(MockResponse.Builder().onResponseStart(Stall).build())\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(500))\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Expect\", \"100-continue\")\n        .post(\"abc\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    val call = client.newCall(request)\n    assertFailsWith<SocketTimeoutException> {\n      call.execute()\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body).isIn(null, ByteString.EMPTY)\n  }\n\n  @Tag(\"Slowish\")\n  @Test\n  fun expect100ContinueTimesOutWithoutContinue_HTTP2() {\n    enableProtocol(Protocol.HTTP_2)\n    expect100ContinueTimesOutWithoutContinue()\n  }\n\n  @Test\n  fun serverRespondsWithUnsolicited100Continue() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .add100Continue()\n        .build(),\n    )\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"abc\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertSuccessful()\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun serverRespondsWithEarlyHintsHttp2() {\n    enableProtocol(Protocol.HTTP_2)\n    serverRespondsWithEarlyHints()\n  }\n\n  @Test\n  fun serverRespondsWithEarlyHints() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addInformationalResponse(\n          MockResponse(\n            code = HTTP_EARLY_HINTS,\n            headers = headersOf(\"Link\", \"</style.css>; rel=preload; as=style\"),\n          ),\n        ).build(),\n    )\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"abc\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertSuccessful()\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"abc\")\n    assertThat(recordedRequest.headers[\"Link\"]).isNull()\n  }\n\n  @Test\n  fun serverReturnsMultiple100ContinuesHttp2() {\n    enableProtocol(Protocol.HTTP_2)\n    serverReturnsMultiple100Continues()\n  }\n\n  @Test\n  fun serverReturnsMultiple100Continues() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .add100Continue()\n        .add100Continue()\n        .add100Continue()\n        .build(),\n    )\n\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"abc\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    executeSynchronously(request).assertCode(200).assertSuccessful()\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun serverRespondsWithProcessingHttp2() {\n    enableProtocol(Protocol.HTTP_2)\n    serverRespondsWithProcessing()\n  }\n\n  @Test\n  fun serverRespondsWithProcessing() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addInformationalResponse(\n          MockResponse(\n            code = HTTP_PROCESSING,\n          ),\n        ).build(),\n    )\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"abc\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertSuccessful()\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun serverRespondsWithProcessingMultiple() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .apply {\n          repeat(10) {\n            addInformationalResponse(\n              MockResponse(\n                code = HTTP_PROCESSING,\n              ),\n            )\n          }\n        }.build(),\n    )\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"abc\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    executeSynchronously(request)\n      .assertCode(200)\n      .assertSuccessful()\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun serverRespondsWithUnsolicited100Continue_HTTP2() {\n    enableProtocol(Protocol.HTTP_2)\n    serverRespondsWithUnsolicited100Continue()\n  }\n\n  @Tag(\"Slow\")\n  @Test\n  fun serverRespondsWith100ContinueOnly() {\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofSeconds(1))\n        .build()\n    server.enqueue(MockResponse(code = 100))\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"abc\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    val call = client.newCall(request)\n    assertFailsWith<SocketTimeoutException> {\n      call.execute()\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Tag(\"Slow\")\n  @Test\n  fun serverRespondsWith100ContinueOnly_HTTP2() {\n    enableProtocol(Protocol.HTTP_2)\n    serverRespondsWith100ContinueOnly()\n  }\n\n  @Test\n  fun successfulExpectContinuePermitsConnectionReuse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .add100Continue()\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    executeSynchronously(\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Expect\", \"100-continue\")\n        .post(\"abc\".toRequestBody(\"text/plain\".toMediaType()))\n        .build(),\n    )\n    executeSynchronously(Request(server.url(\"/\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Tag(\"Slow\")\n  @Test\n  fun successfulExpectContinuePermitsConnectionReuseWithHttp2() {\n    enableProtocol(Protocol.HTTP_2)\n    successfulExpectContinuePermitsConnectionReuse()\n  }\n\n  @Tag(\"Slow\")\n  @Test\n  fun unsuccessfulExpectContinuePreventsConnectionReuse() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    executeSynchronously(\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Expect\", \"100-continue\")\n        .post(\"abc\".toRequestBody(\"text/plain\".toMediaType()))\n        .build(),\n    )\n    executeSynchronously(Request(server.url(\"/\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun unsuccessfulExpectContinuePermitsConnectionReuseWithHttp2() {\n    platform.assumeHttp2Support()\n    enableProtocol(Protocol.HTTP_2)\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    executeSynchronously(\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Expect\", \"100-continue\")\n        .post(\"abc\".toRequestBody(\"text/plain\".toMediaType()))\n        .build(),\n    )\n    executeSynchronously(Request(server.url(\"/\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  /** We forbid non-ASCII characters in outgoing request headers, but accept UTF-8.  */\n  @Test\n  fun responseHeaderParsingIsLenient() {\n    val headersBuilder = Headers.Builder()\n    headersBuilder.add(\"Content-Length\", \"0\")\n    addHeaderLenient(headersBuilder, \"a\\tb: c\\u007fd\")\n    addHeaderLenient(headersBuilder, \": ef\")\n    addHeaderLenient(headersBuilder, \"\\ud83c\\udf69: \\u2615\\ufe0f\")\n    val headers = headersBuilder.build()\n    server.enqueue(MockResponse(headers = headers))\n    executeSynchronously(\"/\")\n      .assertHeader(\"a\\tb\", \"c\\u007fd\")\n      .assertHeader(\"\\ud83c\\udf69\", \"\\u2615\\ufe0f\")\n      .assertHeader(\"\", \"ef\")\n  }\n\n  @Test\n  fun customDns() {\n    // Configure a DNS that returns our local MockWebServer for android.com.\n    val dns = FakeDns()\n    dns[\"android.com\"] = Dns.SYSTEM.lookup(server.url(\"/\").host)\n    client =\n      client\n        .newBuilder()\n        .dns(dns)\n        .build()\n    server.enqueue(MockResponse())\n    val request =\n      Request(\n        server\n          .url(\"/\")\n          .newBuilder()\n          .host(\"android.com\")\n          .build(),\n      )\n    executeSynchronously(request).assertCode(200)\n    dns.assertRequests(\"android.com\")\n  }\n\n  @Test\n  fun dnsReturnsZeroIpAddresses() {\n    // Configure a DNS that returns our local MockWebServer for android.com.\n    val dns = FakeDns()\n    val ipAddresses = mutableListOf<InetAddress>()\n    dns[\"android.com\"] = ipAddresses\n    client =\n      client\n        .newBuilder()\n        .dns(dns)\n        .build()\n    server.enqueue(MockResponse())\n    val request =\n      Request(\n        server\n          .url(\"/\")\n          .newBuilder()\n          .host(\"android.com\")\n          .build(),\n      )\n    executeSynchronously(request).assertFailure(\"$dns returned no addresses for android.com\")\n    dns.assertRequests(\"android.com\")\n  }\n\n  /** We had a bug where failed HTTP/2 calls could break the entire connection.  */\n  @Flaky\n  @Test\n  fun failingCallsDoNotInterfereWithConnection() {\n    enableProtocol(Protocol.HTTP_2)\n    server.enqueue(MockResponse(body = \"Response 1\"))\n    server.enqueue(MockResponse(body = \"Response 2\"))\n    val requestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.writeUtf8(\"abc\")\n          sink.flush()\n          makeFailingCall()\n          sink.writeUtf8(\"def\")\n          sink.flush()\n        }\n      }\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/\"),\n          body = requestBody,\n        ),\n      )\n    call.execute().use { response ->\n      assertThat(response.code).isEqualTo(200)\n      assertThat(response.body.string()).isNotEmpty()\n    }\n    if (!platform.isJdk8()) {\n      val connectCount =\n        eventRecorder.eventSequence\n          .stream()\n          .filter { event: CallEvent? -> event is ConnectStart }\n          .count()\n      assertThat(connectCount).isEqualTo(1)\n    }\n  }\n\n  /** Test which headers are sent unencrypted to the HTTP proxy.  */\n  @Test\n  fun proxyConnectOmitsApplicationHeaders() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"encrypted response from the origin server\"),\n    )\n    val hostnameVerifier = RecordingHostnameVerifier()\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .hostnameVerifier(hostnameVerifier)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://android.com/foo\")\n        .header(\"Private\", \"Secret\")\n        .header(\"User-Agent\", \"App 1.0\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\n      \"encrypted response from the origin server\",\n    )\n    val connect = server.takeRequest()\n    assertThat(connect.headers[\"Private\"]).isNull()\n    assertThat(connect.headers[\"User-Agent\"]).isEqualTo(USER_AGENT)\n    assertThat(connect.headers[\"Proxy-Connection\"]).isEqualTo(\"Keep-Alive\")\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\"android.com:443\")\n    val get = server.takeRequest()\n    assertThat(get.headers[\"Private\"]).isEqualTo(\"Secret\")\n    assertThat(get.headers[\"User-Agent\"]).isEqualTo(\"App 1.0\")\n    assertThat(hostnameVerifier.calls).containsExactly(\"verify android.com\")\n  }\n\n  /**\n   * We had a bug where OkHttp would crash if HTTP proxies returned a truncated response.\n   * https://github.com/square/okhttp/issues/5727\n   */\n  @Test\n  fun proxyUpgradeFailsWithTruncatedResponse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .setHeader(\"Content-Length\", \"5\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val hostnameVerifier = RecordingHostnameVerifier()\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .hostnameVerifier(hostnameVerifier)\n        .build()\n    val request = Request(\"https://android.com/foo\".toHttpUrl())\n    assertFailsWith<IOException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"unexpected end of stream\")\n    }\n  }\n\n  /** Respond to a proxy authorization challenge.  */\n  @Test\n  fun proxyAuthenticateOnConnect() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(407)\n        .headers(headersOf(\"Proxy-Authenticate\", \"Basic realm=\\\"localhost\\\"\"))\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .proxyAuthenticator(RecordingOkAuthenticator(\"password\", \"Basic\"))\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val request = Request(\"https://android.com/foo\".toHttpUrl())\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val connect1 = server.takeRequest()\n    assertThat(connect1.requestLine).isEqualTo(\"CONNECT android.com:443 HTTP/1.1\")\n    assertThat(connect1.headers[\"Proxy-Authorization\"]).isNull()\n    val connect2 = server.takeRequest()\n    assertThat(connect2.requestLine).isEqualTo(\"CONNECT android.com:443 HTTP/1.1\")\n    assertThat(connect2.headers[\"Proxy-Authorization\"]).isEqualTo(\"password\")\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET /foo HTTP/2\")\n    assertThat(get.headers[\"Proxy-Authorization\"]).isNull()\n  }\n\n  /** Confirm that the proxy authenticator works for unencrypted HTTP proxies.  */\n  @Test\n  fun httpProxyAuthenticate() {\n    server.enqueue(\n      MockResponse(\n        code = 407,\n        headers = headersOf(\"Proxy-Authenticate\", \"Basic realm=\\\"localhost\\\"\"),\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .proxyAuthenticator(RecordingOkAuthenticator(\"password\", \"Basic\"))\n        .build()\n    val request = Request(\"http://android.com/foo\".toHttpUrl())\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val get1 = server.takeRequest()\n    assertThat(get1.requestLine).isEqualTo(\"GET http://android.com/foo HTTP/1.1\")\n    assertThat(get1.headers[\"Proxy-Authorization\"]).isNull()\n    val get2 = server.takeRequest()\n    assertThat(get2.requestLine).isEqualTo(\"GET http://android.com/foo HTTP/1.1\")\n    assertThat(get2.headers[\"Proxy-Authorization\"]).isEqualTo(\"password\")\n  }\n\n  /**\n   * OkHttp has a bug where a `Connection: close` response header is not honored when establishing a\n   * TLS tunnel. https://github.com/square/okhttp/issues/2426\n   */\n  @Test\n  fun proxyAuthenticateOnConnectWithConnectionClose() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.protocols = listOf<Protocol>(Protocol.HTTP_1_1)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(407)\n        .headers(\n          headersOf(\n            \"Proxy-Authenticate\",\n            \"Basic realm=\\\"localhost\\\"\",\n            \"Connection\",\n            \"close\",\n          ),\n        ).inTunnel()\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .proxyAuthenticator(RecordingOkAuthenticator(\"password\", \"Basic\"))\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val request = Request(\"https://android.com/foo\".toHttpUrl())\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n\n    // First CONNECT call needs a new connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // Second CONNECT call needs a new connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // GET reuses the connection from the second connect.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun tooManyProxyAuthFailuresWithConnectionClose() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.protocols = listOf(Protocol.HTTP_1_1)\n    for (i in 0..20) {\n      server.enqueue(\n        MockResponse\n          .Builder()\n          .code(407)\n          .headers(\n            headersOf(\n              \"Proxy-Authenticate\",\n              \"Basic realm=\\\"localhost\\\"\",\n              \"Connection\",\n              \"close\",\n            ),\n          ).inTunnel()\n          .build(),\n      )\n    }\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .proxyAuthenticator(RecordingOkAuthenticator(\"password\", \"Basic\"))\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val request = Request(\"https://android.com/foo\".toHttpUrl())\n    assertFailsWith<ProtocolException> {\n      client.newCall(request).execute()\n    }\n  }\n\n  /**\n   * Confirm that we don't send the Proxy-Authorization header from the request to the proxy server.\n   * We used to have that behavior but it is problematic because unrelated requests end up sharing\n   * credentials. Worse, that approach leaks proxy credentials to the origin server.\n   */\n  @Test\n  fun noPreemptiveProxyAuthorization() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"response body\"))\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://android.com/foo\")\n        .header(\"Proxy-Authorization\", \"password\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val connect1 = server.takeRequest()\n    assertThat(connect1.headers[\"Proxy-Authorization\"]).isNull()\n    val connect2 = server.takeRequest()\n    assertThat(connect2.headers[\"Proxy-Authorization\"]).isEqualTo(\"password\")\n  }\n\n  /** Confirm that we can send authentication information without being prompted first.  */\n  @Test\n  fun preemptiveProxyAuthentication() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"encrypted response from the origin server\"))\n    val credential = basic(\"jesse\", \"password1\")\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .proxyAuthenticator { _: Route?, response: Response? ->\n          assertThat(response!!.request.method).isEqualTo(\"CONNECT\")\n          assertThat(response.code).isEqualTo(HttpURLConnection.HTTP_PROXY_AUTH)\n          assertThat(response.request.url.host).isEqualTo(\"android.com\")\n          val challenges = response.challenges()\n          assertThat(challenges[0].scheme).isEqualTo(\"OkHttp-Preemptive\")\n          response.request\n            .newBuilder()\n            .header(\"Proxy-Authorization\", credential)\n            .build()\n        }.build()\n    val request = Request(\"https://android.com/foo\".toHttpUrl())\n    executeSynchronously(request).assertSuccessful()\n    val connect = server.takeRequest()\n    assertThat(connect.method).isEqualTo(\"CONNECT\")\n    assertThat(connect.headers[\"Proxy-Authorization\"]).isEqualTo(credential)\n    assertThat(connect.url.encodedPath).isEqualTo(\"/\")\n    val get = server.takeRequest()\n    assertThat(get.method).isEqualTo(\"GET\")\n    assertThat(get.headers[\"Proxy-Authorization\"]).isNull()\n    assertThat(get.url.encodedPath).isEqualTo(\"/foo\")\n  }\n\n  @Test\n  fun preemptiveThenReactiveProxyAuthentication() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_PROXY_AUTH)\n        .headers(headersOf(\"Proxy-Authenticate\", \"Basic realm=\\\"localhost\\\"\"))\n        .inTunnel()\n        .body(\"proxy auth required\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    val challengeSchemes: MutableList<String?> = ArrayList()\n    val credential = basic(\"jesse\", \"password1\")\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .proxyAuthenticator { _: Route?, response: Response ->\n          val challenges = response.challenges()\n          challengeSchemes.add(challenges[0].scheme)\n          response.request\n            .newBuilder()\n            .header(\"Proxy-Authorization\", credential)\n            .build()\n        }.build()\n    val request = Request(\"https://android.com/foo\".toHttpUrl())\n    executeSynchronously(request).assertSuccessful()\n    val connect1 = server.takeRequest()\n    assertThat(connect1.method).isEqualTo(\"CONNECT\")\n    assertThat(connect1.headers[\"Proxy-Authorization\"]).isEqualTo(credential)\n    val connect2 = server.takeRequest()\n    assertThat(connect2.method).isEqualTo(\"CONNECT\")\n    assertThat(connect2.headers[\"Proxy-Authorization\"]).isEqualTo(credential)\n    assertThat(challengeSchemes).containsExactly(\"OkHttp-Preemptive\", \"Basic\")\n  }\n\n  /** https://github.com/square/okhttp/issues/4915  */\n  @Test\n  @Disabled\n  fun proxyDisconnectsAfterRequest() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .onResponseStart(CloseSocket())\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).proxy(server.proxyAddress)\n        .build()\n    val request = Request(server.url(\"/\"))\n    assertFailsWith<IOException> {\n      client.newCall(request).execute()\n    }\n  }\n\n  @Test\n  fun interceptorGetsHttp2() {\n    platform.assumeHttp2Support()\n    enableProtocol(Protocol.HTTP_2)\n\n    // Capture the protocol as it is observed by the interceptor.\n    val protocolRef = AtomicReference<Protocol?>()\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        protocolRef.set(chain.connection()!!.protocol())\n        chain.proceed(chain.request())\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(interceptor)\n        .build()\n\n    // Make an HTTP/2 request and confirm that the protocol matches.\n    server.enqueue(MockResponse())\n    executeSynchronously(\"/\")\n    assertThat(protocolRef.get()).isEqualTo(Protocol.HTTP_2)\n  }\n\n  @Test\n  fun serverSendsInvalidResponseHeaders() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTP/1.1 200 OK\")\n        .build(),\n    )\n    executeSynchronously(\"/\")\n      .assertFailure(\"Unexpected status line: HTP/1.1 200 OK\")\n  }\n\n  @Test\n  fun serverSendsInvalidCodeTooLarge() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 2147483648 OK\")\n        .build(),\n    )\n    executeSynchronously(\"/\")\n      .assertFailure(\"Unexpected status line: HTTP/1.1 2147483648 OK\")\n  }\n\n  @Test\n  fun serverSendsInvalidCodeNotANumber() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 00a OK\")\n        .build(),\n    )\n    executeSynchronously(\"/\")\n      .assertFailure(\"Unexpected status line: HTTP/1.1 00a OK\")\n  }\n\n  @Test\n  fun serverSendsUnnecessaryWhitespace() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\" HTTP/1.1 200 OK\")\n        .build(),\n    )\n    executeSynchronously(\"/\")\n      .assertFailure(\"Unexpected status line:  HTTP/1.1 200 OK\")\n  }\n\n  @Test\n  fun requestHeaderNameWithSpaceForbidden() {\n    assertFailsWith<IllegalArgumentException> {\n      Request.Builder().addHeader(\"a b\", \"c\")\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Unexpected char 0x20 at 1 in header name: a b\")\n    }\n  }\n\n  @Test\n  fun requestHeaderNameWithTabForbidden() {\n    assertFailsWith<IllegalArgumentException> {\n      Request.Builder().addHeader(\"a\\tb\", \"c\")\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Unexpected char 0x09 at 1 in header name: a\\tb\")\n    }\n  }\n\n  @Test\n  fun responseHeaderNameWithSpacePermitted() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"content-length: 0\")\n        .addHeaderLenient(\"a b\", \"c\")\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.header(\"a b\")).isEqualTo(\"c\")\n  }\n\n  @Test\n  fun responseHeaderNameWithTabPermitted() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"content-length: 0\")\n        .addHeaderLenient(\"a\\tb\", \"c\")\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.header(\"a\\tb\")).isEqualTo(\"c\")\n  }\n\n  @Test\n  fun connectFails() {\n    server.close()\n    executeSynchronously(\"/\")\n      .assertFailure(IOException::class.java)\n  }\n\n  @Test\n  fun requestBodySurvivesRetries() {\n    server.enqueue(MockResponse())\n\n    // Enable a misconfigured proxy selector to guarantee that the request is retried.\n    client =\n      client\n        .newBuilder()\n        .proxySelector(\n          FakeProxySelector()\n            .addProxy(server2.proxyAddress)\n            .addProxy(Proxy.NO_PROXY),\n        ).build()\n    server2.close()\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"abc\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    executeSynchronously(request)\n    assertThat(server.takeRequest().body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Disabled // This may fail in DNS lookup, which we don't have timeouts for.\n  @Test\n  fun invalidHost() {\n    val request = Request(\"http://1234.1.1.1/\".toHttpUrl())\n    executeSynchronously(request)\n      .assertFailure(UnknownHostException::class.java)\n  }\n\n  @Test\n  fun uploadBodySmallChunkedEncoding() {\n    upload(true, 1048576, 256)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.bodySize).isEqualTo(1048576)\n    assertThat(recordedRequest.chunkSizes).isNotNull()\n  }\n\n  @Test\n  fun uploadBodyEmptyChunkedEncoding() {\n    upload(true, 0, 256)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.bodySize).isEqualTo(0)\n    assertThat(recordedRequest.chunkSizes).isEqualTo(listOf())\n  }\n\n  @Test\n  fun uploadBodyLargeChunkedEncoding() {\n    upload(true, 1048576, 65536)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.bodySize).isEqualTo(1048576)\n    assertThat(recordedRequest.chunkSizes).isNotNull()\n  }\n\n  @Test\n  fun uploadBodySmallFixedLength() {\n    upload(false, 1048576, 256)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.bodySize).isEqualTo(1048576)\n    assertThat(recordedRequest.chunkSizes).isNull()\n  }\n\n  @Test\n  fun uploadBodyLargeFixedLength() {\n    upload(false, 1048576, 65536)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.bodySize).isEqualTo(1048576)\n    assertThat(recordedRequest.chunkSizes).isNull()\n  }\n\n  private fun upload(\n    chunked: Boolean,\n    size: Int,\n    writeSize: Int,\n  ) {\n    server.enqueue(MockResponse())\n    executeSynchronously(\n      Request(\n        url = server.url(\"/\"),\n        body = requestBody(chunked, size.toLong(), writeSize),\n      ),\n    )\n  }\n\n  /** https://github.com/square/okhttp/issues/2344  */\n  @Test\n  fun ipv6HostHasSquareBracesHttp1() {\n    configureClientAndServerProxies(http2 = false)\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    val port = server.port\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(\"::1\")\n        .port(port)\n        .build()\n    val request = Request(url)\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val connect = server.takeRequest()\n    assertThat(connect.requestLine).isEqualTo(\"CONNECT [::1]:$port HTTP/1.1\")\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\"[::1]:$port\")\n    assertThat(connect.headers[\":authority\"]).isNull()\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    assertThat(get.headers[\"Host\"]).isEqualTo(\"[::1]:$port\")\n    assertThat(get.headers[\":authority\"]).isNull()\n    assertThat(get.url).isEqualTo(url)\n  }\n\n  @Test\n  fun ipv6HostHasSquareBracesHttp2() {\n    configureClientAndServerProxies(http2 = true)\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    val port = server.port\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(\"::1\")\n        .port(port)\n        .build()\n    val request = Request(url)\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val connect = server.takeRequest()\n    assertThat(connect.requestLine).isEqualTo(\n      \"CONNECT [::1]:$port HTTP/1.1\",\n    )\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\n      \"[::1]:$port\",\n    )\n    assertThat(connect.headers[\":authority\"]).isNull()\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET / HTTP/2\")\n    assertThat(get.headers[\"Host\"]).isNull()\n    assertThat(get.headers[\":authority\"]).isEqualTo(\n      \"[::1]:$port\",\n    )\n    assertThat(get.url).isEqualTo(url)\n  }\n\n  @Test\n  fun ipv4IpHostHasNoSquareBracesHttp1() {\n    configureClientAndServerProxies(http2 = false)\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    val port = server.port\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(\"127.0.0.1\")\n        .port(port)\n        .build()\n    val request = Request(url)\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val connect = server.takeRequest()\n    assertThat(connect.requestLine).isEqualTo(\n      \"CONNECT 127.0.0.1:$port HTTP/1.1\",\n    )\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\n      \"127.0.0.1:$port\",\n    )\n    assertThat(connect.headers[\":authority\"]).isNull()\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    assertThat(get.headers[\"Host\"]).isEqualTo(\n      \"127.0.0.1:$port\",\n    )\n    assertThat(get.headers[\":authority\"]).isNull()\n    assertThat(get.url).isEqualTo(url)\n  }\n\n  @Test\n  fun ipv4IpHostHasNoSquareBracesHttp2() {\n    configureClientAndServerProxies(http2 = true)\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    val port = server.port\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(\"127.0.0.1\")\n        .port(port)\n        .build()\n    val request = Request(url)\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val connect = server.takeRequest()\n    assertThat(connect.requestLine).isEqualTo(\"CONNECT 127.0.0.1:$port HTTP/1.1\")\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\"127.0.0.1:$port\")\n    assertThat(connect.headers[\":authority\"]).isNull()\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET / HTTP/2\")\n    assertThat(get.headers[\"Host\"]).isNull()\n    assertThat(get.headers[\":authority\"]).isEqualTo(\"127.0.0.1:$port\")\n    assertThat(get.url).isEqualTo(url)\n  }\n\n  @Test\n  fun hostnameRequestHostHasNoSquareBracesHttp1() {\n    configureClientAndServerProxies(http2 = false)\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    val port = server.port\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(\"any-host-name\")\n        .port(port)\n        .build()\n    val request = Request(url)\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val connect = server.takeRequest()\n    assertThat(connect.requestLine).isEqualTo(\"CONNECT any-host-name:$port HTTP/1.1\")\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\"any-host-name:$port\")\n    assertThat(connect.headers[\":authority\"]).isNull()\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    assertThat(get.headers[\"Host\"]).isEqualTo(\"any-host-name:$port\")\n    assertThat(get.headers[\":authority\"]).isNull()\n    assertThat(get.url).isEqualTo(url)\n  }\n\n  @Test\n  fun hostnameRequestHostHasNoSquareBracesHttp2() {\n    configureClientAndServerProxies(http2 = true)\n    server.enqueue(\n      MockResponse(body = \"response body\"),\n    )\n    val port = server.port\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(\"any-host-name\")\n        .port(port)\n        .build()\n    val request = Request(url)\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n    val connect = server.takeRequest()\n    assertThat(connect.requestLine).isEqualTo(\"CONNECT any-host-name:$port HTTP/1.1\")\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\"any-host-name:$port\")\n    assertThat(connect.headers[\":authority\"]).isNull()\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET / HTTP/2\")\n    assertThat(get.headers[\"Host\"]).isNull()\n    assertThat(get.headers[\":authority\"]).isEqualTo(\"any-host-name:$port\")\n    assertThat(get.url).isEqualTo(url)\n  }\n\n  /** Use a proxy to fake IPv6 connectivity, even if localhost doesn't have IPv6.  */\n  private fun configureClientAndServerProxies(http2: Boolean) {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.protocols =\n      when {\n        http2 -> listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)\n        else -> listOf(Protocol.HTTP_1_1)\n      }\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .proxy(server.proxyAddress)\n        .build()\n  }\n\n  private fun requestBody(\n    chunked: Boolean,\n    size: Long,\n    writeSize: Int,\n  ): RequestBody {\n    val buffer = ByteArray(writeSize)\n    Arrays.fill(buffer, 'x'.code.toByte())\n    return object : RequestBody() {\n      override fun contentType() = \"text/plain; charset=utf-8\".toMediaType()\n\n      override fun contentLength(): Long = if (chunked) -1L else size\n\n      override fun writeTo(sink: BufferedSink) {\n        var count = 0\n        while (count < size) {\n          sink.write(buffer, 0, Math.min(size - count, writeSize.toLong()).toInt())\n          count += writeSize\n        }\n      }\n    }\n  }\n\n  @Test\n  fun emptyResponseBody() {\n    server.enqueue(\n      MockResponse(headers = headersOf(\"abc\", \"def\")),\n    )\n    executeSynchronously(\"/\")\n      .assertCode(200)\n      .assertHeader(\"abc\", \"def\")\n      .assertBody(\"\")\n  }\n\n  @Test\n  fun leakedResponseBodyLogsStackTrace() {\n    server.enqueue(\n      MockResponse(body = \"This gets leaked.\"),\n    )\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .connectionPool(ConnectionPool(0, 10, TimeUnit.MILLISECONDS))\n        .build()\n    val request = Request(server.url(\"/\"))\n    client.newCall(request).execute() // Ignore the response so it gets leaked then GC'd.\n    awaitGarbageCollection()\n    val message = testLogHandler.take()\n    assertThat(message).contains(\n      \"A connection to ${server.url(\"/\")} was leaked. Did you forget to close a response body?\",\n    )\n  }\n\n  @Tag(\"Slowish\")\n  @Test\n  fun asyncLeakedResponseBodyLogsStackTrace() {\n    server.enqueue(MockResponse(body = \"This gets leaked.\"))\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .connectionPool(ConnectionPool(0, 10, TimeUnit.MILLISECONDS))\n        .build()\n    val request = Request(server.url(\"/\"))\n    val latch = CountDownLatch(1)\n    client.newCall(request).enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          fail(\"\")\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          // Ignore the response so it gets leaked then GC'd.\n          latch.countDown()\n        }\n      },\n    )\n    latch.await()\n    // There's some flakiness when triggering a GC for objects in a separate thread. Adding a\n    // small delay appears to ensure the objects will get GC'd.\n    Thread.sleep(200)\n    awaitGarbageCollection()\n    val message = testLogHandler.take()\n    assertThat(message).contains(\n      \"A connection to ${server.url(\"/\")} was leaked. Did you forget to close a response body?\",\n    )\n  }\n\n  @Test\n  fun failedAuthenticatorReleasesConnection() {\n    server.enqueue(MockResponse(code = 401))\n    client =\n      client\n        .newBuilder()\n        .authenticator { _: Route?, _: Response -> throw IOException(\"IOException!\") }\n        .build()\n    val request = Request(server.url(\"/\"))\n    executeSynchronously(request)\n      .assertFailure(IOException::class.java)\n    assertThat(client.connectionPool.idleConnectionCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun failedProxyAuthenticatorReleasesConnection() {\n    server.enqueue(\n      MockResponse(code = 407),\n    )\n    client =\n      client\n        .newBuilder()\n        .proxyAuthenticator { _: Route?, _: Response ->\n          throw IOException(\"IOException!\")\n        }.build()\n    val request = Request(server.url(\"/\"))\n    executeSynchronously(request)\n      .assertFailure(IOException::class.java)\n    assertThat(client.connectionPool.idleConnectionCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun httpsWithIpAddress() {\n    platform.assumeNotBouncyCastle()\n\n    val localIpAddress = InetAddress.getLoopbackAddress().hostAddress\n\n    // Create a certificate with an IP address in the subject alt name.\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"example.com\")\n        .addSubjectAlternativeName(localIpAddress!!)\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(heldCertificate)\n        .addTrustedCertificate(heldCertificate.certificate)\n        .build()\n\n    // Use that certificate on the server and trust it on the client.\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .protocols(listOf(Protocol.HTTP_1_1))\n        .build()\n\n    // Make a request.\n    server.enqueue(MockResponse())\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(localIpAddress)\n        .build()\n    val request = Request(url)\n    executeSynchronously(request)\n      .assertCode(200)\n\n    // Confirm that the IP address was used in the host header.\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers[\"Host\"]).isEqualTo(\"$localIpAddress:${server.port}\")\n  }\n\n  @Test\n  fun postWithFileNotFound() {\n    val called = AtomicInteger(0)\n    val body: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType = \"application/octet-stream\".toMediaType()\n\n        override fun writeTo(sink: BufferedSink) {\n          called.incrementAndGet()\n          throw FileNotFoundException()\n        }\n      }\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = body,\n      )\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n    executeSynchronously(request)\n      .assertFailure(FileNotFoundException::class.java)\n    assertThat(called.get()).isEqualTo(1)\n  }\n\n  @Test\n  fun clientReadsHeadersDataTrailersHttp1ChunkedTransferEncoding() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"h1\", \"v1\")\n        .addHeader(\"h2\", \"v2\")\n        .chunkedBody(\"HelloBonjour\", 1024)\n        .trailers(headersOf(\"trailers\", \"boom\"))\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    val source = response.body.source()\n    assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n    assertThat(response.header(\"h2\")).isEqualTo(\"v2\")\n    assertThat(source.readUtf8(5)).isEqualTo(\"Hello\")\n    assertThat(source.readUtf8(7)).isEqualTo(\"Bonjour\")\n    assertThat(source.exhausted()).isTrue()\n    assertThat(response.trailers()).isEqualTo(headersOf(\"trailers\", \"boom\"))\n  }\n\n  @Test\n  fun clientReadsHeadersDataTrailersHttp2() {\n    platform.assumeHttp2Support()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"h1\", \"v1\")\n        .addHeader(\"h2\", \"v2\")\n        .body(\"HelloBonjour\")\n        .trailers(headersOf(\"trailers\", \"boom\"))\n        .build(),\n    )\n    enableProtocol(Protocol.HTTP_2)\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      val source = response.body.source()\n      assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n      assertThat(response.header(\"h2\")).isEqualTo(\"v2\")\n      assertThat(source.readUtf8(5)).isEqualTo(\"Hello\")\n      assertThat(source.readUtf8(7)).isEqualTo(\"Bonjour\")\n      assertThat(source.exhausted()).isTrue()\n      assertThat(response.trailers()).isEqualTo(headersOf(\"trailers\", \"boom\"))\n    }\n  }\n\n  @Test\n  fun connectionIsImmediatelyUnhealthy() {\n    val listener: EventListener =\n      object : EventListener() {\n        override fun connectionAcquired(\n          call: Call,\n          connection: Connection,\n        ) {\n          connection.socket().closeQuietly()\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .eventListener(listener)\n        .build()\n    executeSynchronously(\"/\")\n      .assertFailure(IOException::class.java)\n      .assertFailure(\"canceled\", \"Canceled\", \"Socket closed\", \"Socket is closed\")\n  }\n\n  @Test\n  fun requestBodyThrowsUnrelatedToNetwork() {\n    server.enqueue(MockResponse())\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body =\n          object : RequestBody() {\n            override fun contentType(): MediaType? = null\n\n            override fun writeTo(sink: BufferedSink) {\n              sink.flush() // For determinism, always send a partial request to the server.\n              throw IOException(\"boom\")\n            }\n          },\n      )\n    executeSynchronously(request).assertFailure(\"boom\")\n    assertThat(server.takeRequest().failure).isNotNull()\n  }\n\n  @Test\n  fun requestBodyThrowsUnrelatedToNetwork_HTTP2() {\n    enableProtocol(Protocol.HTTP_2)\n    requestBodyThrowsUnrelatedToNetwork()\n  }\n\n  /**\n   * This test cancels the call just after the response body ends. In effect we end up with a\n   * connection that returns to the connection pool with the underlying socket closed. This relies\n   * on an implementation detail so it might not be a valid test case in the future.\n   */\n  @Test\n  fun cancelAfterResponseBodyEnd() {\n    enableTls()\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    client =\n      client\n        .newBuilder()\n        .protocols(listOf(Protocol.HTTP_1_1))\n        .build()\n    val cancelClient =\n      client\n        .newBuilder()\n        .eventListener(\n          object : EventListener() {\n            override fun responseBodyEnd(\n              call: Call,\n              byteCount: Long,\n            ) {\n              call.cancel()\n            }\n          },\n        ).build()\n    val call = cancelClient.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    executeSynchronously(\"/\").assertCode(200)\n  }\n\n  /** https://github.com/square/okhttp/issues/4583  */\n  @Test\n  fun lateCancelCallsOnFailure() {\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    val closed = AtomicBoolean()\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain ->\n            val response = chain.proceed(chain.request())\n            chain.call().cancel() // Cancel after we have the response.\n            val closeTrackingSource =\n              object : ForwardingSource(response.body.source()) {\n                override fun close() {\n                  closed.set(true)\n                  super.close()\n                }\n              }\n            response\n              .newBuilder()\n              .body(closeTrackingSource.buffer().asResponseBody())\n              .build()\n          },\n        ).build()\n    executeSynchronously(\"/\")\n      .assertFailure(\"canceled\", \"Canceled\", \"Socket closed\", \"Socket is closed\")\n    assertThat(closed.get()).isTrue()\n  }\n\n  @Test\n  fun priorResponseBodyNotReadable() {\n    server.enqueue(\n      MockResponse(\n        code = 301,\n        headers =\n          headersOf(\n            \"Location\",\n            \"/b\",\n            \"Content-Type\",\n            \"text/plain; charset=UTF-8\",\n            \"Test\",\n            \"Redirect from /a to /b\",\n          ),\n        body = \"/a has moved!\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"this is the redirect target\"),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"this is the redirect target\")\n    assertThat(response.priorResponse?.body?.contentType())\n      .isEqualTo(\"text/plain; charset=UTF-8\".toMediaType())\n    assertFailsWith<IllegalStateException> {\n      response.priorResponse?.body?.string()\n    }.also { expected ->\n      assertThat(expected.message!!).contains(\"Unreadable ResponseBody!\")\n    }\n  }\n\n  private fun makeFailingCall() {\n    val requestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType() = null\n\n        override fun contentLength() = 1L\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.flush() // For determinism, always send a partial request to the server.\n          throw IOException(\"write body fail!\")\n        }\n      }\n    val nonRetryingClient =\n      client\n        .newBuilder()\n        .retryOnConnectionFailure(false)\n        .build()\n    val call =\n      nonRetryingClient.newCall(\n        Request(\n          url = server.url(\"/\"),\n          body = requestBody,\n        ),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"write body fail!\")\n    }\n  }\n\n  private fun executeSynchronously(\n    path: String,\n    vararg headers: String?,\n  ): RecordedResponse {\n    val builder = Request.Builder()\n    builder.url(server.url(path))\n    var i = 0\n    val size = headers.size\n    while (i < size) {\n      builder.addHeader(headers[i]!!, headers[i + 1]!!)\n      i += 2\n    }\n    return executeSynchronously(builder.build())\n  }\n\n  private fun executeSynchronously(request: Request): RecordedResponse {\n    val call = client.newCall(request)\n    return try {\n      val response = call.execute()\n      val bodyString = response.body.string()\n      RecordedResponse(request, response, null, bodyString, null)\n    } catch (e: IOException) {\n      RecordedResponse(request, null, null, null, e)\n    }\n  }\n\n  /**\n   * Tests that use this will fail unless boot classpath is set. Ex. `-Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317`\n   */\n  private fun enableProtocol(protocol: Protocol) {\n    enableTls()\n    client =\n      client\n        .newBuilder()\n        .protocols(listOf(protocol, Protocol.HTTP_1_1))\n        .build()\n    server.protocols = client.protocols\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n\n  private fun gzip(data: String): Buffer {\n    val result = Buffer()\n    val sink = GzipSink(result).buffer()\n    sink.writeUtf8(data)\n    sink.close()\n    return result\n  }\n\n  private fun cancelLater(\n    call: Call,\n    delay: Long,\n  ): Thread {\n    val thread =\n      object : Thread(\"canceler\") {\n        override fun run() {\n          try {\n            sleep(delay)\n          } catch (e: InterruptedException) {\n            throw AssertionError()\n          }\n          call.cancel()\n        }\n      }\n    thread.start()\n    return thread\n  }\n\n  private fun socketFactoryWithCipherSuite(\n    sslSocketFactory: SSLSocketFactory,\n    cipherSuite: CipherSuite,\n  ): SSLSocketFactory {\n    return object : DelegatingSSLSocketFactory(sslSocketFactory) {\n      override fun configureSocket(sslSocket: SSLSocket): SSLSocket {\n        sslSocket.enabledCipherSuites = arrayOf(cipherSuite.javaName)\n        return super.configureSocket(sslSocket)\n      }\n    }\n  }\n\n  private class RecordingSSLSocketFactory(\n    delegate: SSLSocketFactory,\n  ) : DelegatingSSLSocketFactory(delegate) {\n    val socketsCreated = mutableListOf<SSLSocket>()\n\n    override fun configureSocket(sslSocket: SSLSocket): SSLSocket {\n      socketsCreated.add(sslSocket)\n      return sslSocket\n    }\n  }\n\n  /**\n   * Used during tests that involve TLS connection fallback attempts. OkHttp includes the\n   * TLS_FALLBACK_SCSV cipher on fallback connections. See [FallbackTestClientSocketFactory]\n   * for details.\n   */\n  private fun suppressTlsFallbackClientSocketFactory(): FallbackTestClientSocketFactory =\n    FallbackTestClientSocketFactory(handshakeCertificates.sslSocketFactory())\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CertificateChainCleanerTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.security.cert.Certificate\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport kotlin.test.assertFailsWith\nimport okhttp3.internal.tls.CertificateChainCleaner.Companion.get\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport org.junit.jupiter.api.Test\n\nclass CertificateChainCleanerTest {\n  @Test\n  fun equalsFromCertificate() {\n    val rootA =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .build()\n    val rootB =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .build()\n    assertThat(get(rootB.certificate, rootA.certificate))\n      .isEqualTo(get(rootA.certificate, rootB.certificate))\n  }\n\n  @Test\n  fun equalsFromTrustManager() {\n    val handshakeCertificates = HandshakeCertificates.Builder().build()\n    val x509TrustManager = handshakeCertificates.trustManager\n    assertThat(get(x509TrustManager)).isEqualTo(get(x509TrustManager))\n  }\n\n  @Test\n  fun normalizeSingleSelfSignedCertificate() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .build()\n    val cleaner = get(root.certificate)\n    assertThat(cleaner.clean(list(root), \"hostname\")).isEqualTo(list(root))\n  }\n\n  @Test\n  fun normalizeUnknownSelfSignedCertificate() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .build()\n    val cleaner = get()\n    assertFailsWith<SSLPeerUnverifiedException> {\n      cleaner.clean(list(root), \"hostname\")\n    }\n  }\n\n  @Test\n  fun orderedChainOfCertificatesWithRoot() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .build()\n    val certA =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .certificateAuthority(0)\n        .signedBy(root)\n        .build()\n    val certB =\n      HeldCertificate\n        .Builder()\n        .serialNumber(3L)\n        .signedBy(certA)\n        .build()\n    val cleaner = get(root.certificate)\n    assertThat(cleaner.clean(list(certB, certA, root), \"hostname\"))\n      .isEqualTo(list(certB, certA, root))\n  }\n\n  @Test\n  fun orderedChainOfCertificatesWithoutRoot() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .build()\n    val certA =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .certificateAuthority(0)\n        .signedBy(root)\n        .build()\n    val certB =\n      HeldCertificate\n        .Builder()\n        .serialNumber(3L)\n        .signedBy(certA)\n        .build()\n    val cleaner = get(root.certificate)\n    // Root is added!\n    assertThat(cleaner.clean(list(certB, certA), \"hostname\")).isEqualTo(\n      list(certB, certA, root),\n    )\n  }\n\n  @Test\n  fun unorderedChainOfCertificatesWithRoot() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(2)\n        .build()\n    val certA =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .certificateAuthority(1)\n        .signedBy(root)\n        .build()\n    val certB =\n      HeldCertificate\n        .Builder()\n        .serialNumber(3L)\n        .certificateAuthority(0)\n        .signedBy(certA)\n        .build()\n    val certC =\n      HeldCertificate\n        .Builder()\n        .serialNumber(4L)\n        .signedBy(certB)\n        .build()\n    val cleaner = get(root.certificate)\n    assertThat(cleaner.clean(list(certC, certA, root, certB), \"hostname\")).isEqualTo(\n      list(certC, certB, certA, root),\n    )\n  }\n\n  @Test\n  fun unorderedChainOfCertificatesWithoutRoot() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(2)\n        .build()\n    val certA =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .certificateAuthority(1)\n        .signedBy(root)\n        .build()\n    val certB =\n      HeldCertificate\n        .Builder()\n        .serialNumber(3L)\n        .certificateAuthority(0)\n        .signedBy(certA)\n        .build()\n    val certC =\n      HeldCertificate\n        .Builder()\n        .serialNumber(4L)\n        .signedBy(certB)\n        .build()\n    val cleaner = get(root.certificate)\n    assertThat(cleaner.clean(list(certC, certA, certB), \"hostname\")).isEqualTo(\n      list(certC, certB, certA, root),\n    )\n  }\n\n  @Test\n  fun unrelatedCertificatesAreOmitted() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .build()\n    val certA =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .certificateAuthority(0)\n        .signedBy(root)\n        .build()\n    val certB =\n      HeldCertificate\n        .Builder()\n        .serialNumber(3L)\n        .signedBy(certA)\n        .build()\n    val certUnnecessary =\n      HeldCertificate\n        .Builder()\n        .serialNumber(4L)\n        .build()\n    val cleaner = get(root.certificate)\n    assertThat(cleaner.clean(list(certB, certUnnecessary, certA, root), \"hostname\"))\n      .isEqualTo(\n        list(certB, certA, root),\n      )\n  }\n\n  @Test\n  fun chainGoesAllTheWayToSelfSignedRoot() {\n    val selfSigned =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(2)\n        .build()\n    val trusted =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .signedBy(selfSigned)\n        .certificateAuthority(1)\n        .build()\n    val certA =\n      HeldCertificate\n        .Builder()\n        .serialNumber(3L)\n        .certificateAuthority(0)\n        .signedBy(trusted)\n        .build()\n    val certB =\n      HeldCertificate\n        .Builder()\n        .serialNumber(4L)\n        .signedBy(certA)\n        .build()\n    val cleaner =\n      get(\n        selfSigned.certificate,\n        trusted.certificate,\n      )\n    assertThat(cleaner.clean(list(certB, certA), \"hostname\")).isEqualTo(\n      list(certB, certA, trusted, selfSigned),\n    )\n    assertThat(cleaner.clean(list(certB, certA, trusted), \"hostname\")).isEqualTo(\n      list(certB, certA, trusted, selfSigned),\n    )\n    assertThat(cleaner.clean(list(certB, certA, trusted, selfSigned), \"hostname\"))\n      .isEqualTo(\n        list(certB, certA, trusted, selfSigned),\n      )\n  }\n\n  @Test\n  fun trustedRootNotSelfSigned() {\n    val unknownSigner =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(2)\n        .build()\n    val trusted =\n      HeldCertificate\n        .Builder()\n        .signedBy(unknownSigner)\n        .certificateAuthority(1)\n        .serialNumber(2L)\n        .build()\n    val intermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(trusted)\n        .certificateAuthority(0)\n        .serialNumber(3L)\n        .build()\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(intermediateCa)\n        .serialNumber(4L)\n        .build()\n    val cleaner = get(trusted.certificate)\n    assertThat(cleaner.clean(list(certificate, intermediateCa), \"hostname\"))\n      .isEqualTo(\n        list(certificate, intermediateCa, trusted),\n      )\n    assertThat(cleaner.clean(list(certificate, intermediateCa, trusted), \"hostname\"))\n      .isEqualTo(\n        list(certificate, intermediateCa, trusted),\n      )\n  }\n\n  @Test\n  fun chainMaxLength() {\n    val heldCertificates = chainOfLength(10)\n    val certificates: MutableList<Certificate> = ArrayList()\n    for (heldCertificate in heldCertificates) {\n      certificates.add(heldCertificate.certificate)\n    }\n    val root = heldCertificates[heldCertificates.size - 1].certificate\n    val cleaner = get(root)\n    assertThat(cleaner.clean(certificates, \"hostname\")).isEqualTo(certificates)\n    assertThat(cleaner.clean(certificates.subList(0, 9), \"hostname\")).isEqualTo(\n      certificates,\n    )\n  }\n\n  @Test\n  fun chainTooLong() {\n    val heldCertificates = chainOfLength(11)\n    val certificates: MutableList<Certificate> = ArrayList()\n    for (heldCertificate in heldCertificates) {\n      certificates.add(heldCertificate.certificate)\n    }\n    val root = heldCertificates[heldCertificates.size - 1].certificate\n    val cleaner = get(root)\n    assertFailsWith<SSLPeerUnverifiedException> {\n      cleaner.clean(certificates, \"hostname\")\n    }\n  }\n\n  /** Returns a chain starting at the leaf certificate and progressing to the root.  */\n  private fun chainOfLength(length: Int): List<HeldCertificate> {\n    val result = mutableListOf<HeldCertificate>()\n    for (i in 1..length) {\n      result.add(\n        0,\n        HeldCertificate\n          .Builder()\n          .signedBy(if (result.isNotEmpty()) result[0] else null)\n          .certificateAuthority(length - i)\n          .serialNumber(i.toLong())\n          .build(),\n      )\n    }\n    return result\n  }\n\n  private fun list(vararg heldCertificates: HeldCertificate): List<Certificate> {\n    val result: MutableList<Certificate> = ArrayList()\n    for (heldCertificate in heldCertificates) {\n      result.add(heldCertificate.certificate)\n    }\n    return result\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CertificatePinnerKotlinTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport okhttp3.CertificatePinner.Companion.sha1Hash\nimport okhttp3.CertificatePinner.Pin\nimport okhttp3.tls.HeldCertificate\nimport okio.ByteString.Companion.decodeBase64\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\n\nclass CertificatePinnerKotlinTest {\n  @Test\n  fun successfulCheckSha1Pin() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", \"sha1/\" + certA1.certificate.sha1Hash().base64())\n        .build()\n\n    certificatePinner.check(\"example.com\", listOf(certA1.certificate))\n  }\n\n  @Test fun successfulFindMatchingPins() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"first.com\", certA1Sha256Pin, certB1Sha256Pin)\n        .add(\"second.com\", certC1Sha256Pin)\n        .build()\n\n    val expectedPins =\n      listOf(\n        Pin(\"first.com\", certA1Sha256Pin),\n        Pin(\"first.com\", certB1Sha256Pin),\n      )\n    assertThat(certificatePinner.findMatchingPins(\"first.com\")).isEqualTo(expectedPins)\n  }\n\n  @Test fun successfulFindMatchingPinsForWildcardAndDirectCertificates() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certA1Sha256Pin)\n        .add(\"a.example.com\", certB1Sha256Pin)\n        .add(\"b.example.com\", certC1Sha256Pin)\n        .build()\n\n    val expectedPins =\n      listOf(\n        Pin(\"*.example.com\", certA1Sha256Pin),\n        Pin(\"a.example.com\", certB1Sha256Pin),\n      )\n    assertThat(certificatePinner.findMatchingPins(\"a.example.com\")).isEqualTo(expectedPins)\n  }\n\n  @Test\n  fun wildcardHostnameShouldNotMatchThroughDot() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certA1Sha256Pin)\n        .build()\n\n    assertThat(certificatePinner.findMatchingPins(\"example.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"..example.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"a..example.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"a.b.example.com\")).isEmpty()\n  }\n\n  @Test\n  fun doubleWildcardHostnameShouldMatchThroughDot() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"**.example.com\", certA1Sha256Pin)\n        .build()\n\n    val expectedPin1 = listOf(Pin(\"**.example.com\", certA1Sha256Pin))\n    assertThat(certificatePinner.findMatchingPins(\"example.com\")).isEqualTo(expectedPin1)\n    assertThat(certificatePinner.findMatchingPins(\".example.com\")).isEqualTo(expectedPin1)\n    assertThat(certificatePinner.findMatchingPins(\"..example.com\")).isEqualTo(expectedPin1)\n    assertThat(certificatePinner.findMatchingPins(\"a..example.com\")).isEqualTo(expectedPin1)\n    assertThat(certificatePinner.findMatchingPins(\"a.b.example.com\")).isEqualTo(expectedPin1)\n  }\n\n  @Test\n  fun doubleWildcardHostnameShouldNotMatchSuffix() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"**.example.com\", certA1Sha256Pin)\n        .build()\n\n    assertThat(certificatePinner.findMatchingPins(\"xample.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"dexample.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"barnexample.com\")).isEmpty()\n  }\n\n  @Test fun successfulFindMatchingPinsIgnoresCase() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"EXAMPLE.com\", certA1Sha256Pin)\n        .add(\"*.MyExample.Com\", certB1Sha256Pin)\n        .build()\n\n    val expectedPin1 = listOf(Pin(\"EXAMPLE.com\", certA1Sha256Pin))\n    assertThat(certificatePinner.findMatchingPins(\"example.com\")).isEqualTo(expectedPin1)\n\n    val expectedPin2 = listOf(Pin(\"*.MyExample.Com\", certB1Sha256Pin))\n    assertThat(certificatePinner.findMatchingPins(\"a.myexample.com\")).isEqualTo(expectedPin2)\n  }\n\n  @Test fun successfulFindMatchingPinPunycode() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"σkhttp.com\", certA1Sha256Pin)\n        .build()\n\n    val expectedPin = listOf(Pin(\"σkhttp.com\", certA1Sha256Pin))\n    assertThat(certificatePinner.findMatchingPins(\"xn--khttp-fde.com\")).isEqualTo(expectedPin)\n  }\n\n  /** https://github.com/square/okhttp/issues/3324  */\n  @Test\n  fun checkSubstringMatch() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certA1Sha256Pin)\n        .build()\n\n    assertThat(certificatePinner.findMatchingPins(\"a.example.com.notexample.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"example.com.notexample.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"notexample.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"example.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"a.b.example.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"ple.com\")).isEmpty()\n    assertThat(certificatePinner.findMatchingPins(\"com\")).isEmpty()\n\n    val expectedPin = Pin(\"*.example.com\", certA1Sha256Pin)\n    assertThat(certificatePinner.findMatchingPins(\"a.example.com\")).containsExactly(expectedPin)\n    assertThat(certificatePinner.findMatchingPins(\".example.com\")).containsExactly(expectedPin)\n    assertThat(certificatePinner.findMatchingPins(\"example.example.com\"))\n      .containsExactly(expectedPin)\n  }\n\n  @Test fun testGoodPin() {\n    val pin =\n      Pin(\n        \"**.example.co.uk\",\n        \"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\n      )\n    assertEquals(\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\".decodeBase64(), pin.hash)\n    assertEquals(\"sha256\", pin.hashAlgorithm)\n    assertEquals(\"**.example.co.uk\", pin.pattern)\n    assertTrue(pin.matchesHostname(\"www.example.co.uk\"))\n    assertTrue(pin.matchesHostname(\"gopher.example.co.uk\"))\n    assertFalse(pin.matchesHostname(\"www.example.com\"))\n  }\n\n  @Test fun testMatchesSha256() {\n    val pin = Pin(\"example.com\", certA1Sha256Pin)\n    assertTrue(pin.matchesCertificate(certA1.certificate))\n    assertFalse(pin.matchesCertificate(certB1.certificate))\n  }\n\n  @Test fun testMatchesSha1() {\n    val pin = Pin(\"example.com\", certC1Sha1Pin)\n    assertTrue(pin.matchesCertificate(certC1.certificate))\n    assertFalse(pin.matchesCertificate(certB1.certificate))\n  }\n\n  @Test fun pinList() {\n    val builder =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", CertificatePinnerTest.certA1Sha256Pin)\n        .add(\"www.example.com\", CertificatePinnerTest.certA1Sha256Pin)\n    val certificatePinner = builder.build()\n\n    val expectedPins =\n      listOf(\n        Pin(\"example.com\", CertificatePinnerTest.certA1Sha256Pin),\n        Pin(\"www.example.com\", CertificatePinnerTest.certA1Sha256Pin),\n      )\n\n    assertEquals(expectedPins, builder.pins)\n    assertEquals(expectedPins.toSet(), certificatePinner.pins)\n  }\n\n  companion object {\n    internal var certA1: HeldCertificate =\n      HeldCertificate\n        .Builder()\n        .serialNumber(100L)\n        .build()\n    internal var certA1Sha256Pin = CertificatePinner.pin(certA1.certificate)\n\n    private var certB1 =\n      HeldCertificate\n        .Builder()\n        .serialNumber(200L)\n        .build()\n    internal var certB1Sha256Pin = CertificatePinner.pin(certB1.certificate)\n\n    private var certC1 =\n      HeldCertificate\n        .Builder()\n        .serialNumber(300L)\n        .build()\n    internal var certC1Sha256Pin = CertificatePinner.pin(certC1.certificate)\n    var certC1Sha1Pin = \"sha1/\" + certC1.certificate.sha1Hash().base64()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CertificatePinnerTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotEqualTo\nimport java.util.Arrays\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport kotlin.test.assertFailsWith\nimport okhttp3.CertificatePinner.Companion.pin\nimport okhttp3.CertificatePinner.Companion.sha1Hash\nimport okhttp3.tls.HeldCertificate\nimport okio.ByteString.Companion.decodeBase64\nimport org.junit.jupiter.api.Assertions\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\nclass CertificatePinnerTest {\n  @Test\n  fun malformedPin() {\n    val builder = CertificatePinner.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.add(\"example.com\", \"md5/DmxUShsZuNiqPQsX2Oi9uv2sCnw=\")\n    }\n  }\n\n  @Test\n  fun malformedBase64() {\n    val builder = CertificatePinner.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.add(\"example.com\", \"sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw*\")\n    }\n  }\n\n  /** Multiple certificates generated from the same keypair have the same pin.  */\n  @Test\n  fun sameKeypairSamePin() {\n    val heldCertificateA2 =\n      HeldCertificate\n        .Builder()\n        .keyPair(certA1.keyPair)\n        .serialNumber(101L)\n        .build()\n    val keypairACertificate2Pin = pin(heldCertificateA2.certificate)\n    val heldCertificateB2 =\n      HeldCertificate\n        .Builder()\n        .keyPair(certB1.keyPair)\n        .serialNumber(201L)\n        .build()\n    val keypairBCertificate2Pin = pin(heldCertificateB2.certificate)\n    assertThat(keypairACertificate2Pin).isEqualTo(certA1Sha256Pin)\n    assertThat(keypairBCertificate2Pin).isEqualTo(certB1Sha256Pin)\n    assertThat(certB1Sha256Pin).isNotEqualTo(certA1Sha256Pin)\n  }\n\n  @Test\n  fun successfulCheck() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", certA1Sha256Pin)\n        .build()\n    certificatePinner.check(\"example.com\", listOf(certA1.certificate))\n  }\n\n  @Test\n  fun successfulMatchAcceptsAnyMatchingCertificateOld() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", certB1Sha256Pin)\n        .build()\n    certificatePinner.check(\"example.com\", certA1.certificate, certB1.certificate)\n  }\n\n  @Test\n  fun successfulMatchAcceptsAnyMatchingCertificate() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", certB1Sha256Pin)\n        .build()\n    certificatePinner.check(\n      \"example.com\",\n      Arrays.asList(certA1.certificate, certB1.certificate),\n    )\n  }\n\n  @Test\n  fun unsuccessfulCheck() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", certA1Sha256Pin)\n        .build()\n    assertFailsWith<SSLPeerUnverifiedException> {\n      certificatePinner.check(\"example.com\", certB1.certificate)\n    }\n  }\n\n  @Test\n  fun multipleCertificatesForOneHostname() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", certA1Sha256Pin, certB1Sha256Pin)\n        .build()\n    certificatePinner.check(\"example.com\", listOf(certA1.certificate))\n    certificatePinner.check(\"example.com\", listOf(certB1.certificate))\n  }\n\n  @Test\n  fun multipleHostnamesForOneCertificate() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", certA1Sha256Pin)\n        .add(\"www.example.com\", certA1Sha256Pin)\n        .build()\n    certificatePinner.check(\"example.com\", listOf(certA1.certificate))\n    certificatePinner.check(\"www.example.com\", listOf(certA1.certificate))\n  }\n\n  @Test\n  fun absentHostnameMatches() {\n    val certificatePinner = CertificatePinner.Builder().build()\n    certificatePinner.check(\"example.com\", listOf(certA1.certificate))\n  }\n\n  @Test\n  fun successfulCheckForWildcardHostname() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certA1Sha256Pin)\n        .build()\n    certificatePinner.check(\"a.example.com\", listOf(certA1.certificate))\n  }\n\n  @Test\n  fun successfulMatchAcceptsAnyMatchingCertificateForWildcardHostname() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certB1Sha256Pin)\n        .build()\n    certificatePinner.check(\n      \"a.example.com\",\n      Arrays.asList(certA1.certificate, certB1.certificate),\n    )\n  }\n\n  @Test\n  fun unsuccessfulCheckForWildcardHostname() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certA1Sha256Pin)\n        .build()\n    assertFailsWith<SSLPeerUnverifiedException> {\n      certificatePinner.check(\"a.example.com\", listOf(certB1.certificate))\n    }\n  }\n\n  @Test\n  fun multipleCertificatesForOneWildcardHostname() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certA1Sha256Pin, certB1Sha256Pin)\n        .build()\n    certificatePinner.check(\"a.example.com\", listOf(certA1.certificate))\n    certificatePinner.check(\"a.example.com\", listOf(certB1.certificate))\n  }\n\n  @Test\n  fun successfulCheckForOneHostnameWithWildcardAndDirectCertificate() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certA1Sha256Pin)\n        .add(\"a.example.com\", certB1Sha256Pin)\n        .build()\n    certificatePinner.check(\"a.example.com\", listOf(certA1.certificate))\n    certificatePinner.check(\"a.example.com\", listOf(certB1.certificate))\n  }\n\n  @Test\n  fun unsuccessfulCheckForOneHostnameWithWildcardAndDirectCertificate() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"*.example.com\", certA1Sha256Pin)\n        .add(\"a.example.com\", certB1Sha256Pin)\n        .build()\n    assertFailsWith<SSLPeerUnverifiedException> {\n      certificatePinner.check(\"a.example.com\", listOf(certC1.certificate))\n    }\n  }\n\n  @Test\n  fun checkForHostnameWithDoubleAsterisk() {\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(\"**.example.co.uk\", certA1Sha256Pin)\n        .build()\n\n    // Should be pinned:\n    assertFailsWith<SSLPeerUnverifiedException> {\n      certificatePinner.check(\"example.co.uk\", listOf(certB1.certificate))\n    }\n    assertFailsWith<SSLPeerUnverifiedException> {\n      certificatePinner.check(\"foo.example.co.uk\", listOf(certB1.certificate))\n    }\n    assertFailsWith<SSLPeerUnverifiedException> {\n      certificatePinner.check(\"foo.bar.example.co.uk\", listOf(certB1.certificate))\n    }\n    assertFailsWith<SSLPeerUnverifiedException> {\n      certificatePinner.check(\"foo.bar.baz.example.co.uk\", listOf(certB1.certificate))\n    }\n\n    // Should not be pinned:\n    certificatePinner.check(\"uk\", listOf(certB1.certificate))\n    certificatePinner.check(\"co.uk\", listOf(certB1.certificate))\n    certificatePinner.check(\"anotherexample.co.uk\", listOf(certB1.certificate))\n    certificatePinner.check(\"foo.anotherexample.co.uk\", listOf(certB1.certificate))\n  }\n\n  @Test\n  fun testBadPin() {\n    assertFailsWith<IllegalArgumentException> {\n      CertificatePinner.Pin(\n        \"example.co.uk\",\n        \"sha256/a\",\n      )\n    }\n  }\n\n  @Test\n  fun testBadAlgorithm() {\n    assertFailsWith<IllegalArgumentException> {\n      CertificatePinner.Pin(\n        \"example.co.uk\",\n        \"sha512/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\n      )\n    }\n  }\n\n  @Test\n  fun testBadHost() {\n    assertFailsWith<IllegalArgumentException> {\n      CertificatePinner.Pin(\n        \"example.*\",\n        \"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\n      )\n    }\n  }\n\n  @Test\n  fun testGoodPin() {\n    val pin =\n      CertificatePinner.Pin(\n        \"**.example.co.uk\",\n        \"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\n      )\n    assertEquals(\n      \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\".decodeBase64(),\n      pin.hash,\n    )\n    assertEquals(\"sha256\", pin.hashAlgorithm)\n    assertEquals(\"**.example.co.uk\", pin.pattern)\n    Assertions.assertTrue(pin.matchesHostname(\"www.example.co.uk\"))\n    Assertions.assertTrue(pin.matchesHostname(\"gopher.example.co.uk\"))\n    Assertions.assertFalse(pin.matchesHostname(\"www.example.com\"))\n  }\n\n  @Test\n  fun testMatchesSha256() {\n    val pin = CertificatePinner.Pin(\"example.com\", certA1Sha256Pin)\n    Assertions.assertTrue(pin.matchesCertificate(certA1.certificate))\n    Assertions.assertFalse(pin.matchesCertificate(certB1.certificate))\n  }\n\n  @Test\n  fun testMatchesSha1() {\n    val pin = CertificatePinner.Pin(\"example.com\", certC1Sha1Pin)\n    Assertions.assertTrue(pin.matchesCertificate(certC1.certificate))\n    Assertions.assertFalse(pin.matchesCertificate(certB1.certificate))\n  }\n\n  @Test\n  fun pinList() {\n    val builder =\n      CertificatePinner\n        .Builder()\n        .add(\"example.com\", certA1Sha256Pin)\n        .add(\"www.example.com\", certA1Sha256Pin)\n    val certificatePinner = builder.build()\n    val expectedPins =\n      Arrays.asList(\n        CertificatePinner.Pin(\"example.com\", certA1Sha256Pin),\n        CertificatePinner.Pin(\"www.example.com\", certA1Sha256Pin),\n      )\n    assertEquals(expectedPins, builder.pins)\n    assertEquals(HashSet(expectedPins), certificatePinner.pins)\n  }\n\n  companion object {\n    val certA1 =\n      HeldCertificate\n        .Builder()\n        .serialNumber(100L)\n        .build()\n    val certA1Sha256Pin = pin(certA1.certificate)\n    val certB1 =\n      HeldCertificate\n        .Builder()\n        .serialNumber(200L)\n        .build()\n    val certB1Sha256Pin = pin(certB1.certificate)\n    val certC1 =\n      HeldCertificate\n        .Builder()\n        .serialNumber(300L)\n        .build()\n    val certC1Sha1Pin = \"sha1/\" + certC1.certificate.sha1Hash().base64()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ChannelSocketFactory.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.net.InetAddress\nimport java.net.Socket\nimport java.nio.channels.SocketChannel\nimport javax.net.SocketFactory\n\nclass ChannelSocketFactory : SocketFactory() {\n  override fun createSocket(): Socket = SocketChannel.open().socket()\n\n  override fun createSocket(\n    host: String,\n    port: Int,\n  ): Socket = TODO(\"Not yet implemented\")\n\n  override fun createSocket(\n    host: String,\n    port: Int,\n    localHost: InetAddress,\n    localPort: Int,\n  ): Socket = TODO(\"Not yet implemented\")\n\n  override fun createSocket(\n    host: InetAddress,\n    port: Int,\n  ): Socket = TODO(\"Not yet implemented\")\n\n  override fun createSocket(\n    address: InetAddress,\n    port: Int,\n    localAddress: InetAddress,\n    localPort: Int,\n  ): Socket = TODO(\"Not yet implemented\")\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CipherSuiteTest.kt",
    "content": "/*\n * Copyright (C) 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotEqualTo\nimport assertk.assertions.isSameInstanceAs\nimport okhttp3.CipherSuite.Companion.forJavaName\nimport okhttp3.internal.applyConnectionSpec\nimport org.junit.jupiter.api.Assertions.assertArrayEquals\nimport org.junit.jupiter.api.Test\n\nclass CipherSuiteTest {\n  @Test\n  fun hashCode_usesIdentityHashCode_legacyCase() {\n    val cs = CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5 // This one's javaName starts with \"SSL_\".\n    assertThat(cs.hashCode(), cs.toString())\n      .isEqualTo(System.identityHashCode(cs))\n  }\n\n  @Test\n  fun hashCode_usesIdentityHashCode_regularCase() {\n    // This one's javaName matches the identifier.\n    val cs = CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256\n    assertThat(cs.hashCode(), cs.toString())\n      .isEqualTo(System.identityHashCode(cs))\n  }\n\n  @Test\n  fun instancesAreInterned() {\n    assertThat(forJavaName(\"TestCipherSuite\"))\n      .isSameInstanceAs(forJavaName(\"TestCipherSuite\"))\n    assertThat(forJavaName(CipherSuite.TLS_KRB5_WITH_DES_CBC_MD5.javaName))\n      .isSameInstanceAs(\n        CipherSuite.TLS_KRB5_WITH_DES_CBC_MD5,\n      )\n  }\n\n  /**\n   * Tests that interned CipherSuite instances remain the case across garbage collections, even if\n   * the String used to construct them is no longer strongly referenced outside of the CipherSuite.\n   */\n  @Test\n  fun instancesAreInterned_survivesGarbageCollection() {\n    // We're not holding onto a reference to this String instance outside of the CipherSuite...\n    val cs = forJavaName(\"FakeCipherSuite_instancesAreInterned\")\n    System.gc() // Unless cs references the String instance, it may now be garbage collected.\n    assertThat(forJavaName(java.lang.String(cs.javaName) as String))\n      .isSameInstanceAs(cs)\n  }\n\n  @Test\n  fun equals() {\n    assertThat(forJavaName(\"cipher\")).isEqualTo(forJavaName(\"cipher\"))\n    assertThat(forJavaName(\"cipherB\")).isNotEqualTo(forJavaName(\"cipherA\"))\n    assertThat(CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5)\n      .isEqualTo(forJavaName(\"SSL_RSA_EXPORT_WITH_RC4_40_MD5\"))\n    assertThat(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256)\n      .isNotEqualTo(CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5)\n  }\n\n  @Test\n  fun forJavaName_acceptsArbitraryStrings() {\n    // Shouldn't throw.\n    forJavaName(\"example CipherSuite name that is not in the whitelist\")\n  }\n\n  @Test\n  fun javaName_examples() {\n    assertThat(CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.javaName)\n      .isEqualTo(\"SSL_RSA_EXPORT_WITH_RC4_40_MD5\")\n    assertThat(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256.javaName)\n      .isEqualTo(\"TLS_RSA_WITH_AES_128_CBC_SHA256\")\n    assertThat(forJavaName(\"TestCipherSuite\").javaName)\n      .isEqualTo(\"TestCipherSuite\")\n  }\n\n  @Test\n  fun javaName_equalsToString() {\n    assertThat(CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.toString())\n      .isEqualTo(CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.javaName)\n    assertThat(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256.toString())\n      .isEqualTo(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256.javaName)\n  }\n\n  /**\n   * On the Oracle JVM some older cipher suites have the \"SSL_\" prefix and others have the \"TLS_\"\n   * prefix. On the IBM JVM all cipher suites have the \"SSL_\" prefix.\n   *\n   * Prior to OkHttp 3.3.1 we accepted either form and consider them equivalent. And since OkHttp\n   * 3.7.0 this is also true. But OkHttp 3.3.1 through 3.6.0 treated these as different.\n   */\n  @Test\n  fun forJavaName_fromLegacyEnumName() {\n    // These would have been considered equal in OkHttp 3.3.1, but now aren't.\n    assertThat(forJavaName(\"SSL_RSA_EXPORT_WITH_RC4_40_MD5\"))\n      .isEqualTo(forJavaName(\"TLS_RSA_EXPORT_WITH_RC4_40_MD5\"))\n    assertThat(forJavaName(\"SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA\"))\n      .isEqualTo(forJavaName(\"TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA\"))\n    assertThat(forJavaName(\"SSL_FAKE_NEW_CIPHER\"))\n      .isEqualTo(forJavaName(\"TLS_FAKE_NEW_CIPHER\"))\n  }\n\n  @Test\n  fun applyIntersectionRetainsTlsPrefixes() {\n    val socket = FakeSslSocket()\n    socket.enabledProtocols = arrayOf(\"TLSv1\")\n    socket.supportedCipherSuites = arrayOf(\"SSL_A\", \"SSL_B\", \"SSL_C\", \"SSL_D\", \"SSL_E\")\n    socket.enabledCipherSuites = arrayOf(\"SSL_A\", \"SSL_B\", \"SSL_C\")\n    val connectionSpec =\n      ConnectionSpec\n        .Builder(true)\n        .tlsVersions(TlsVersion.TLS_1_0)\n        .cipherSuites(\"TLS_A\", \"TLS_C\", \"TLS_E\")\n        .build()\n    applyConnectionSpec(connectionSpec, socket, false)\n    assertArrayEquals(arrayOf(\"TLS_A\", \"TLS_C\"), socket.enabledCipherSuites)\n  }\n\n  @Test\n  fun applyIntersectionRetainsSslPrefixes() {\n    val socket = FakeSslSocket()\n    socket.enabledProtocols = arrayOf(\"TLSv1\")\n    socket.supportedCipherSuites =\n      arrayOf(\"TLS_A\", \"TLS_B\", \"TLS_C\", \"TLS_D\", \"TLS_E\")\n    socket.enabledCipherSuites = arrayOf(\"TLS_A\", \"TLS_B\", \"TLS_C\")\n    val connectionSpec =\n      ConnectionSpec\n        .Builder(true)\n        .tlsVersions(TlsVersion.TLS_1_0)\n        .cipherSuites(\"SSL_A\", \"SSL_C\", \"SSL_E\")\n        .build()\n    applyConnectionSpec(connectionSpec, socket, false)\n    assertArrayEquals(arrayOf(\"SSL_A\", \"SSL_C\"), socket.enabledCipherSuites)\n  }\n\n  @Test\n  fun applyIntersectionAddsSslScsvForFallback() {\n    val socket = FakeSslSocket()\n    socket.enabledProtocols = arrayOf(\"TLSv1\")\n    socket.supportedCipherSuites = arrayOf(\"SSL_A\", \"SSL_FALLBACK_SCSV\")\n    socket.enabledCipherSuites = arrayOf(\"SSL_A\")\n    val connectionSpec =\n      ConnectionSpec\n        .Builder(true)\n        .tlsVersions(TlsVersion.TLS_1_0)\n        .cipherSuites(\"SSL_A\")\n        .build()\n    applyConnectionSpec(connectionSpec, socket, true)\n    assertArrayEquals(\n      arrayOf(\"SSL_A\", \"SSL_FALLBACK_SCSV\"),\n      socket.enabledCipherSuites,\n    )\n  }\n\n  @Test\n  fun applyIntersectionAddsTlsScsvForFallback() {\n    val socket = FakeSslSocket()\n    socket.enabledProtocols = arrayOf(\"TLSv1\")\n    socket.supportedCipherSuites = arrayOf(\"TLS_A\", \"TLS_FALLBACK_SCSV\")\n    socket.enabledCipherSuites = arrayOf(\"TLS_A\")\n    val connectionSpec =\n      ConnectionSpec\n        .Builder(true)\n        .tlsVersions(TlsVersion.TLS_1_0)\n        .cipherSuites(\"TLS_A\")\n        .build()\n    applyConnectionSpec(connectionSpec, socket, true)\n    assertArrayEquals(\n      arrayOf(\"TLS_A\", \"TLS_FALLBACK_SCSV\"),\n      socket.enabledCipherSuites,\n    )\n  }\n\n  @Test\n  fun applyIntersectionToProtocolVersion() {\n    val socket = FakeSslSocket()\n    socket.enabledProtocols = arrayOf(\"TLSv1\", \"TLSv1.1\", \"TLSv1.2\")\n    socket.supportedCipherSuites = arrayOf(\"TLS_A\")\n    socket.enabledCipherSuites = arrayOf(\"TLS_A\")\n    val connectionSpec =\n      ConnectionSpec\n        .Builder(true)\n        .tlsVersions(TlsVersion.TLS_1_1, TlsVersion.TLS_1_2, TlsVersion.TLS_1_3)\n        .cipherSuites(\"TLS_A\")\n        .build()\n    applyConnectionSpec(connectionSpec, socket, false)\n    assertArrayEquals(arrayOf(\"TLSv1.1\", \"TLSv1.2\"), socket.enabledProtocols)\n  }\n\n  internal class FakeSslSocket : DelegatingSSLSocket(null) {\n    private lateinit var enabledProtocols: Array<String>\n    private lateinit var supportedCipherSuites: Array<String>\n    private lateinit var enabledCipherSuites: Array<String>\n\n    override fun getEnabledProtocols(): Array<String> = enabledProtocols\n\n    override fun setEnabledProtocols(protocols: Array<String>) {\n      this.enabledProtocols = protocols\n    }\n\n    override fun getSupportedCipherSuites(): Array<String> = supportedCipherSuites\n\n    fun setSupportedCipherSuites(supportedCipherSuites: Array<String>) {\n      this.supportedCipherSuites = supportedCipherSuites\n    }\n\n    override fun getEnabledCipherSuites(): Array<String> = enabledCipherSuites\n\n    override fun setEnabledCipherSuites(suites: Array<String>) {\n      this.enabledCipherSuites = suites\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CommonRequestBodyTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport kotlin.test.Test\nimport okhttp3.RequestBody.Companion.toRequestBody\n\nclass CommonRequestBodyTest {\n  @Test\n  fun correctContentType() {\n    val body = \"Body\"\n    val requestBody = body.toRequestBody(MediaType(\"text/plain\", \"text\", \"plain\", arrayOf()))\n\n    val contentType = requestBody.contentType()!!\n\n    assertThat(contentType.mediaType).isEqualTo(\"text/plain; charset=utf-8\")\n    assertThat(contentType.parameter(\"charset\")).isEqualTo(\"utf-8\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ConnectionCoalescingTest.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.fail\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.security.cert.X509Certificate\nimport java.util.Arrays\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.concurrent.atomic.AtomicReference\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSession\nimport javax.net.ssl.X509TrustManager\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CertificatePinner.Companion.pin\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Tag(\"Slowish\")\nclass ConnectionCoalescingTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private lateinit var client: OkHttpClient\n  private lateinit var rootCa: HeldCertificate\n  private lateinit var certificate: HeldCertificate\n  private val dns = FakeDns()\n  private lateinit var url: HttpUrl\n  private lateinit var serverIps: List<InetAddress>\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeHttp2Support()\n    platform.assumeNotBouncyCastle()\n    rootCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(0)\n        .commonName(\"root\")\n        .build()\n    certificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(rootCa)\n        .serialNumber(2L)\n        .commonName(server.hostName)\n        .addSubjectAlternativeName(server.hostName)\n        .addSubjectAlternativeName(\"san.com\")\n        .addSubjectAlternativeName(\"*.wildcard.com\")\n        .addSubjectAlternativeName(\"differentdns.com\")\n        .build()\n    serverIps = Dns.SYSTEM.lookup(server.hostName)\n    dns[server.hostName] = serverIps\n    dns[\"san.com\"] = serverIps\n    dns[\"nonsan.com\"] = serverIps\n    dns[\"www.wildcard.com\"] = serverIps\n    dns[\"differentdns.com\"] = listOf()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(rootCa.certificate)\n        .build()\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .fastFallback(false) // Avoid data races.\n        .dns(dns)\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    val serverHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(certificate)\n        .build()\n    server.useHttps(serverHandshakeCertificates.sslSocketFactory())\n    url = server.url(\"/robots.txt\")\n  }\n\n  /**\n   * Test connecting to the main host then an alternative, although only subject alternative names\n   * are used if present no special consideration of common name.\n   */\n  @Test\n  fun commonThenAlternative() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    assert200Http2Response(execute(sanUrl), \"san.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n  }\n\n  /**\n   * Test connecting to an alternative host then common name, although only subject alternative\n   * names are used if present no special consideration of common name.\n   */\n  @Test\n  fun alternativeThenCommon() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    assert200Http2Response(execute(sanUrl), \"san.com\")\n    assert200Http2Response(execute(url), server.hostName)\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n  }\n\n  /** Test a previously coalesced connection that's no longer healthy.  */\n  @Test\n  fun staleCoalescedConnection() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    val connection = AtomicReference<Connection?>()\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            connection.set(chain!!.connection())\n            chain.proceed(chain.request())\n          },\n        ).build()\n    dns[\"san.com\"] = Dns.SYSTEM.lookup(server.hostName).subList(0, 1)\n    assert200Http2Response(execute(url), server.hostName)\n\n    // Simulate a stale connection in the pool.\n    connection.get()!!.socket().close()\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    assert200Http2Response(execute(sanUrl), \"san.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n  }\n\n  /**\n   * This is an extraordinary test case. Here's what it's trying to simulate.\n   * - 2 requests happen concurrently to a host that can be coalesced onto a single connection.\n   * - Both request discover no existing connection. They both make a connection.\n   * - The first request \"wins the race\".\n   * - The second request discovers it \"lost the race\" and closes the connection it just opened.\n   * - The second request uses the coalesced connection from request1.\n   * - The coalesced connection is violently closed after servicing the first request.\n   * - The second request discovers the coalesced connection is unhealthy just after acquiring it.\n   */\n  @Test\n  fun coalescedConnectionDestroyedAfterAcquire() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    dns[\"san.com\"] = Dns.SYSTEM.lookup(server.hostName).subList(0, 1)\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    val latch1 = CountDownLatch(1)\n    val latch2 = CountDownLatch(1)\n    val latch3 = CountDownLatch(1)\n    val latch4 = CountDownLatch(1)\n    val listener1: EventListener =\n      object : EventListener() {\n        override fun connectStart(\n          call: Call,\n          inetSocketAddress: InetSocketAddress,\n          proxy: Proxy,\n        ) {\n          try {\n            // Wait for request2 to guarantee we make 2 separate connections to the server.\n            latch1.await()\n          } catch (e: InterruptedException) {\n            throw AssertionError(e)\n          }\n        }\n\n        override fun connectionAcquired(\n          call: Call,\n          connection: Connection,\n        ) {\n          // We have the connection and it's in the pool. Let request2 proceed to make a connection.\n          latch2.countDown()\n        }\n      }\n    val request2Listener: EventListener =\n      object : EventListener() {\n        override fun connectStart(\n          call: Call,\n          inetSocketAddress: InetSocketAddress,\n          proxy: Proxy,\n        ) {\n          // Let request1 proceed to make a connection.\n          latch1.countDown()\n          try {\n            // Wait until request1 makes the connection and puts it in the connection pool.\n            latch2.await()\n          } catch (e: InterruptedException) {\n            throw AssertionError(e)\n          }\n        }\n\n        override fun connectionAcquired(\n          call: Call,\n          connection: Connection,\n        ) {\n          // We obtained the coalesced connection. Let request1 violently destroy it.\n          latch3.countDown()\n          try {\n            latch4.await()\n          } catch (e: InterruptedException) {\n            throw AssertionError(e)\n          }\n        }\n      }\n\n    // Get a reference to the connection so we can violently destroy it.\n    val connection = AtomicReference<Connection?>()\n    val client1 =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            connection.set(chain!!.connection())\n            chain.proceed(chain.request())\n          },\n        ).eventListenerFactory(clientTestRule.wrap(listener1))\n        .build()\n    val request = Request.Builder().url(sanUrl).build()\n    val call1 = client1.newCall(request)\n    call1.enqueue(\n      object : Callback {\n        @Throws(IOException::class)\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          try {\n            // Wait until request2 acquires the connection before we destroy it violently.\n            latch3.await()\n          } catch (e: InterruptedException) {\n            throw AssertionError(e)\n          }\n          assert200Http2Response(response, \"san.com\")\n          connection.get()!!.socket().close()\n          latch4.countDown()\n        }\n\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          fail(\"\")\n        }\n      },\n    )\n    val client2 =\n      client\n        .newBuilder()\n        .eventListenerFactory(clientTestRule.wrap(request2Listener))\n        .build()\n    val call2 = client2.newCall(request)\n    val response = call2.execute()\n    assert200Http2Response(response, \"san.com\")\n  }\n\n  /** If the existing connection matches a SAN but not a match for DNS then skip.  */\n  @Test\n  fun skipsWhenDnsDontMatch() {\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val differentDnsUrl = url.newBuilder().host(\"differentdns.com\").build()\n    assertFailsWith<IOException> {\n      execute(differentDnsUrl)\n    }\n  }\n\n  @Test\n  fun skipsOnRedirectWhenDnsDontMatch() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .addHeader(\"Location\", url.newBuilder().host(\"differentdns.com\").build())\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"unexpected call\")\n        .build(),\n    )\n    assertFailsWith<IOException> {\n      val response = execute(url)\n      response.close()\n    }\n  }\n\n  /** Not in the certificate SAN.  */\n  @Test\n  fun skipsWhenNotSubjectAltName() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val nonsanUrl = url.newBuilder().host(\"nonsan.com\").build()\n    assertFailsWith<SSLPeerUnverifiedException> {\n      execute(nonsanUrl)\n    }\n  }\n\n  @Test\n  fun skipsOnRedirectWhenNotSubjectAltName() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .addHeader(\"Location\", url.newBuilder().host(\"nonsan.com\").build())\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    assertFailsWith<SSLPeerUnverifiedException> {\n      val response = execute(url)\n      response.close()\n    }\n  }\n\n  /** Can still coalesce when pinning is used if pins match.  */\n  @Test\n  fun coalescesWhenCertificatePinsMatch() {\n    val pinner =\n      CertificatePinner\n        .Builder()\n        .add(\"san.com\", pin(certificate.certificate))\n        .build()\n    client = client.newBuilder().certificatePinner(pinner).build()\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    assert200Http2Response(execute(sanUrl), \"san.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n  }\n\n  /** Certificate pinning used and not a match will avoid coalescing and try to connect.  */\n  @Test\n  fun skipsWhenCertificatePinningFails() {\n    val pinner =\n      CertificatePinner\n        .Builder()\n        .add(\"san.com\", \"sha1/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=\")\n        .build()\n    client = client.newBuilder().certificatePinner(pinner).build()\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    assertFailsWith<IOException> {\n      execute(sanUrl)\n    }\n  }\n\n  @Test\n  fun skipsOnRedirectWhenCertificatePinningFails() {\n    val pinner =\n      CertificatePinner\n        .Builder()\n        .add(\"san.com\", \"sha1/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=\")\n        .build()\n    client = client.newBuilder().certificatePinner(pinner).build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .addHeader(\"Location\", url.newBuilder().host(\"san.com\").build())\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    assertFailsWith<SSLPeerUnverifiedException> {\n      execute(url)\n    }\n  }\n\n  /**\n   * Skips coalescing when hostname verifier is overridden since the intention of the hostname\n   * verification is a black box.\n   */\n  @Test\n  fun skipsWhenHostnameVerifierUsed() {\n    val verifier = HostnameVerifier { name: String?, session: SSLSession? -> true }\n    client = client.newBuilder().hostnameVerifier(verifier).build()\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    assert200Http2Response(execute(sanUrl), \"san.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(2)\n  }\n\n  @Test\n  fun skipsOnRedirectWhenHostnameVerifierUsed() {\n    val verifier = HostnameVerifier { name: String?, session: SSLSession? -> true }\n    client = client.newBuilder().hostnameVerifier(verifier).build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .addHeader(\"Location\", url.newBuilder().host(\"san.com\").build())\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), \"san.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(2)\n    val c0e0 = server.takeRequest()\n    assertThat(c0e0.connectionIndex).isEqualTo(0) // Fresh connection.\n    val c1e0 = server.takeRequest()\n    assertThat(c1e0.connectionIndex).isEqualTo(1) // Fresh connection.\n  }\n\n  /**\n   * Check we would use an existing connection to a later DNS result instead of connecting to the\n   * first DNS result for the first time.\n   */\n  @Test\n  fun prefersExistingCompatible() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    val connectCount = AtomicInteger()\n    val listener: EventListener =\n      object : EventListener() {\n        override fun connectStart(\n          call: Call,\n          inetSocketAddress: InetSocketAddress,\n          proxy: Proxy,\n        ) {\n          connectCount.getAndIncrement()\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .eventListenerFactory(clientTestRule.wrap(listener))\n        .build()\n    assert200Http2Response(execute(url), server.hostName)\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    dns[\"san.com\"] =\n      Arrays.asList(\n        InetAddress.getByAddress(\"san.com\", byteArrayOf(0, 0, 0, 0)),\n        serverIps[0],\n      )\n    assert200Http2Response(execute(sanUrl), \"san.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n    assertThat(connectCount.get()).isEqualTo(1)\n  }\n\n  /** Check that wildcard SANs are supported.  */\n  @Test\n  fun commonThenWildcard() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val sanUrl = url.newBuilder().host(\"www.wildcard.com\").build()\n    assert200Http2Response(execute(sanUrl), \"www.wildcard.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n  }\n\n  /** Network interceptors check for changes to target.  */\n  @Test\n  fun worksWithNetworkInterceptors() {\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            chain!!.proceed(\n              chain.request(),\n            )\n          },\n        ).build()\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    assert200Http2Response(execute(sanUrl), \"san.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun misdirectedRequestResponseCode() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"seed connection\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(421)\n        .body(\"misdirected!\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"after misdirect\")\n        .build(),\n    )\n\n    // Seed the connection pool.\n    assert200Http2Response(execute(url), server.hostName)\n\n    // Use the coalesced connection which should retry on a fresh connection.\n    val sanUrl =\n      url\n        .newBuilder()\n        .host(\"san.com\")\n        .build()\n    execute(sanUrl).use { response ->\n      assertThat(response.code).isEqualTo(200)\n      assertThat(response.priorResponse!!.code).isEqualTo(421)\n      assertThat(response.body.string()).isEqualTo(\"after misdirect\")\n    }\n    val c0e0 = server.takeRequest()\n    assertThat(c0e0.connectionIndex).isEqualTo(0)\n    assertThat(c0e0.exchangeIndex).isEqualTo(0)\n    val c0e1 = server.takeRequest()\n    assertThat(c0e1.connectionIndex).isEqualTo(0)\n    assertThat(c0e1.exchangeIndex).isEqualTo(1)\n    val c1e0 = server.takeRequest()\n    assertThat(c1e0.connectionIndex).isEqualTo(1) // Fresh connection.\n    assertThat(c1e0.exchangeIndex).isEqualTo(0)\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(2)\n  }\n\n  /**\n   * Won't coalesce if we can't clean certs e.g. a dev setup.\n   */\n  @Test\n  fun redirectWithDevSetup() {\n    val trustManager: X509TrustManager =\n      object : X509TrustManager {\n        override fun checkClientTrusted(\n          x509Certificates: Array<X509Certificate>,\n          s: String,\n        ) {\n        }\n\n        override fun checkServerTrusted(\n          x509Certificates: Array<X509Certificate>,\n          s: String,\n        ) {\n        }\n\n        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()\n      }\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(client.sslSocketFactory, trustManager)\n        .build()\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    assert200Http2Response(execute(url), server.hostName)\n    val sanUrl = url.newBuilder().host(\"san.com\").build()\n    assert200Http2Response(execute(sanUrl), \"san.com\")\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(2)\n  }\n\n  private fun execute(url: HttpUrl) = client.newCall(Request(url = url)).execute()\n\n  private fun assert200Http2Response(\n    response: Response,\n    expectedHost: String,\n  ) {\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.request.url.host).isEqualTo(expectedHost)\n    assertThat(response.protocol).isEqualTo(Protocol.HTTP_2)\n    response.body.close()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ConnectionListenerTest.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\n  \"CANNOT_OVERRIDE_INVISIBLE_MEMBER\",\n  \"INVISIBLE_MEMBER\",\n  \"INVISIBLE_REFERENCE\",\n)\n\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isIn\nimport java.io.IOException\nimport java.net.InetSocketAddress\nimport java.net.UnknownHostException\nimport java.time.Duration\nimport java.util.Arrays\nimport java.util.concurrent.TimeUnit\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.internal.DoubleInetAddressDns\nimport okhttp3.internal.connection.RealConnectionPool.Companion.get\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Flaky // STDOUT logging enabled for test\n@Timeout(30)\n@Tag(\"Slow\")\nopen class ConnectionListenerTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private val listener = RecordingConnectionListener()\n  private val handshakeCertificates = localhost()\n\n  open val fastFallback: Boolean get() = true\n\n  private var client: OkHttpClient =\n    clientTestRule\n      .newClientBuilder()\n      .connectionPool(ConnectionPool(connectionListener = listener))\n      .fastFallback(fastFallback)\n      .build()\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n    platform.assumeNotBouncyCastle()\n    listener.forbidLock(get(client.connectionPool))\n    listener.forbidLock(client.dispatcher)\n  }\n\n  @Test\n  fun successfulCallEventSequence() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.body.close()\n    assertThat(listener.recordedEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @Test\n  fun failedCallEventSequence() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(2, TimeUnit.SECONDS)\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(250))\n        .build()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isIn(\"timeout\", \"Read timed out\")\n    }\n    assertThat(listener.recordedEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"NoNewExchanges\",\n      \"ConnectionReleased\",\n      \"ConnectionClosed\",\n    )\n  }\n\n  @Throws(IOException::class)\n  private fun assertSuccessfulEventOrder() {\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.string()\n    response.body.close()\n    assertThat(listener.recordedEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun secondCallEventSequence() {\n    enableTls()\n    server.protocols = listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n\n    client\n      .newCall(Request(server.url(\"/\")))\n      .execute()\n      .close()\n\n    client\n      .newCall(Request(server.url(\"/\")))\n      .execute()\n      .close()\n\n    assertThat(listener.recordedEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun successfulEmptyH2CallEventSequence() {\n    enableTls()\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    server.enqueue(MockResponse())\n    assertSuccessfulEventOrder()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun multipleDnsLookupsForSingleCall() {\n    server.enqueue(\n      MockResponse(\n        code = 301,\n        headers = headersOf(\"Location\", \"http://www.fakeurl:\" + server.port),\n      ),\n    )\n    server.enqueue(MockResponse())\n    val dns = FakeDns()\n    dns[\"fakeurl\"] = client.dns.lookup(server.hostName)\n    dns[\"www.fakeurl\"] = client.dns.lookup(server.hostName)\n    client =\n      client\n        .newBuilder()\n        .dns(dns)\n        .build()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(\"http://fakeurl:\" + server.port)\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    listener.removeUpToEvent(ConnectionEvent.ConnectEnd::class.java)\n    listener.removeUpToEvent(ConnectionEvent.ConnectionReleased::class.java)\n    listener.removeUpToEvent(ConnectionEvent.ConnectionAcquired::class.java)\n    listener.removeUpToEvent(ConnectionEvent.ConnectionReleased::class.java)\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun successfulConnect() {\n    server.enqueue(MockResponse())\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val address = client.dns.lookup(server.hostName)[0]\n    val expectedAddress = InetSocketAddress(address, server.port)\n    val event = listener.removeUpToEvent(ConnectionEvent.ConnectStart::class.java)\n    assertThat(event.route.socketAddress).isEqualTo(expectedAddress)\n  }\n\n  @Test\n  @Throws(UnknownHostException::class)\n  fun failedConnect() {\n    enableTls()\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    val address = client.dns.lookup(server.hostName)[0]\n    val expectedAddress = InetSocketAddress(address, server.port)\n    val event = listener.removeUpToEvent(ConnectionEvent.ConnectFailed::class.java)\n    assertThat(event.route.socketAddress).isEqualTo(expectedAddress)\n\n    // Read error: ssl=0x7fd1d8d0fee8: Failure in SSL library, usually a protocol error\n    if (!platform.isConscrypt()) {\n      assertThat(event.exception.message).isIn(\n        \"Unexpected handshake message: client_hello\",\n        \"(unexpected_message) Unexpected handshake message: client_hello\",\n      )\n    }\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun multipleConnectsForSingleCall() {\n    enableTls()\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    assertThat(listener.recordedEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectFailed\",\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun successfulHttpProxyConnect() {\n    server.enqueue(MockResponse())\n    val proxy = server.proxyAddress\n    client =\n      client\n        .newBuilder()\n        .proxy(proxy)\n        .build()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(\"http://www.fakeurl\")\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    assertThat(listener.recordedEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n    val event = listener.removeUpToEvent(ConnectionEvent.ConnectEnd::class.java)\n    assertThat(event.connection.route().proxy).isEqualTo(proxy)\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n}\n\n@Flaky // STDOUT logging enabled for test\n@Timeout(30)\n@Tag(\"Slow\")\nclass ConnectionListenerLegacyTest : ConnectionListenerTest() {\n  override val fastFallback get() = false\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ConnectionReuseTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport java.util.concurrent.TimeUnit\nimport javax.net.ssl.SSLException\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okhttp3.internal.closeQuietly\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport org.bouncycastle.tls.TlsFatalAlert\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Timeout(30)\n@Tag(\"Slowish\")\nclass ConnectionReuseTest {\n  @RegisterExtension\n  val platform: PlatformRule = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule: OkHttpClientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private var client: OkHttpClient = clientTestRule.newClient()\n\n  @Test\n  fun connectionsAreReused() {\n    server.enqueue(MockResponse(body = \"a\"))\n    server.enqueue(MockResponse(body = \"b\"))\n    val request = Request(server.url(\"/\"))\n    assertConnectionReused(request, request)\n  }\n\n  @Test\n  fun connectionsAreReusedForPosts() {\n    server.enqueue(MockResponse(body = \"a\"))\n    server.enqueue(MockResponse(body = \"b\"))\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = \"request body\".toRequestBody(\"text/plain\".toMediaType()),\n      )\n    assertConnectionReused(request, request)\n  }\n\n  @Test\n  fun connectionsAreReusedWithHttp2() {\n    enableHttp2()\n    server.enqueue(MockResponse(body = \"a\"))\n    server.enqueue(MockResponse(body = \"b\"))\n    val request = Request(server.url(\"/\"))\n    assertConnectionReused(request, request)\n  }\n\n  @Test\n  fun connectionsAreNotReusedWithRequestConnectionClose() {\n    server.enqueue(MockResponse(body = \"a\"))\n    server.enqueue(MockResponse(body = \"b\"))\n    val requestA =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Connection\", \"close\")\n        .build()\n    val requestB = Request(server.url(\"/\"))\n    assertConnectionNotReused(requestA, requestB)\n  }\n\n  @Test\n  fun connectionsAreNotReusedWithResponseConnectionClose() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Connection\", \"close\"),\n        body = \"a\",\n      ),\n    )\n    server.enqueue(MockResponse(body = \"b\"))\n    val requestA = Request(server.url(\"/\"))\n    val requestB = Request(server.url(\"/\"))\n    assertConnectionNotReused(requestA, requestB)\n  }\n\n  @Test\n  fun connectionsAreNotReusedWithUnknownLengthResponseBody() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .clearHeaders()\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"b\"))\n    val request = Request(server.url(\"/\"))\n    assertConnectionNotReused(request, request)\n  }\n\n  @Test\n  fun connectionsAreNotReusedIfPoolIsSizeZero() {\n    client =\n      client\n        .newBuilder()\n        .connectionPool(ConnectionPool(0, 5, TimeUnit.SECONDS))\n        .build()\n    server.enqueue(MockResponse(body = \"a\"))\n    server.enqueue(MockResponse(body = \"b\"))\n    val request = Request(server.url(\"/\"))\n    assertConnectionNotReused(request, request)\n  }\n\n  @Test\n  fun connectionsReusedWithRedirectEvenIfPoolIsSizeZero() {\n    client =\n      client\n        .newBuilder()\n        .connectionPool(ConnectionPool(0, 5, TimeUnit.SECONDS))\n        .build()\n    server.enqueue(\n      MockResponse(\n        code = 301,\n        headers = headersOf(\"Location\", \"/b\"),\n        body = \"a\",\n      ),\n    )\n    server.enqueue(MockResponse(body = \"b\"))\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"b\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun connectionsNotReusedWithRedirectIfDiscardingResponseIsSlow() {\n    client =\n      client\n        .newBuilder()\n        .connectionPool(ConnectionPool(0, 5, TimeUnit.SECONDS))\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .addHeader(\"Location: /b\")\n        .bodyDelay(1, TimeUnit.SECONDS)\n        .body(\"a\")\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"b\"))\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"b\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun silentRetryWhenIdempotentRequestFailsOnReusedConnection() {\n    server.enqueue(MockResponse(body = \"a\"))\n    server.enqueue(MockResponse.Builder().onResponseStart(CloseSocket()).build())\n    server.enqueue(MockResponse(body = \"b\"))\n    val request = Request(server.url(\"/\"))\n    val responseA = client.newCall(request).execute()\n    assertThat(responseA.body.string()).isEqualTo(\"a\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    val responseB = client.newCall(request).execute()\n    assertThat(responseB.body.string()).isEqualTo(\"b\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun http2ConnectionsAreSharedBeforeResponseIsConsumed() {\n    enableHttp2()\n    server.enqueue(MockResponse(body = \"a\"))\n    server.enqueue(MockResponse(body = \"b\"))\n    val request = Request(server.url(\"/\"))\n    val response1 = client.newCall(request).execute()\n    val response2 = client.newCall(request).execute()\n    response1.body.string() // Discard the response body.\n    response2.body.string() // Discard the response body.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun connectionsAreEvicted() {\n    server.enqueue(MockResponse(body = \"a\"))\n    server.enqueue(MockResponse(body = \"b\"))\n    client =\n      client\n        .newBuilder()\n        .connectionPool(ConnectionPool(5, 250, TimeUnit.MILLISECONDS))\n        .build()\n    val request = Request(server.url(\"/\"))\n    val response1 = client.newCall(request).execute()\n    assertThat(response1.body.string()).isEqualTo(\"a\")\n\n    // Give the thread pool a chance to evict.\n    Thread.sleep(500)\n    val response2 = client.newCall(request).execute()\n    assertThat(response2.body.string()).isEqualTo(\"b\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun connectionsAreNotReusedIfSslSocketFactoryChanges() {\n    enableHttps()\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    response.body.close()\n\n    // This client shares a connection pool but has a different SSL socket factory.\n    val handshakeCertificates2 = HandshakeCertificates.Builder().build()\n    val anotherClient =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates2.sslSocketFactory(),\n          handshakeCertificates2.trustManager,\n        ).build()\n\n    // This client fails to connect because the new SSL socket factory refuses.\n    assertFailsWith<IOException> {\n      anotherClient.newCall(request).execute()\n    }.also { expected ->\n      when (expected) {\n        is SSLException, is TlsFatalAlert -> {}\n\n        else -> {\n          throw expected\n        }\n      }\n    }\n  }\n\n  @Test\n  fun connectionsAreNotReusedIfHostnameVerifierChanges() {\n    enableHttps()\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    val request = Request(server.url(\"/\"))\n    val response1 = client.newCall(request).execute()\n    response1.body.close()\n\n    // This client shares a connection pool but has a different SSL socket factory.\n    val anotherClient =\n      client\n        .newBuilder()\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val response2 = anotherClient.newCall(request).execute()\n    response2.body.close()\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  /**\n   * Regression test for an edge case where closing response body in the HTTP engine doesn't release\n   * the corresponding stream allocation. This test keeps those response bodies alive and reads\n   * them after the redirect has completed. This forces a connection to not be reused where it would\n   * be otherwise.\n   *\n   *\n   * This test leaks a response body by not closing it.\n   *\n   * https://github.com/square/okhttp/issues/2409\n   */\n  @Test\n  fun connectionsAreNotReusedIfNetworkInterceptorInterferes() {\n    val responsesNotClosed: MutableList<Response?> = ArrayList()\n    client =\n      client\n        .newBuilder()\n        // Since this test knowingly leaks a connection, avoid using the default shared connection\n        // pool, which should remain clean for subsequent tests.\n        .connectionPool(ConnectionPool())\n        .addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            val response =\n              chain!!.proceed(\n                chain.request(),\n              )\n            responsesNotClosed.add(response)\n            response\n              .newBuilder()\n              .body(\"unrelated response body!\".toResponseBody(null))\n              .build()\n          },\n        ).build()\n    server.enqueue(\n      MockResponse(\n        code = 301,\n        headers = headersOf(\"Location\", \"/b\"),\n        body = \"/a has moved!\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"/b is here\"),\n    )\n    val request = Request(server.url(\"/\"))\n    val call = client.newCall(request)\n    call.execute().use { response ->\n      assertThat(\n        response.body.string(),\n      ).isEqualTo(\"unrelated response body!\")\n    }\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // No connection reuse.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    for (response in responsesNotClosed) {\n      response!!.closeQuietly()\n    }\n  }\n\n  private fun enableHttps() {\n    enableHttpsAndAlpn(Protocol.HTTP_1_1)\n  }\n\n  private fun enableHttp2() {\n    platform.assumeHttp2Support()\n    enableHttpsAndAlpn(Protocol.HTTP_2, Protocol.HTTP_1_1)\n  }\n\n  private fun enableHttpsAndAlpn(vararg protocols: Protocol) {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .protocols(protocols.toList())\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.protocols = client.protocols\n  }\n\n  private fun assertConnectionReused(vararg requests: Request?) {\n    for (i in requests.indices) {\n      val response = client.newCall(requests[i]!!).execute()\n      response.body.string() // Discard the response body.\n      assertThat(server.takeRequest().exchangeIndex).isEqualTo(i)\n    }\n  }\n\n  private fun assertConnectionNotReused(vararg requests: Request?) {\n    for (request in requests) {\n      val response = client.newCall(request!!).execute()\n      response.body.string() // Discard the response body.\n      assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ConnectionSpecTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.containsExactlyInAnyOrder\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.util.concurrent.CopyOnWriteArraySet\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport kotlin.test.assertFailsWith\nimport okhttp3.internal.applyConnectionSpec\nimport okhttp3.internal.platform.Platform.Companion.isAndroid\nimport okhttp3.testing.PlatformRule\nimport okhttp3.testing.PlatformVersion.majorVersion\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass ConnectionSpecTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @Test\n  fun noTlsVersions() {\n    assertFailsWith<IllegalArgumentException> {\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .tlsVersions(*arrayOf<String>())\n        .build()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"At least one TLS version is required\")\n    }\n  }\n\n  @Test\n  fun noCipherSuites() {\n    assertFailsWith<IllegalArgumentException> {\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .cipherSuites(*arrayOf<CipherSuite>())\n        .build()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"At least one cipher suite is required\")\n    }\n  }\n\n  @Test\n  fun cleartextBuilder() {\n    val cleartextSpec = ConnectionSpec.Builder(false).build()\n    assertThat(cleartextSpec.isTls).isFalse()\n  }\n\n  @Test\n  fun tlsBuilder_explicitCiphers() {\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(true)\n        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)\n        .tlsVersions(TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(true)\n        .build()\n    assertThat(tlsSpec.cipherSuites!!.toList())\n      .containsExactly(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)\n    assertThat(tlsSpec.tlsVersions!!.toList())\n      .containsExactly(TlsVersion.TLS_1_2)\n    assertThat(tlsSpec.supportsTlsExtensions).isTrue()\n  }\n\n  @Test\n  fun tlsBuilder_defaultCiphers() {\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(true)\n        .tlsVersions(TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(true)\n        .build()\n    assertThat(tlsSpec.cipherSuites).isNull()\n    assertThat(tlsSpec.tlsVersions!!.toList())\n      .containsExactly(TlsVersion.TLS_1_2)\n    assertThat(tlsSpec.supportsTlsExtensions).isTrue()\n  }\n\n  @Test\n  fun tls_defaultCiphers_noFallbackIndicator() {\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(true)\n        .tlsVersions(TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(false)\n        .build()\n    val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket\n    socket.enabledCipherSuites =\n      arrayOf(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n      )\n    socket.enabledProtocols =\n      arrayOf(\n        TlsVersion.TLS_1_2.javaName,\n        TlsVersion.TLS_1_1.javaName,\n      )\n    assertThat(tlsSpec.isCompatible(socket)).isTrue()\n    applyConnectionSpec(tlsSpec, socket, isFallback = false)\n    assertThat(socket.enabledProtocols).containsExactly(\n      TlsVersion.TLS_1_2.javaName,\n    )\n    assertThat(socket.enabledCipherSuites.toList())\n      .containsExactlyInAnyOrder(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n      )\n  }\n\n  @Test\n  fun tls_defaultCiphers_withFallbackIndicator() {\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(true)\n        .tlsVersions(TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(false)\n        .build()\n    val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket\n    socket.enabledCipherSuites =\n      arrayOf(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n      )\n    socket.enabledProtocols =\n      arrayOf(\n        TlsVersion.TLS_1_2.javaName,\n        TlsVersion.TLS_1_1.javaName,\n      )\n    assertThat(tlsSpec.isCompatible(socket)).isTrue()\n    applyConnectionSpec(tlsSpec, socket, isFallback = true)\n    assertThat(socket.enabledProtocols).containsExactly(\n      TlsVersion.TLS_1_2.javaName,\n    )\n    val expectedCipherSuites: MutableList<String> = ArrayList()\n    expectedCipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName)\n    expectedCipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName)\n    if (listOf<String>(*socket.supportedCipherSuites).contains(\"TLS_FALLBACK_SCSV\")) {\n      expectedCipherSuites.add(\"TLS_FALLBACK_SCSV\")\n    }\n    assertThat(socket.enabledCipherSuites)\n      .containsExactly(*expectedCipherSuites.toTypedArray())\n  }\n\n  @Test\n  fun tls_explicitCiphers() {\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(true)\n        .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)\n        .tlsVersions(TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(false)\n        .build()\n    val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket\n    socket.enabledCipherSuites =\n      arrayOf(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n      )\n    socket.enabledProtocols =\n      arrayOf(\n        TlsVersion.TLS_1_2.javaName,\n        TlsVersion.TLS_1_1.javaName,\n      )\n    assertThat(tlsSpec.isCompatible(socket)).isTrue()\n    applyConnectionSpec(tlsSpec, socket, isFallback = true)\n    assertThat(socket.enabledProtocols).containsExactly(\n      TlsVersion.TLS_1_2.javaName,\n    )\n    val expectedCipherSuites: MutableList<String> = ArrayList()\n    expectedCipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName)\n    if (listOf<String>(*socket.supportedCipherSuites).contains(\"TLS_FALLBACK_SCSV\")) {\n      expectedCipherSuites.add(\"TLS_FALLBACK_SCSV\")\n    }\n    assertThat(socket.enabledCipherSuites)\n      .containsExactly(*expectedCipherSuites.toTypedArray())\n  }\n\n  @Test\n  fun tls_stringCiphersAndVersions() {\n    // Supporting arbitrary input strings allows users to enable suites and versions that are not\n    // yet known to the library, but are supported by the platform.\n    ConnectionSpec\n      .Builder(ConnectionSpec.MODERN_TLS)\n      .cipherSuites(\"MAGIC-CIPHER\")\n      .tlsVersions(\"TLS9k\")\n      .build()\n  }\n\n  @Test\n  fun tls_missingRequiredCipher() {\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(true)\n        .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)\n        .tlsVersions(TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(false)\n        .build()\n    val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket\n    socket.enabledProtocols =\n      arrayOf(\n        TlsVersion.TLS_1_2.javaName,\n        TlsVersion.TLS_1_1.javaName,\n      )\n    socket.enabledCipherSuites =\n      arrayOf(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n      )\n    assertThat(tlsSpec.isCompatible(socket)).isTrue()\n    socket.enabledCipherSuites =\n      arrayOf(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n      )\n    assertThat(tlsSpec.isCompatible(socket)).isFalse()\n  }\n\n  @Test\n  fun allEnabledCipherSuites() {\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .allEnabledCipherSuites()\n        .build()\n    assertThat(tlsSpec.cipherSuites).isNull()\n    val sslSocket = SSLSocketFactory.getDefault().createSocket() as SSLSocket\n    sslSocket.enabledCipherSuites =\n      arrayOf(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n      )\n    applyConnectionSpec(tlsSpec, sslSocket, false)\n    if (platform.isAndroid) {\n      // https://developer.android.com/reference/javax/net/ssl/SSLSocket\n      val sdkVersion = platform.androidSdkVersion()\n      if (sdkVersion != null && sdkVersion >= 29) {\n        assertThat(sslSocket.enabledCipherSuites)\n          .containsExactly(\n            CipherSuite.TLS_AES_128_GCM_SHA256.javaName,\n            CipherSuite.TLS_AES_256_GCM_SHA384.javaName,\n            CipherSuite.TLS_CHACHA20_POLY1305_SHA256.javaName,\n            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n          )\n      } else {\n        assertThat(sslSocket.enabledCipherSuites)\n          .containsExactly(\n            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n          )\n      }\n    } else {\n      assertThat(sslSocket.enabledCipherSuites)\n        .containsExactly(\n          CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n          CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,\n        )\n    }\n  }\n\n  @Test\n  fun allEnabledTlsVersions() {\n    platform.assumeNotConscrypt()\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .allEnabledTlsVersions()\n        .build()\n    assertThat(tlsSpec.tlsVersions).isNull()\n    val sslSocket = SSLSocketFactory.getDefault().createSocket() as SSLSocket\n    if (majorVersion > 11) {\n      sslSocket.enabledProtocols =\n        arrayOf(\n          TlsVersion.SSL_3_0.javaName,\n          TlsVersion.TLS_1_1.javaName,\n          TlsVersion.TLS_1_2.javaName,\n          TlsVersion.TLS_1_3.javaName,\n        )\n    } else {\n      sslSocket.enabledProtocols =\n        arrayOf(\n          TlsVersion.SSL_3_0.javaName,\n          TlsVersion.TLS_1_1.javaName,\n          TlsVersion.TLS_1_2.javaName,\n        )\n    }\n    applyConnectionSpec(tlsSpec, sslSocket, false)\n    if (isAndroid) {\n      val sdkVersion = platform.androidSdkVersion()\n      // https://developer.android.com/reference/javax/net/ssl/SSLSocket\n      if (sdkVersion != null && sdkVersion >= 29) {\n        assertThat(sslSocket.enabledProtocols)\n          .containsExactly(\n            TlsVersion.TLS_1_1.javaName,\n            TlsVersion.TLS_1_2.javaName,\n            TlsVersion.TLS_1_3.javaName,\n          )\n      } else if (sdkVersion != null && sdkVersion >= 26) {\n        assertThat(sslSocket.enabledProtocols)\n          .containsExactly(\n            TlsVersion.TLS_1_1.javaName,\n            TlsVersion.TLS_1_2.javaName,\n          )\n      } else {\n        assertThat(sslSocket.enabledProtocols)\n          .containsExactly(\n            TlsVersion.SSL_3_0.javaName,\n            TlsVersion.TLS_1_1.javaName,\n            TlsVersion.TLS_1_2.javaName,\n          )\n      }\n    } else {\n      if (majorVersion > 11) {\n        assertThat(sslSocket.enabledProtocols)\n          .containsExactly(\n            TlsVersion.SSL_3_0.javaName,\n            TlsVersion.TLS_1_1.javaName,\n            TlsVersion.TLS_1_2.javaName,\n            TlsVersion.TLS_1_3.javaName,\n          )\n      } else {\n        assertThat(sslSocket.enabledProtocols)\n          .containsExactly(\n            TlsVersion.SSL_3_0.javaName,\n            TlsVersion.TLS_1_1.javaName,\n            TlsVersion.TLS_1_2.javaName,\n          )\n      }\n    }\n  }\n\n  @Test\n  fun tls_missingTlsVersion() {\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n    val tlsSpec =\n      ConnectionSpec\n        .Builder(true)\n        .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)\n        .tlsVersions(TlsVersion.TLS_1_2)\n        .supportsTlsExtensions(false)\n        .build()\n    val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket\n    socket.enabledCipherSuites =\n      arrayOf(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,\n      )\n    socket.enabledProtocols =\n      arrayOf(\n        TlsVersion.TLS_1_2.javaName,\n        TlsVersion.TLS_1_1.javaName,\n      )\n    assertThat(tlsSpec.isCompatible(socket)).isTrue()\n    socket.enabledProtocols = arrayOf(TlsVersion.TLS_1_1.javaName)\n    assertThat(tlsSpec.isCompatible(socket)).isFalse()\n  }\n\n  @Test\n  fun equalsAndHashCode() {\n    val allCipherSuites =\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .allEnabledCipherSuites()\n        .build()\n    val allTlsVersions =\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .allEnabledTlsVersions()\n        .build()\n    val set: MutableSet<Any> = CopyOnWriteArraySet()\n    assertThat(set.add(ConnectionSpec.MODERN_TLS)).isTrue()\n    assertThat(set.add(ConnectionSpec.COMPATIBLE_TLS)).isTrue()\n    assertThat(set.add(ConnectionSpec.CLEARTEXT)).isTrue()\n    assertThat(set.add(allTlsVersions)).isTrue()\n    assertThat(set.add(allCipherSuites)).isTrue()\n    allCipherSuites.hashCode()\n    assertThat(allCipherSuites.equals(null)).isFalse()\n    assertThat(set.remove(ConnectionSpec.MODERN_TLS)).isTrue()\n    assertThat(set.remove(ConnectionSpec.COMPATIBLE_TLS))\n      .isTrue()\n    assertThat(set.remove(ConnectionSpec.CLEARTEXT)).isTrue()\n    assertThat(set.remove(allTlsVersions)).isTrue()\n    assertThat(set.remove(allCipherSuites)).isTrue()\n    assertThat(set).isEmpty()\n    allTlsVersions.hashCode()\n    assertThat(allTlsVersions.equals(null)).isFalse()\n  }\n\n  @Test\n  fun allEnabledToString() {\n    val connectionSpec =\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .allEnabledTlsVersions()\n        .allEnabledCipherSuites()\n        .build()\n    assertThat(connectionSpec.toString()).isEqualTo(\n      \"ConnectionSpec(cipherSuites=[all enabled], tlsVersions=[all enabled], \" +\n        \"supportsTlsExtensions=true)\",\n    )\n  }\n\n  @Test\n  fun simpleToString() {\n    val connectionSpec =\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .tlsVersions(TlsVersion.TLS_1_2)\n        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)\n        .build()\n    assertThat(connectionSpec.toString()).isEqualTo(\n      \"ConnectionSpec(cipherSuites=[SSL_RSA_WITH_RC4_128_MD5], tlsVersions=[TLS_1_2], \" +\n        \"supportsTlsExtensions=true)\",\n    )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ConscryptTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isTrue\nimport okhttp3.TestUtil.assumeNetwork\nimport okhttp3.internal.platform.ConscryptPlatform\nimport okhttp3.internal.platform.Platform\nimport okhttp3.testing.PlatformRule\nimport org.conscrypt.Conscrypt\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass ConscryptTest {\n  @JvmField @RegisterExtension\n  val platform = PlatformRule()\n\n  @JvmField @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private val client = clientTestRule.newClient()\n\n  @BeforeEach fun setUp() {\n    platform.assumeConscrypt()\n  }\n\n  @Test\n  fun testTrustManager() {\n    assertThat(Conscrypt.isConscrypt(Platform.get().platformTrustManager())).isTrue()\n  }\n\n  @Test\n  @Disabled\n  fun testMozilla() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://mozilla.org/robots.txt\").build()\n\n    client.newCall(request).execute().use {\n      assertThat(it.protocol).isEqualTo(Protocol.HTTP_2)\n      assertThat(it.handshake!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n    }\n  }\n\n  @Test\n  @Disabled\n  fun testGoogle() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://google.com/robots.txt\").build()\n\n    client.newCall(request).execute().use {\n      assertThat(it.protocol).isEqualTo(Protocol.HTTP_2)\n      if (it.handshake!!.tlsVersion != TlsVersion.TLS_1_3) {\n        System.err.println(\"Flaky TLSv1.3 with google\")\n//    assertThat(it.handshake()!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n      }\n    }\n  }\n\n  @Test\n  fun testBuildIfSupported() {\n    val actual = ConscryptPlatform.buildIfSupported()\n    assertThat(actual).isNotNull()\n  }\n\n  @Test\n  fun testVersion() {\n    val version = Conscrypt.version()\n\n    assertTrue(ConscryptPlatform.atLeastVersion(1, 4, 9))\n    assertTrue(ConscryptPlatform.atLeastVersion(version.major()))\n    assertTrue(ConscryptPlatform.atLeastVersion(version.major(), version.minor()))\n    assertTrue(ConscryptPlatform.atLeastVersion(version.major(), version.minor(), version.patch()))\n    assertFalse(ConscryptPlatform.atLeastVersion(version.major(), version.minor(), version.patch() + 1))\n    assertFalse(ConscryptPlatform.atLeastVersion(version.major(), version.minor() + 1))\n    assertFalse(ConscryptPlatform.atLeastVersion(version.major() + 1))\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CookieTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport app.cash.burst.Burst\nimport app.cash.burst.burstValues\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNotEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.text.ParseException\nimport java.text.SimpleDateFormat\nimport java.util.Arrays\nimport java.util.Date\nimport kotlin.test.assertFailsWith\nimport okhttp3.Cookie.Companion.parse\nimport okhttp3.Cookie.Companion.parseAll\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.internal.UTC\nimport okhttp3.internal.http.MAX_DATE\nimport okhttp3.internal.parseCookie\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\n\n@Burst\nclass CookieTest {\n  val url = \"https://example.com/\".toHttpUrl()\n\n  @Test fun simpleCookie() {\n    val cookie = parse(url, \"SID=31d4d96e407aad42\")\n    assertThat(cookie.toString()).isEqualTo(\"SID=31d4d96e407aad42; path=/\")\n  }\n\n  @Test fun noEqualsSign() {\n    assertThat(parse(url, \"foo\")).isNull()\n    assertThat(parse(url, \"foo; Path=/\")).isNull()\n  }\n\n  @Test fun emptyName() {\n    assertThat(parse(url, \"=b\")).isNull()\n    assertThat(parse(url, \" =b\")).isNull()\n    assertThat(parse(url, \"\\r\\t \\n=b\")).isNull()\n  }\n\n  @Test fun spaceInName() {\n    assertThat(parse(url, \"a b=cd\")!!.name).isEqualTo(\"a b\")\n  }\n\n  @Test fun spaceInValue() {\n    assertThat(parse(url, \"ab=c d\")!!.value).isEqualTo(\"c d\")\n  }\n\n  @Test fun trimLeadingAndTrailingWhitespaceFromName() {\n    assertThat(parse(url, \" a=b\")!!.name).isEqualTo(\"a\")\n    assertThat(parse(url, \"a =b\")!!.name).isEqualTo(\"a\")\n    assertThat(parse(url, \"\\r\\t \\na\\n\\t \\n=b\")!!.name).isEqualTo(\"a\")\n  }\n\n  @Test fun emptyValue() {\n    assertThat(parse(url, \"a=\")!!.value).isEqualTo(\"\")\n    assertThat(parse(url, \"a= \")!!.value).isEqualTo(\"\")\n    assertThat(parse(url, \"a=\\r\\t \\n\")!!.value).isEqualTo(\"\")\n  }\n\n  @Test fun trimLeadingAndTrailingWhitespaceFromValue() {\n    assertThat(parse(url, \"a= \")!!.value).isEqualTo(\"\")\n    assertThat(parse(url, \"a= b\")!!.value).isEqualTo(\"b\")\n    assertThat(parse(url, \"a=b \")!!.value).isEqualTo(\"b\")\n    assertThat(parse(url, \"a=\\r\\t \\nb\\n\\t \\n\")!!.value).isEqualTo(\"b\")\n  }\n\n  @Test fun invalidCharacters() {\n    assertThat(parse(url, \"a\\u0000b=cd\")).isNull()\n    assertThat(parse(url, \"ab=c\\u0000d\")).isNull()\n    assertThat(parse(url, \"a\\u0001b=cd\")).isNull()\n    assertThat(parse(url, \"ab=c\\u0001d\")).isNull()\n    assertThat(parse(url, \"a\\u0009b=cd\")).isNull()\n    assertThat(parse(url, \"ab=c\\u0009d\")).isNull()\n    assertThat(parse(url, \"a\\u001fb=cd\")).isNull()\n    assertThat(parse(url, \"ab=c\\u001fd\")).isNull()\n    assertThat(parse(url, \"a\\u007fb=cd\")).isNull()\n    assertThat(parse(url, \"ab=c\\u007fd\")).isNull()\n    assertThat(parse(url, \"a\\u0080b=cd\")).isNull()\n    assertThat(parse(url, \"ab=c\\u0080d\")).isNull()\n    assertThat(parse(url, \"a\\u00ffb=cd\")).isNull()\n    assertThat(parse(url, \"ab=c\\u00ffd\")).isNull()\n  }\n\n  @Test fun maxAge() {\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=1\")!!.expiresAt).isEqualTo(51000L)\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=9223372036854724\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=9223372036854725\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=9223372036854726\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parseCookie(9223372036854773807L, url, \"a=b; Max-Age=1\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parseCookie(9223372036854773807L, url, \"a=b; Max-Age=2\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parseCookie(9223372036854773807L, url, \"a=b; Max-Age=3\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=10000000000000000000\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n  }\n\n  @Test fun maxAgeNonPositive() {\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=-1\")!!.expiresAt)\n      .isEqualTo(Long.MIN_VALUE)\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=0\")!!.expiresAt)\n      .isEqualTo(Long.MIN_VALUE)\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=-9223372036854775808\")!!.expiresAt)\n      .isEqualTo(Long.MIN_VALUE)\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=-9223372036854775809\")!!.expiresAt)\n      .isEqualTo(Long.MIN_VALUE)\n    assertThat(parseCookie(50000L, url, \"a=b; Max-Age=-10000000000000000000\")!!.expiresAt)\n      .isEqualTo(Long.MIN_VALUE)\n  }\n\n  @Test fun domainAndPath() {\n    val cookie = parse(url, \"SID=31d4d96e407aad42; Path=/; Domain=example.com\")\n    assertThat(cookie!!.domain).isEqualTo(\"example.com\")\n    assertThat(cookie.path).isEqualTo(\"/\")\n    assertThat(cookie.hostOnly).isFalse()\n    assertThat(cookie.toString()).isEqualTo(\"SID=31d4d96e407aad42; domain=example.com; path=/\")\n  }\n\n  @Test fun secureAndHttpOnly() {\n    val cookie = parse(url, \"SID=31d4d96e407aad42; Path=/; Secure; HttpOnly\")\n    assertThat(cookie!!.secure).isTrue()\n    assertThat(cookie.httpOnly).isTrue()\n    assertThat(cookie.toString()).isEqualTo(\"SID=31d4d96e407aad42; path=/; secure; httponly\")\n  }\n\n  @Test fun expiresDate() {\n    assertThat(Date(parse(url, \"a=b; Expires=Thu, 01 Jan 1970 00:00:00 GMT\")!!.expiresAt))\n      .isEqualTo(date(\"1970-01-01T00:00:00.000+0000\"))\n    assertThat(Date(parse(url, \"a=b; Expires=Wed, 09 Jun 2021 10:18:14 GMT\")!!.expiresAt))\n      .isEqualTo(date(\"2021-06-09T10:18:14.000+0000\"))\n    assertThat(Date(parse(url, \"a=b; Expires=Sun, 06 Nov 1994 08:49:37 GMT\")!!.expiresAt))\n      .isEqualTo(date(\"1994-11-06T08:49:37.000+0000\"))\n  }\n\n  @Test fun awkwardDates() {\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 70 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 January 1970 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Janucember 1970 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=Thu, 1 Jan 1970 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 1970 0:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 1970 00:0:00 GMT\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 1970 00:00:0 GMT\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=00:00:00 Thu, 01 Jan 1970 GMT\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=00:00:00 1970 Jan 01\")!!.expiresAt)\n      .isEqualTo(0L)\n    assertThat(parse(url, \"a=b; Expires=00:00:00 1970 Jan 1\")!!.expiresAt)\n      .isEqualTo(0L)\n  }\n\n  @Test fun invalidYear() {\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 1600 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 19999 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n  }\n\n  @Test fun invalidMonth() {\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Foo 1970 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Foocember 1970 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 1970 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n  }\n\n  @Test fun invalidDayOfMonth() {\n    assertThat(parse(url, \"a=b; Expires=Thu, 32 Jan 1970 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n    assertThat(parse(url, \"a=b; Expires=Thu, Jan 1970 00:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n  }\n\n  @Test fun invalidHour() {\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 1970 24:00:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n  }\n\n  @Test fun invalidMinute() {\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 1970 00:60:00 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n  }\n\n  @Test fun invalidSecond() {\n    assertThat(parse(url, \"a=b; Expires=Thu, 01 Jan 1970 00:00:60 GMT\")!!.expiresAt)\n      .isEqualTo(MAX_DATE)\n  }\n\n  @Test fun domainMatches() {\n    val cookie = parse(url, \"a=b; domain=example.com\")\n    assertThat(cookie!!.matches(\"http://example.com\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://www.example.com\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://square.com\".toHttpUrl())).isFalse()\n  }\n\n  /** If no domain is present, match only the origin domain.  */\n  @Test fun domainMatchesNoDomain() {\n    val cookie = parse(url, \"a=b\")\n    assertThat(cookie!!.matches(\"http://example.com\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://www.example.com\".toHttpUrl())).isFalse()\n    assertThat(cookie.matches(\"http://square.com\".toHttpUrl())).isFalse()\n  }\n\n  /** Ignore an optional leading `.` in the domain.  */\n  @Test fun domainMatchesIgnoresLeadingDot() {\n    val cookie = parse(url, \"a=b; domain=.example.com\")\n    assertThat(cookie!!.matches(\"http://example.com\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://www.example.com\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://square.com\".toHttpUrl())).isFalse()\n  }\n\n  /** Ignore the entire attribute if the domain ends with `.`.  */\n  @Test fun domainIgnoredWithTrailingDot() {\n    val cookie = parse(url, \"a=b; domain=example.com.\")\n    assertThat(cookie!!.matches(\"http://example.com\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://www.example.com\".toHttpUrl())).isFalse()\n    assertThat(cookie.matches(\"http://square.com\".toHttpUrl())).isFalse()\n  }\n\n  @Test fun idnDomainMatches() {\n    val cookie = parse(\"http://☃.net/\".toHttpUrl(), \"a=b; domain=☃.net\")\n    assertThat(cookie!!.matches(\"http://☃.net/\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://xn--n3h.net/\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://www.☃.net/\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://www.xn--n3h.net/\".toHttpUrl())).isTrue()\n  }\n\n  @Test fun punycodeDomainMatches() {\n    val cookie = parse(\"http://xn--n3h.net/\".toHttpUrl(), \"a=b; domain=xn--n3h.net\")\n    assertThat(cookie!!.matches(\"http://☃.net/\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://xn--n3h.net/\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://www.☃.net/\".toHttpUrl())).isTrue()\n    assertThat(cookie.matches(\"http://www.xn--n3h.net/\".toHttpUrl())).isTrue()\n  }\n\n  @Test fun domainMatchesIpAddress() {\n    val urlWithIp = \"http://123.45.234.56/\".toHttpUrl()\n    assertThat(parse(urlWithIp, \"a=b; domain=234.56\")).isNull()\n    assertThat(parse(urlWithIp, \"a=b; domain=123.45.234.56\")!!.domain).isEqualTo(\"123.45.234.56\")\n  }\n\n  @Test fun domainMatchesIpv6Address() {\n    val cookie = parse(\"http://[::1]/\".toHttpUrl(), \"a=b; domain=::1\")\n    assertThat(cookie!!.domain).isEqualTo(\"::1\")\n    assertThat(cookie.matches(\"http://[::1]/\".toHttpUrl())).isTrue()\n  }\n\n  @Test fun domainMatchesIpv6AddressWithCompression() {\n    val cookie = parse(\"http://[0001:0000::]/\".toHttpUrl(), \"a=b; domain=0001:0000::\")\n    assertThat(cookie!!.domain).isEqualTo(\"1::\")\n    assertThat(cookie.matches(\"http://[1::]/\".toHttpUrl())).isTrue()\n  }\n\n  @Test fun domainMatchesIpv6AddressWithIpv4Suffix() {\n    val cookie =\n      parse(\n        \"http://[::1:ffff:ffff]/\".toHttpUrl(),\n        \"a=b; domain=::1:255.255.255.255\",\n      )\n    assertThat(cookie!!.domain).isEqualTo(\"::1:ffff:ffff\")\n    assertThat(cookie.matches(\"http://[::1:ffff:ffff]/\".toHttpUrl())).isTrue()\n  }\n\n  @Test fun ipv6AddressDoesntMatch() {\n    val cookie = parse(\"http://[::1]/\".toHttpUrl(), \"a=b; domain=::2\")\n    assertThat(cookie).isNull()\n  }\n\n  @Test fun ipv6AddressMalformed() {\n    val cookie = parse(\"http://[::1]/\".toHttpUrl(), \"a=b; domain=::2::2\")\n    assertThat(cookie!!.domain).isEqualTo(\"::1\")\n  }\n\n  /**\n   * These public suffixes were selected by inspecting the publicsuffix.org list. It's possible they\n   * may change in the future. If this test begins to fail, please double check they are still\n   * present in the public suffix list.\n   */\n  @Test fun domainIsPublicSuffix() {\n    val ascii = \"https://foo1.foo.bar.elb.amazonaws.com\".toHttpUrl()\n    assertThat(parse(ascii, \"a=b; domain=foo.bar.elb.amazonaws.com\")).isNotNull()\n    assertThat(parse(ascii, \"a=b; domain=bar.elb.amazonaws.com\")).isNull()\n    assertThat(parse(ascii, \"a=b; domain=com\")).isNull()\n    val unicode = \"https://長.長.長崎.jp\".toHttpUrl()\n    assertThat(parse(unicode, \"a=b; domain=長.長崎.jp\")).isNotNull()\n    assertThat(parse(unicode, \"a=b; domain=長崎.jp\")).isNull()\n    val punycode = \"https://xn--ue5a.xn--ue5a.xn--8ltr62k.jp\".toHttpUrl()\n    assertThat(parse(punycode, \"a=b; domain=xn--ue5a.xn--8ltr62k.jp\")).isNotNull()\n    assertThat(parse(punycode, \"a=b; domain=xn--8ltr62k.jp\")).isNull()\n  }\n\n  @Test fun hostOnly() {\n    assertThat(parse(url, \"a=b\")!!.hostOnly).isTrue()\n    assertThat(\n      parse(url, \"a=b; domain=example.com\")!!.hostOnly,\n    ).isFalse()\n  }\n\n  @Test fun defaultPath() {\n    assertThat(parse(\"http://example.com/foo/bar\".toHttpUrl(), \"a=b\")!!.path).isEqualTo(\"/foo\")\n    assertThat(parse(\"http://example.com/foo/\".toHttpUrl(), \"a=b\")!!.path).isEqualTo(\"/foo\")\n    assertThat(parse(\"http://example.com/foo\".toHttpUrl(), \"a=b\")!!.path).isEqualTo(\"/\")\n    assertThat(parse(\"http://example.com/\".toHttpUrl(), \"a=b\")!!.path).isEqualTo(\"/\")\n  }\n\n  @Test fun defaultPathIsUsedIfPathDoesntHaveLeadingSlash() {\n    assertThat(\n      parse(\"http://example.com/foo/bar\".toHttpUrl(), \"a=b; path=quux\")!!.path,\n    ).isEqualTo(\"/foo\")\n    assertThat(parse(\"http://example.com/foo/bar\".toHttpUrl(), \"a=b; path=\")!!.path)\n      .isEqualTo(\"/foo\")\n  }\n\n  @Test fun pathAttributeDoesntNeedToMatch() {\n    assertThat(parse(\"http://example.com/\".toHttpUrl(), \"a=b; path=/quux\")!!.path)\n      .isEqualTo(\"/quux\")\n    assertThat(parse(\"http://example.com/foo/bar\".toHttpUrl(), \"a=b; path=/quux\")!!.path)\n      .isEqualTo(\"/quux\")\n  }\n\n  @Test fun httpOnly() {\n    assertThat(parse(url, \"a=b\")!!.httpOnly).isFalse()\n    assertThat(parse(url, \"a=b; HttpOnly\")!!.httpOnly).isTrue()\n  }\n\n  @Test fun secure() {\n    assertThat(parse(url, \"a=b\")!!.secure).isFalse()\n    assertThat(parse(url, \"a=b; Secure\")!!.secure).isTrue()\n  }\n\n  @Test fun maxAgeTakesPrecedenceOverExpires() {\n    // Max-Age = 1, Expires = 2. In either order.\n    assertThat(parseCookie(0L, url, \"a=b; Max-Age=1; Expires=Thu, 01 Jan 1970 00:00:02 GMT\")!!.expiresAt)\n      .isEqualTo(1000L)\n    assertThat(parseCookie(0L, url, \"a=b; Expires=Thu, 01 Jan 1970 00:00:02 GMT; Max-Age=1\")!!.expiresAt)\n      .isEqualTo(1000L)\n    // Max-Age = 2, Expires = 1. In either order.\n    assertThat(parseCookie(0L, url, \"a=b; Max-Age=2; Expires=Thu, 01 Jan 1970 00:00:01 GMT\")!!.expiresAt)\n      .isEqualTo(2000L)\n    assertThat(parseCookie(0L, url, \"a=b; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=2\")!!.expiresAt)\n      .isEqualTo(2000L)\n  }\n\n  /** If a cookie incorrectly defines multiple 'Max-Age' attributes, the last one defined wins.  */\n  @Test fun lastMaxAgeWins() {\n    assertThat(parseCookie(0L, url, \"a=b; Max-Age=2; Max-Age=4; Max-Age=1; Max-Age=3\")!!.expiresAt)\n      .isEqualTo(3000L)\n  }\n\n  /** If a cookie incorrectly defines multiple 'Expires' attributes, the last one defined wins.  */\n  @Test fun lastExpiresAtWins() {\n    assertThat(\n      parseCookie(\n        0L,\n        url,\n        \"a=b; \" +\n          \"Expires=Thu, 01 Jan 1970 00:00:02 GMT; \" +\n          \"Expires=Thu, 01 Jan 1970 00:00:04 GMT; \" +\n          \"Expires=Thu, 01 Jan 1970 00:00:01 GMT; \" +\n          \"Expires=Thu, 01 Jan 1970 00:00:03 GMT\",\n      )!!.expiresAt,\n    ).isEqualTo(3000L)\n  }\n\n  @Test fun maxAgeOrExpiresMakesCookiePersistent() {\n    assertThat(parseCookie(0L, url, \"a=b\")!!.persistent).isFalse()\n    assertThat(parseCookie(0L, url, \"a=b; Max-Age=1\")!!.persistent).isTrue()\n    assertThat(parseCookie(0L, url, \"a=b; Expires=Thu, 01 Jan 1970 00:00:01 GMT\")!!.persistent)\n      .isTrue()\n  }\n\n  @Test fun parseAll() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"Set-Cookie: a=b\")\n        .add(\"Set-Cookie: c=d\")\n        .build()\n    val cookies = parseAll(url, headers)\n    assertThat(cookies.size).isEqualTo(2)\n    assertThat(cookies[0].toString()).isEqualTo(\"a=b; path=/\")\n    assertThat(cookies[1].toString()).isEqualTo(\"c=d; path=/\")\n  }\n\n  @Test fun builder() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .domain(\"example.com\")\n        .build()\n    assertThat(cookie.name).isEqualTo(\"a\")\n    assertThat(cookie.value).isEqualTo(\"b\")\n    assertThat(cookie.expiresAt).isEqualTo(MAX_DATE)\n    assertThat(cookie.domain).isEqualTo(\"example.com\")\n    assertThat(cookie.path).isEqualTo(\"/\")\n    assertThat(cookie.secure).isFalse()\n    assertThat(cookie.httpOnly).isFalse()\n    assertThat(cookie.persistent).isFalse()\n    assertThat(cookie.hostOnly).isFalse()\n    assertThat(cookie.sameSite).isNull()\n  }\n\n  @Test fun newBuilder() {\n    val cookie =\n      parseCookie(0L, url, \"c=d; Max-Age=1\")!!\n        .newBuilder()\n        .name(\"a\")\n        .value(\"b\")\n        .domain(\"example.com\")\n        .expiresAt(MAX_DATE)\n        .build()\n    assertThat(cookie.name).isEqualTo(\"a\")\n    assertThat(cookie.value).isEqualTo(\"b\")\n    assertThat(cookie.expiresAt).isEqualTo(MAX_DATE)\n    assertThat(cookie.domain).isEqualTo(\"example.com\")\n    assertThat(cookie.path).isEqualTo(\"/\")\n    assertThat(cookie.secure).isFalse()\n    assertThat(cookie.httpOnly).isFalse()\n    // can't be unset\n    assertThat(cookie.persistent).isTrue()\n    assertThat(cookie.hostOnly).isFalse()\n  }\n\n  @Test fun builderNameValidation() {\n    assertFailsWith<IllegalArgumentException> {\n      Cookie.Builder().name(\" a \")\n    }\n  }\n\n  @Test fun builderValueValidation() {\n    assertFailsWith<IllegalArgumentException> {\n      Cookie.Builder().value(\" b \")\n    }\n  }\n\n  @Test fun builderClampsMaxDate() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .hostOnlyDomain(\"example.com\")\n        .expiresAt(Long.MAX_VALUE)\n        .build()\n    assertThat(cookie.toString()).isEqualTo(\"a=b; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/\")\n  }\n\n  @Test fun builderExpiresAt() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .hostOnlyDomain(\"example.com\")\n        .expiresAt(date(\"1970-01-01T00:00:01.000+0000\").time)\n        .build()\n    assertThat(cookie.toString()).isEqualTo(\"a=b; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/\")\n  }\n\n  @Test fun builderClampsMinDate() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .hostOnlyDomain(\"example.com\")\n        .expiresAt(date(\"1970-01-01T00:00:00.000+0000\").time)\n        .build()\n    assertThat(cookie.toString()).isEqualTo(\"a=b; max-age=0; path=/\")\n  }\n\n  @Test fun builderDomainValidation() {\n    assertFailsWith<IllegalArgumentException> {\n      Cookie.Builder().hostOnlyDomain(\"a/b\")\n    }\n  }\n\n  @Test fun builderDomain() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .hostOnlyDomain(\"squareup.com\")\n        .build()\n    assertThat(cookie.domain).isEqualTo(\"squareup.com\")\n    assertThat(cookie.hostOnly).isTrue()\n  }\n\n  @Test fun builderPath() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .hostOnlyDomain(\"example.com\")\n        .path(\"/foo\")\n        .build()\n    assertThat(cookie.path).isEqualTo(\"/foo\")\n  }\n\n  @Test fun builderPathValidation() {\n    assertFailsWith<IllegalArgumentException> {\n      Cookie.Builder().path(\"foo\")\n    }\n  }\n\n  @Test fun builderSecure() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .hostOnlyDomain(\"example.com\")\n        .secure()\n        .build()\n    assertThat(cookie.secure).isTrue()\n  }\n\n  @Test fun builderHttpOnly() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .hostOnlyDomain(\"example.com\")\n        .httpOnly()\n        .build()\n    assertThat(cookie.httpOnly).isTrue()\n  }\n\n  @Test fun builderIpv6() {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .domain(\"0:0:0:0:0:0:0:1\")\n        .build()\n    assertThat(cookie.domain).isEqualTo(\"::1\")\n  }\n\n  @Test fun emptySameSite() {\n    assertThat(parse(url, \"a=b; SameSite=\")!!.sameSite).isEqualTo(\"\")\n    assertThat(parse(url, \"a=b; SameSite= \")!!.sameSite).isEqualTo(\"\")\n    assertThat(parse(url, \"a=b; SameSite=\\r\\t \\n\")!!.sameSite).isEqualTo(\"\")\n  }\n\n  @Test fun spaceInSameSite() {\n    assertThat(parse(url, \"a=b; SameSite=a b\")!!.sameSite).isEqualTo(\"a b\")\n  }\n\n  @Test fun trimLeadingAndTrailingWhitespaceFromSameSite() {\n    assertThat(parse(url, \"a=b; SameSite= \")!!.sameSite).isEqualTo(\"\")\n    assertThat(parse(url, \"a= b; SameSite= Lax\")!!.sameSite).isEqualTo(\"Lax\")\n    assertThat(parse(url, \"a=b ; SameSite=Lax ;\")!!.sameSite).isEqualTo(\"Lax\")\n    assertThat(parse(url, \"a=\\r\\t \\nb\\n; \\rSameSite=\\n \\tLax\")!!.sameSite).isEqualTo(\"Lax\")\n  }\n\n  @Test fun builderSameSiteTrimmed() {\n    var cookieBuilder =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .domain(\"example.com\")\n\n    assertThrows<IllegalArgumentException> {\n      cookieBuilder.sameSite(\" a\").build()\n    }\n    assertThrows<IllegalArgumentException> {\n      cookieBuilder.sameSite(\"a \").build()\n    }\n    assertThrows<IllegalArgumentException> {\n      cookieBuilder.sameSite(\" a \").build()\n    }\n\n    cookieBuilder.sameSite(\"a\").build()\n  }\n\n  @Test\n  fun builderSameSite(sameSite: String = burstValues(\"Lax\", \"Strict\", \"UnrecognizedButValid\")) {\n    val cookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .domain(\"example.com\")\n        .sameSite(sameSite)\n        .build()\n    assertThat(cookie.sameSite).isEqualTo(sameSite)\n  }\n\n  /** Note that we permit building a cookie that doesn’t follow the rules. */\n  @Test fun builderSameSiteNoneDoesNotRequireSecure() {\n    val cookieBuilder =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .domain(\"example.com\")\n        .sameSite(\"None\")\n\n    val cookie = cookieBuilder.build()\n    assertThat(cookie.sameSite).isEqualTo(\"None\")\n  }\n\n  @Test fun equalsAndHashCode() {\n    val cookieStrings =\n      Arrays.asList(\n        \"a=b; Path=/c; Domain=example.com; Max-Age=5; Secure; HttpOnly\",\n        \"a= ; Path=/c; Domain=example.com; Max-Age=5; Secure; HttpOnly\",\n        \"a=b;          Domain=example.com; Max-Age=5; Secure; HttpOnly\",\n        \"a=b; Path=/c;                     Max-Age=5; Secure; HttpOnly\",\n        \"a=b; Path=/c; Domain=example.com;            Secure; HttpOnly\",\n        \"a=b; Path=/c; Domain=example.com; Max-Age=5;         HttpOnly\",\n        \"a=b; Path=/c; Domain=example.com; Max-Age=5; Secure;         \",\n        \"a=b; Path=/c; Domain=example.com; Max-Age=5; Secure; HttpOnly; SameSite=Lax\",\n        \"a= ; Path=/c; Domain=example.com; Max-Age=5; Secure; HttpOnly; SameSite=Lax\",\n        \"a=b;          Domain=example.com; Max-Age=5; Secure; HttpOnly; SameSite=Lax\",\n        \"a=b; Path=/c;                     Max-Age=5; Secure; HttpOnly; SameSite=Lax\",\n        \"a=b; Path=/c; Domain=example.com;            Secure; HttpOnly; SameSite=Lax\",\n        \"a=b; Path=/c; Domain=example.com; Max-Age=5;         HttpOnly; SameSite=Lax\",\n        \"a=b; Path=/c; Domain=example.com; Max-Age=5; Secure;         ; SameSite=Lax\",\n      )\n    for (stringA in cookieStrings) {\n      val cookieA = parseCookie(0, url, stringA!!)\n      for (stringB in cookieStrings) {\n        val cookieB = parseCookie(0, url, stringB!!)\n        if (stringA == stringB) {\n          assertThat(cookieB.hashCode()).isEqualTo(cookieA.hashCode())\n          assertThat(cookieB).isEqualTo(cookieA)\n        } else {\n          assertThat(cookieB.hashCode()).isNotEqualTo(cookieA.hashCode().toLong())\n          assertThat(cookieB).isNotEqualTo(cookieA)\n        }\n      }\n      assertThat(cookieA).isNotEqualTo(null)\n    }\n  }\n\n  @Throws(ParseException::class)\n  private fun date(s: String): Date {\n    val format = SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSSZ\")\n    format.timeZone = UTC\n    return format.parse(s)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CookiesTest.kt",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isGreaterThan\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport assertk.fail\nimport java.net.CookieHandler\nimport java.net.CookieManager\nimport java.net.CookiePolicy\nimport java.net.HttpCookie\nimport java.net.HttpURLConnection\nimport java.net.InetAddress\nimport java.net.URI\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Cookie.Companion.parse\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.java.net.cookiejar.JavaNetCookieJar\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n/** Derived from Android's CookiesTest.  */\n@Timeout(30)\nclass CookiesTest {\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private var client = clientTestRule.newClient()\n\n  @Test\n  fun testNetscapeResponse() {\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n    val urlWithIpAddress = urlWithIpAddress(server, \"/path/foo\")\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\n          \"Set-Cookie: a=android; \" +\n            \"expires=Fri, 31-Dec-9999 23:59:59 GMT; \" +\n            \"path=/path; \" +\n            \"domain=${urlWithIpAddress.host}; \" +\n            \"secure\",\n        ).build(),\n    )\n    get(urlWithIpAddress)\n    val cookies = cookieManager.cookieStore.cookies\n    assertThat(cookies.size).isEqualTo(1)\n    val cookie = cookies[0]\n    assertThat(cookie.name).isEqualTo(\"a\")\n    assertThat(cookie.value).isEqualTo(\"android\")\n    assertThat(cookie.comment).isNull()\n    assertThat(cookie.commentURL).isNull()\n    assertThat(cookie.discard).isFalse()\n    assertThat(cookie.maxAge).isGreaterThan(100000000000L)\n    assertThat(cookie.path).isEqualTo(\"/path\")\n    assertThat(cookie.secure).isTrue()\n    assertThat(cookie.version).isEqualTo(0)\n  }\n\n  @Test\n  fun testRfc2109Response() {\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n    val urlWithIpAddress = urlWithIpAddress(server, \"/path/foo\")\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\n          \"Set-Cookie: a=android; \" +\n            \"Comment=this cookie is delicious; \" +\n            \"Domain=${urlWithIpAddress.host}; \" +\n            \"Max-Age=60; \" +\n            \"Path=/path; \" +\n            \"Secure; \" +\n            \"Version=1\",\n        ).build(),\n    )\n    get(urlWithIpAddress)\n    val cookies = cookieManager.cookieStore.cookies\n    assertThat(cookies.size).isEqualTo(1)\n    val cookie = cookies[0]\n    assertThat(cookie.name).isEqualTo(\"a\")\n    assertThat(cookie.value).isEqualTo(\"android\")\n    assertThat(cookie.commentURL).isNull()\n    assertThat(cookie.discard).isFalse()\n    // Converting to a fixed date can cause rounding!\n    assertThat(cookie.maxAge.toDouble()).isCloseTo(60.0, 5.0)\n    assertThat(cookie.path).isEqualTo(\"/path\")\n    assertThat(cookie.secure).isTrue()\n  }\n\n  @Test\n  fun testQuotedAttributeValues() {\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n    val urlWithIpAddress = urlWithIpAddress(server, \"/path/foo\")\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\n          \"Set-Cookie: a=\\\"android\\\"; \" +\n            \"Comment=\\\"this cookie is delicious\\\"; \" +\n            \"CommentURL=\\\"http://google.com/\\\"; \" +\n            \"Discard; \" +\n            \"Domain=${urlWithIpAddress.host}; \" +\n            \"Max-Age=60; \" +\n            \"Path=\\\"/path\\\"; \" +\n            \"Port=\\\"80,443,${server.port}\\\"; \" +\n            \"Secure; \" +\n            \"Version=\\\"1\\\"\",\n        ).build(),\n    )\n    get(urlWithIpAddress)\n    val cookies = cookieManager.cookieStore.cookies\n    assertThat(cookies.size).isEqualTo(1)\n    val cookie = cookies[0]\n    assertThat(cookie.name).isEqualTo(\"a\")\n    assertThat(cookie.value).isEqualTo(\"android\")\n    // Converting to a fixed date can cause rounding!\n    assertThat(cookie.maxAge.toDouble()).isCloseTo(60.0, 1.0)\n    assertThat(cookie.path).isEqualTo(\"/path\")\n    assertThat(cookie.secure).isTrue()\n  }\n\n  @Test\n  fun testSendingCookiesFromStore() {\n    server.enqueue(MockResponse())\n    val serverUrl = urlWithIpAddress(server, \"/\")\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    val cookieA = HttpCookie(\"a\", \"android\")\n    cookieA.domain = serverUrl.host\n    cookieA.path = \"/\"\n    cookieManager.cookieStore.add(serverUrl.toUri(), cookieA)\n    val cookieB = HttpCookie(\"b\", \"banana\")\n    cookieB.domain = serverUrl.host\n    cookieB.path = \"/\"\n    cookieManager.cookieStore.add(serverUrl.toUri(), cookieB)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n    get(serverUrl)\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Cookie\"]).isEqualTo(\"a=android; b=banana\")\n  }\n\n  @Test\n  fun cookieHandlerLikeAndroid() {\n    server.enqueue(MockResponse())\n    val serverUrl = urlWithIpAddress(server, \"/\")\n    val androidCookieHandler: CookieHandler =\n      object : CookieHandler() {\n        override fun get(\n          uri: URI,\n          map: Map<String, List<String>>,\n        ) = mapOf(\n          \"Cookie\" to\n            listOf(\n              \"\\$Version=\\\"1\\\"; \" +\n                \"a=\\\"android\\\";\\$Path=\\\"/\\\";\\$Domain=\\\"${serverUrl.host}\\\"; \" +\n                \"b=\\\"banana\\\";\\$Path=\\\"/\\\";\\$Domain=\\\"${serverUrl.host}\\\"\",\n            ),\n        )\n\n        override fun put(\n          uri: URI,\n          map: Map<String, List<String>>,\n        ) {\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(androidCookieHandler))\n        .build()\n    get(serverUrl)\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Cookie\"]).isEqualTo(\"a=android; b=banana\")\n  }\n\n  @Test\n  fun receiveAndSendMultipleCookies() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Set-Cookie\", \"a=android\")\n        .addHeader(\"Set-Cookie\", \"b=banana\")\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n    get(urlWithIpAddress(server, \"/\"))\n    val request1 = server.takeRequest()\n    assertThat(request1.headers[\"Cookie\"]).isNull()\n    get(urlWithIpAddress(server, \"/\"))\n    val request2 = server.takeRequest()\n    assertThat(request2.headers[\"Cookie\"]).isEqualTo(\"a=android; b=banana\")\n  }\n\n  @Test\n  fun testRedirectsDoNotIncludeTooManyCookies() {\n    val redirectTarget = MockWebServer()\n    redirectTarget.enqueue(MockResponse.Builder().body(\"A\").build())\n    redirectTarget.start()\n    val redirectTargetUrl = urlWithIpAddress(redirectTarget, \"/\")\n    val redirectSource = MockWebServer()\n    redirectSource.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .addHeader(\"Location: $redirectTargetUrl\")\n        .build(),\n    )\n    redirectSource.start()\n    val redirectSourceUrl = urlWithIpAddress(redirectSource, \"/\")\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    val cookie = HttpCookie(\"c\", \"cookie\")\n    cookie.domain = redirectSourceUrl.host\n    cookie.path = \"/\"\n    val portList = redirectSource.port.toString()\n    cookie.portlist = portList\n    cookieManager.cookieStore.add(redirectSourceUrl.toUri(), cookie)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n    get(redirectSourceUrl)\n    val request = redirectSource.takeRequest()\n    assertThat(request.headers[\"Cookie\"]).isEqualTo(\"c=cookie\")\n    for (header in redirectTarget.takeRequest().headers.names()) {\n      if (header.startsWith(\"Cookie\")) {\n        fail(header)\n      }\n    }\n  }\n\n  @Test\n  fun testCookiesSentIgnoresCase() {\n    client =\n      client\n        .newBuilder()\n        .cookieJar(\n          JavaNetCookieJar(\n            object : CookieManager() {\n              override fun get(\n                uri: URI,\n                requestHeaders: Map<String, List<String>>,\n              ) = mapOf(\n                \"COOKIE\" to listOf(\"Bar=bar\"),\n                \"cooKIE2\" to listOf(\"Baz=baz\"),\n              )\n            },\n          ),\n        ).build()\n    server.enqueue(MockResponse())\n    get(server.url(\"/\"))\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Cookie\"]).isEqualTo(\"Bar=bar; Baz=baz\")\n    assertThat(request.headers[\"Cookie2\"]).isNull()\n    assertThat(request.headers[\"Quux\"]).isNull()\n  }\n\n  @Test\n  fun acceptOriginalServerMatchesSubdomain() {\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    val cookieJar = JavaNetCookieJar(cookieManager)\n    val url = \"https://www.squareup.com/\".toHttpUrl()\n    cookieJar.saveFromResponse(url, listOf(parse(url, \"a=android; Domain=squareup.com\")!!))\n    val actualCookies = cookieJar.loadForRequest(url)\n    assertThat(actualCookies.size).isEqualTo(1)\n    assertThat(actualCookies[0].name).isEqualTo(\"a\")\n    assertThat(actualCookies[0].value).isEqualTo(\"android\")\n  }\n\n  @Test\n  fun acceptOriginalServerMatchesRfc2965Dot() {\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    val cookieJar = JavaNetCookieJar(cookieManager)\n    val url = \"https://www.squareup.com/\".toHttpUrl()\n    cookieJar.saveFromResponse(url, listOf(parse(url, \"a=android; Domain=.squareup.com\")!!))\n    val actualCookies = cookieJar.loadForRequest(url)\n    assertThat(actualCookies.size).isEqualTo(1)\n    assertThat(actualCookies[0].name).isEqualTo(\"a\")\n    assertThat(actualCookies[0].value).isEqualTo(\"android\")\n  }\n\n  @Test\n  fun acceptOriginalServerMatchesExactly() {\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    val cookieJar = JavaNetCookieJar(cookieManager)\n    val url = \"https://squareup.com/\".toHttpUrl()\n    cookieJar.saveFromResponse(url, listOf(parse(url, \"a=android; Domain=squareup.com\")!!))\n    val actualCookies = cookieJar.loadForRequest(url)\n    assertThat(actualCookies.size).isEqualTo(1)\n    assertThat(actualCookies[0].name).isEqualTo(\"a\")\n    assertThat(actualCookies[0].value).isEqualTo(\"android\")\n  }\n\n  @Test\n  fun acceptOriginalServerDoesNotMatchDifferentServer() {\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    val cookieJar = JavaNetCookieJar(cookieManager)\n    val url1 = \"https://api.squareup.com/\".toHttpUrl()\n    cookieJar.saveFromResponse(url1, listOf(parse(url1, \"a=android; Domain=api.squareup.com\")!!))\n    val url2 = \"https://www.squareup.com/\".toHttpUrl()\n    val actualCookies = cookieJar.loadForRequest(url2)\n    assertThat(actualCookies).isEmpty()\n  }\n\n  @Test\n  fun testQuoteStripping() {\n    client =\n      client\n        .newBuilder()\n        .cookieJar(\n          JavaNetCookieJar(\n            object : CookieManager() {\n              override fun get(\n                uri: URI,\n                requestHeaders: Map<String, List<String>>,\n              ) = mapOf(\n                \"COOKIE\" to listOf(\"Bar=\\\"\"),\n                \"cooKIE2\" to listOf(\"Baz=\\\"baz\\\"\"),\n              )\n            },\n          ),\n        ).build()\n    server.enqueue(MockResponse())\n    get(server.url(\"/\"))\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Cookie\"]).isEqualTo(\"Bar=\\\"; Baz=baz\")\n    assertThat(request.headers[\"Cookie2\"]).isNull()\n    assertThat(request.headers[\"Quux\"]).isNull()\n  }\n\n  @Test\n  fun cookieHandlerWithQuotedValueAndTrailingSpace() {\n    server.enqueue(MockResponse())\n    val serverUrl = urlWithIpAddress(server, \"/\")\n    val androidCookieHandler: CookieHandler =\n      object : CookieHandler() {\n        override fun get(\n          uri: URI,\n          map: Map<String, List<String>>,\n        ) = mapOf(\n          \"Cookie\" to\n            listOf(\n              \"a=\\\"android \\\"\",\n            ),\n        )\n\n        override fun put(\n          uri: URI,\n          map: Map<String, List<String>>,\n        ) {\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(androidCookieHandler))\n        .build()\n    get(serverUrl)\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Cookie\"]).isEqualTo(\"a=android\")\n    assertThat(request.headers[\"Quux\"]).isNull()\n  }\n\n  @Test\n  fun receiveAndSendUntrimmedCookie() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Set-Cookie\", \"a=\\\"android \\\"\")\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    val cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(JavaNetCookieJar(cookieManager))\n        .build()\n    get(urlWithIpAddress(server, \"/\"))\n    val request1 = server.takeRequest()\n    assertThat(request1.headers[\"Cookie\"]).isNull()\n    get(urlWithIpAddress(server, \"/\"))\n    val request2 = server.takeRequest()\n    assertThat(request2.headers[\"Cookie\"]).isEqualTo(\"a=android\")\n  }\n\n  private fun urlWithIpAddress(\n    server: MockWebServer,\n    path: String,\n  ): HttpUrl =\n    server\n      .url(path)\n      .newBuilder()\n      .host(InetAddress.getByName(server.hostName).hostAddress)\n      .build()\n\n  private operator fun get(url: HttpUrl) {\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(url)\n          .build(),\n      )\n    val response = call.execute()\n    response.body.close()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/CorrettoTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isTrue\nimport okhttp3.TestUtil.assumeNetwork\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass CorrettoTest {\n  @JvmField @RegisterExtension\n  val platform = PlatformRule()\n\n  @JvmField @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private val client = clientTestRule.newClient()\n\n  @BeforeEach fun setUp() {\n    platform.assumeCorretto()\n  }\n\n  @Test\n  @Disabled\n  fun testMozilla() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://mozilla.org/robots.txt\").build()\n\n    client.newCall(request).execute().use {\n      assertThat(it.protocol).isEqualTo(Protocol.HTTP_2)\n      assertThat(it.handshake!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n    }\n  }\n\n  @Test\n  @Disabled\n  fun testGoogle() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://google.com/robots.txt\").build()\n\n    client.newCall(request).execute().use {\n      assertThat(it.protocol).isEqualTo(Protocol.HTTP_2)\n      if (it.handshake!!.tlsVersion != TlsVersion.TLS_1_3) {\n        System.err.println(\"Flaky TLSv1.3 with google\")\n//    assertThat(it.handshake()!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n      }\n    }\n  }\n\n  @Test\n  fun testIfSupported() {\n    assertThat(PlatformRule.isCorrettoSupported).isTrue()\n    assertThat(PlatformRule.isCorrettoInstalled).isTrue()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/DispatcherCleanupTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport org.junit.jupiter.api.Test\n\nclass DispatcherCleanupTest {\n  @StartStop\n  private val server = MockWebServer()\n\n  @Test\n  fun testFinish() {\n    val okhttp = OkHttpClient()\n    val callback =\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {}\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          response.close()\n        }\n      }\n    repeat(10_000) {\n      okhttp.newCall(Request.Builder().url(server.url(\"/\")).build()).enqueue(callback)\n    }\n    okhttp.dispatcher.executorService.shutdown()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/DispatcherTest.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.containsExactlyInAnyOrder\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isTrue\nimport assertk.assertions.none\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.UnknownHostException\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicBoolean\nimport kotlin.test.assertFailsWith\nimport okhttp3.CallEvent.CallFailed\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.DispatcherQueueEnd\nimport okhttp3.CallEvent.DispatcherQueueStart\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Tag(\"Slowish\")\nclass DispatcherTest {\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n  private val executor = RecordingExecutor(this)\n  val callback = RecordingCallback()\n  val webSocketListener =\n    object : WebSocketListener() {\n    }\n  val dispatcher = Dispatcher(executor)\n  val eventRecorder = EventRecorder()\n  var client =\n    clientTestRule\n      .newClientBuilder()\n      .dns { throw UnknownHostException() }\n      .dispatcher(dispatcher)\n      .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n      .build()\n\n  @BeforeEach\n  fun setUp() {\n    dispatcher.maxRequests = 20\n    dispatcher.maxRequestsPerHost = 10\n    eventRecorder.forbidLock(dispatcher)\n  }\n\n  @Test\n  fun maxRequestsZero() {\n    assertFailsWith<IllegalArgumentException> {\n      dispatcher.maxRequests = 0\n    }\n  }\n\n  @Test\n  fun maxPerHostZero() {\n    assertFailsWith<IllegalArgumentException> {\n      dispatcher.maxRequestsPerHost = 0\n    }\n  }\n\n  @Test\n  fun enqueuedJobsRunImmediately() {\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    executor.assertJobs(\"http://a/1\")\n\n    assertThat(eventRecorder.eventSequence).none { it.isInstanceOf<DispatcherQueueStart>() }\n    assertThat(eventRecorder.eventSequence).none { it.isInstanceOf<DispatcherQueueEnd>() }\n  }\n\n  @Test\n  fun maxRequestsEnforced() {\n    dispatcher.maxRequests = 3\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/2\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/2\")).enqueue(callback)\n    executor.assertJobs(\"http://a/1\", \"http://a/2\", \"http://b/1\")\n\n    val dispatcherQueueStart = eventRecorder.removeUpToEvent<DispatcherQueueStart>()\n    assertThat(dispatcherQueueStart.call.request().url).isEqualTo(\"http://b/2\".toHttpUrl())\n    assertThat(eventRecorder.eventSequence).none { it.isInstanceOf<DispatcherQueueEnd>() }\n  }\n\n  @Test\n  fun maxPerHostEnforced() {\n    dispatcher.maxRequestsPerHost = 2\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/2\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/3\")).enqueue(callback)\n    executor.assertJobs(\"http://a/1\", \"http://a/2\")\n\n    val dispatcherQueueStart = eventRecorder.removeUpToEvent<DispatcherQueueStart>()\n    assertThat(dispatcherQueueStart.call.request().url).isEqualTo(\"http://a/3\".toHttpUrl())\n    assertThat(eventRecorder.eventSequence).none { it.isInstanceOf<DispatcherQueueEnd>() }\n  }\n\n  @Test\n  fun maxPerHostNotEnforcedForWebSockets() {\n    dispatcher.maxRequestsPerHost = 2\n    client.newWebSocket(newRequest(\"http://a/1\"), webSocketListener)\n    client.newWebSocket(newRequest(\"http://a/2\"), webSocketListener)\n    client.newWebSocket(newRequest(\"http://a/3\"), webSocketListener)\n    executor.assertJobs(\"http://a/1\", \"http://a/2\", \"http://a/3\")\n  }\n\n  @Test\n  fun increasingMaxRequestsPromotesJobsImmediately() {\n    dispatcher.maxRequests = 2\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://c/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/2\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/2\")).enqueue(callback)\n\n    val dispatcherQueueStartC1 = eventRecorder.removeUpToEvent<DispatcherQueueStart>()\n    assertThat(dispatcherQueueStartC1.call.request().url).isEqualTo(\"http://c/1\".toHttpUrl())\n    val dispatcherQueueStartA2 = eventRecorder.removeUpToEvent<DispatcherQueueStart>()\n    assertThat(dispatcherQueueStartA2.call.request().url).isEqualTo(\"http://a/2\".toHttpUrl())\n    val dispatcherQueueStartB2 = eventRecorder.removeUpToEvent<DispatcherQueueStart>()\n    assertThat(dispatcherQueueStartB2.call.request().url).isEqualTo(\"http://b/2\".toHttpUrl())\n    assertThat(eventRecorder.eventSequence).none { it.isInstanceOf<DispatcherQueueEnd>() }\n\n    dispatcher.maxRequests = 4\n    executor.assertJobs(\"http://a/1\", \"http://b/1\", \"http://c/1\", \"http://a/2\")\n\n    val dispatcherQueueEndC1 = eventRecorder.removeUpToEvent<DispatcherQueueEnd>()\n    assertThat(dispatcherQueueEndC1.call.request().url).isEqualTo(\"http://c/1\".toHttpUrl())\n    val dispatcherQueueEndA2 = eventRecorder.removeUpToEvent<DispatcherQueueEnd>()\n    assertThat(dispatcherQueueEndA2.call.request().url).isEqualTo(\"http://a/2\".toHttpUrl())\n    assertThat(eventRecorder.eventSequence).none { it.isInstanceOf<DispatcherQueueEnd>() }\n  }\n\n  @Test\n  fun increasingMaxPerHostPromotesJobsImmediately() {\n    dispatcher.maxRequestsPerHost = 2\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/2\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/3\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/4\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/5\")).enqueue(callback)\n\n    val dispatcherQueueStartA3 = eventRecorder.removeUpToEvent<DispatcherQueueStart>()\n    assertThat(dispatcherQueueStartA3.call.request().url).isEqualTo(\"http://a/3\".toHttpUrl())\n    val dispatcherQueueStartA4 = eventRecorder.removeUpToEvent<DispatcherQueueStart>()\n    assertThat(dispatcherQueueStartA4.call.request().url).isEqualTo(\"http://a/4\".toHttpUrl())\n    val dispatcherQueueStartA5 = eventRecorder.removeUpToEvent<DispatcherQueueStart>()\n    assertThat(dispatcherQueueStartA5.call.request().url).isEqualTo(\"http://a/5\".toHttpUrl())\n    assertThat(eventRecorder.eventSequence).none { it.isInstanceOf<DispatcherQueueEnd>() }\n\n    dispatcher.maxRequestsPerHost = 4\n    executor.assertJobs(\"http://a/1\", \"http://a/2\", \"http://a/3\", \"http://a/4\")\n\n    val dispatcherQueueEndA3 = eventRecorder.removeUpToEvent<DispatcherQueueEnd>()\n    assertThat(dispatcherQueueEndA3.call.request().url).isEqualTo(\"http://a/3\".toHttpUrl())\n    val dispatcherQueueEndA4 = eventRecorder.removeUpToEvent<DispatcherQueueEnd>()\n    assertThat(dispatcherQueueEndA4.call.request().url).isEqualTo(\"http://a/4\".toHttpUrl())\n    assertThat(eventRecorder.eventSequence).none { it.isInstanceOf<DispatcherQueueEnd>() }\n  }\n\n  @Test\n  fun oldJobFinishesNewJobCanRunDifferentHost() {\n    dispatcher.maxRequests = 1\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/1\")).enqueue(callback)\n    executor.finishJob(\"http://a/1\")\n    executor.assertJobs(\"http://b/1\")\n  }\n\n  @Test\n  fun oldJobFinishesNewJobWithSameHostStarts() {\n    dispatcher.maxRequests = 2\n    dispatcher.maxRequestsPerHost = 1\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/2\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/2\")).enqueue(callback)\n    executor.finishJob(\"http://a/1\")\n    executor.assertJobs(\"http://b/1\", \"http://a/2\")\n  }\n\n  @Test\n  fun oldJobFinishesNewJobCantRunDueToHostLimit() {\n    dispatcher.maxRequestsPerHost = 1\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/2\")).enqueue(callback)\n    executor.finishJob(\"http://b/1\")\n    executor.assertJobs(\"http://a/1\")\n  }\n\n  @Test\n  fun enqueuedCallsStillRespectMaxCallsPerHost() {\n    dispatcher.maxRequests = 1\n    dispatcher.maxRequestsPerHost = 1\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/2\")).enqueue(callback)\n    client.newCall(newRequest(\"http://b/3\")).enqueue(callback)\n    dispatcher.maxRequests = 3\n    executor.finishJob(\"http://a/1\")\n    executor.assertJobs(\"http://b/1\")\n  }\n\n  @Test\n  fun cancelingRunningJobTakesNoEffectUntilJobFinishes() {\n    dispatcher.maxRequests = 1\n    val c1 = client.newCall(newRequest(\"http://a/1\", \"tag1\"))\n    val c2 = client.newCall(newRequest(\"http://a/2\"))\n    c1.enqueue(callback)\n    c2.enqueue(callback)\n    c1.cancel()\n    executor.assertJobs(\"http://a/1\")\n    executor.finishJob(\"http://a/1\")\n    executor.assertJobs(\"http://a/2\")\n  }\n\n  @Test\n  fun asyncCallAccessors() {\n    dispatcher.maxRequests = 3\n    val a1 = client.newCall(newRequest(\"http://a/1\"))\n    val a2 = client.newCall(newRequest(\"http://a/2\"))\n    val a3 = client.newCall(newRequest(\"http://a/3\"))\n    val a4 = client.newCall(newRequest(\"http://a/4\"))\n    val a5 = client.newCall(newRequest(\"http://a/5\"))\n    a1.enqueue(callback)\n    a2.enqueue(callback)\n    a3.enqueue(callback)\n    a4.enqueue(callback)\n    a5.enqueue(callback)\n    assertThat(dispatcher.runningCallsCount()).isEqualTo(3)\n    assertThat(dispatcher.queuedCallsCount()).isEqualTo(2)\n    assertThat(dispatcher.runningCalls())\n      .containsExactlyInAnyOrder(a1, a2, a3)\n    assertThat(dispatcher.queuedCalls())\n      .containsExactlyInAnyOrder(a4, a5)\n  }\n\n  @Test\n  fun synchronousCallAccessors() {\n    val ready = CountDownLatch(2)\n    val waiting = CountDownLatch(1)\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            try {\n              ready.countDown()\n              waiting.await()\n            } catch (e: InterruptedException) {\n              throw AssertionError()\n            }\n            throw IOException()\n          },\n        ).build()\n    val a1 = client.newCall(newRequest(\"http://a/1\"))\n    val a2 = client.newCall(newRequest(\"http://a/2\"))\n    val a3 = client.newCall(newRequest(\"http://a/3\"))\n    val a4 = client.newCall(newRequest(\"http://a/4\"))\n    val t1 = makeSynchronousCall(a1)\n    val t2 = makeSynchronousCall(a2)\n\n    // We created 4 calls and started 2 of them. That's 2 running calls and 0 queued.\n    ready.await()\n    assertThat(dispatcher.runningCallsCount()).isEqualTo(2)\n    assertThat(dispatcher.queuedCallsCount()).isEqualTo(0)\n    assertThat(dispatcher.runningCalls())\n      .containsExactlyInAnyOrder(a1, a2)\n    assertThat(dispatcher.queuedCalls()).isEmpty()\n\n    // Cancel some calls. That doesn't impact running or queued.\n    a2.cancel()\n    a3.cancel()\n    assertThat(dispatcher.runningCalls())\n      .containsExactlyInAnyOrder(a1, a2)\n    assertThat(dispatcher.queuedCalls()).isEmpty()\n\n    // Let the calls finish.\n    waiting.countDown()\n    t1.join()\n    t2.join()\n\n    // Now we should have 0 running calls and 0 queued calls.\n    assertThat(dispatcher.runningCallsCount()).isEqualTo(0)\n    assertThat(dispatcher.queuedCallsCount()).isEqualTo(0)\n    assertThat(dispatcher.runningCalls()).isEmpty()\n    assertThat(dispatcher.queuedCalls()).isEmpty()\n    assertThat(a1.isExecuted()).isTrue()\n    assertThat(a1.isCanceled()).isFalse()\n    assertThat(a2.isExecuted()).isTrue()\n    assertThat(a2.isCanceled()).isTrue()\n    assertThat(a3.isExecuted()).isFalse()\n    assertThat(a3.isCanceled()).isTrue()\n    assertThat(a4.isExecuted()).isFalse()\n    assertThat(a4.isCanceled()).isFalse()\n  }\n\n  @Test\n  fun idleCallbackInvokedWhenIdle() {\n    val idle = AtomicBoolean()\n    dispatcher.idleCallback = Runnable { idle.set(true) }\n    client.newCall(newRequest(\"http://a/1\")).enqueue(callback)\n    client.newCall(newRequest(\"http://a/2\")).enqueue(callback)\n    executor.finishJob(\"http://a/1\")\n    assertThat(idle.get()).isFalse()\n    val ready = CountDownLatch(1)\n    val proceed = CountDownLatch(1)\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            ready.countDown()\n            try {\n              proceed.await(5, TimeUnit.SECONDS)\n            } catch (e: InterruptedException) {\n              throw RuntimeException(e)\n            }\n            chain.proceed(chain.request())\n          },\n        ).build()\n    val t1 = makeSynchronousCall(client.newCall(newRequest(\"http://a/3\")))\n    ready.await(5, TimeUnit.SECONDS)\n    executor.finishJob(\"http://a/2\")\n    assertThat(idle.get()).isFalse()\n    proceed.countDown()\n    t1.join()\n    assertThat(idle.get()).isTrue()\n  }\n\n  @Test\n  fun executionRejectedImmediately() {\n    val request = newRequest(\"http://a/1\")\n    executor.shutdown()\n    client.newCall(request).enqueue(callback)\n    callback.await(request.url).assertFailure(InterruptedIOException::class.java)\n    assertThat(eventRecorder.recordedEventTypes())\n      .containsExactly(CallStart::class, CallFailed::class)\n  }\n\n  @Test\n  fun executionRejectedAfterMaxRequestsChange() {\n    val request1 = newRequest(\"http://a/1\")\n    val request2 = newRequest(\"http://a/2\")\n    dispatcher.maxRequests = 1\n    client.newCall(request1).enqueue(callback)\n    executor.shutdown()\n    client.newCall(request2).enqueue(callback)\n    dispatcher.maxRequests = 2 // Trigger promotion.\n    callback.await(request2.url).assertFailure(InterruptedIOException::class.java)\n    assertThat(eventRecorder.recordedEventTypes())\n      .containsExactly(CallStart::class, CallStart::class, CallFailed::class)\n  }\n\n  @Test\n  fun executionRejectedAfterMaxRequestsPerHostChange() {\n    val request1 = newRequest(\"http://a/1\")\n    val request2 = newRequest(\"http://a/2\")\n    dispatcher.maxRequestsPerHost = 1\n    client.newCall(request1).enqueue(callback)\n    executor.shutdown()\n    client.newCall(request2).enqueue(callback)\n    dispatcher.maxRequestsPerHost = 2 // Trigger promotion.\n    callback.await(request2.url).assertFailure(InterruptedIOException::class.java)\n    assertThat(eventRecorder.recordedEventTypes())\n      .containsExactly(CallStart::class, CallStart::class, CallFailed::class)\n  }\n\n  @Test\n  fun executionRejectedAfterPrecedingCallFinishes() {\n    val request1 = newRequest(\"http://a/1\")\n    val request2 = newRequest(\"http://a/2\")\n    dispatcher.maxRequests = 1\n    client.newCall(request1).enqueue(callback)\n    executor.shutdown()\n    client.newCall(request2).enqueue(callback)\n    executor.finishJob(\"http://a/1\") // Trigger promotion.\n    callback.await(request2.url).assertFailure(InterruptedIOException::class.java)\n    assertThat(eventRecorder.recordedEventTypes())\n      .containsExactly(CallStart::class, CallStart::class, CallFailed::class)\n  }\n\n  private fun makeSynchronousCall(call: Call): Thread {\n    val thread =\n      Thread {\n        try {\n          call.execute()\n          throw AssertionError()\n        } catch (expected: IOException) {\n        }\n      }\n    thread.start()\n    return thread\n  }\n\n  private fun newRequest(url: String): Request = Request.Builder().url(url).build()\n\n  private fun newRequest(\n    url: String,\n    tag: String,\n  ): Request =\n    Request\n      .Builder()\n      .url(url)\n      .tag(tag)\n      .build()\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/DuplexTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.all\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport assertk.assertions.prop\nimport java.io.IOException\nimport java.net.HttpURLConnection\nimport java.net.ProtocolException\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.Executors\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.TimeUnit\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.FollowUpDecision\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.RequestBodyEnd\nimport okhttp3.CallEvent.RequestBodyStart\nimport okhttp3.CallEvent.RequestFailed\nimport okhttp3.CallEvent.RequestHeadersEnd\nimport okhttp3.CallEvent.RequestHeadersStart\nimport okhttp3.CallEvent.ResponseBodyEnd\nimport okhttp3.CallEvent.ResponseBodyStart\nimport okhttp3.CallEvent.ResponseHeadersEnd\nimport okhttp3.CallEvent.ResponseHeadersStart\nimport okhttp3.CallEvent.SecureConnectEnd\nimport okhttp3.CallEvent.SecureConnectStart\nimport okhttp3.Credentials.basic\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.TestUtil.assumeNotWindows\nimport okhttp3.internal.RecordingOkAuthenticator\nimport okhttp3.internal.duplex.AsyncRequestBody\nimport okhttp3.internal.duplex.MockSocketHandler\nimport okhttp3.testing.PlatformRule\nimport okio.BufferedSink\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Timeout(30)\n@Tag(\"Slowish\")\nclass DuplexTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  var clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private var eventRecorder = EventRecorder()\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n      .build()\n  private val executorService = Executors.newScheduledThreadPool(1)\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n    platform.assumeHttp2Support()\n  }\n\n  @AfterEach\n  fun tearDown() {\n    executorService.shutdown()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun http1DoesntSupportDuplex() {\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(AsyncRequestBody())\n          .build(),\n      )\n    assertFailsWith<ProtocolException> {\n      call.execute()\n    }\n  }\n\n  @Test\n  fun trueDuplexClientWritesFirst() {\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .receiveRequest(\"request A\\n\")\n        .sendResponse(\"response B\\n\")\n        .receiveRequest(\"request C\\n\")\n        .sendResponse(\"response D\\n\")\n        .receiveRequest(\"request E\\n\")\n        .sendResponse(\"response F\\n\")\n        .exhaustRequest()\n        .exhaustResponse()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .socketHandler(body)\n        .build(),\n    )\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(AsyncRequestBody())\n          .build(),\n      )\n    call.execute().use { response ->\n      val requestBody = (call.request().body as AsyncRequestBody?)!!.takeSink()\n      requestBody.writeUtf8(\"request A\\n\")\n      requestBody.flush()\n      val responseBody = response.body.source()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response B\")\n      requestBody.writeUtf8(\"request C\\n\")\n      requestBody.flush()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response D\")\n      requestBody.writeUtf8(\"request E\\n\")\n      requestBody.flush()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response F\")\n      requestBody.close()\n      assertThat(responseBody.readUtf8Line()).isNull()\n    }\n    body.awaitSuccess()\n  }\n\n  @Test\n  fun trueDuplexServerWritesFirst() {\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .sendResponse(\"response A\\n\")\n        .receiveRequest(\"request B\\n\")\n        .sendResponse(\"response C\\n\")\n        .receiveRequest(\"request D\\n\")\n        .sendResponse(\"response E\\n\")\n        .receiveRequest(\"request F\\n\")\n        .exhaustResponse()\n        .exhaustRequest()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .socketHandler(body)\n        .build(),\n    )\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(AsyncRequestBody())\n          .build(),\n      )\n    call.execute().use { response ->\n      val requestBody = (call.request().body as AsyncRequestBody?)!!.takeSink()\n      val responseBody = response.body.source()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response A\")\n      requestBody.writeUtf8(\"request B\\n\")\n      requestBody.flush()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response C\")\n      requestBody.writeUtf8(\"request D\\n\")\n      requestBody.flush()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response E\")\n      requestBody.writeUtf8(\"request F\\n\")\n      requestBody.flush()\n      assertThat(responseBody.readUtf8Line()).isNull()\n      requestBody.close()\n    }\n    body.awaitSuccess()\n  }\n\n  @Test\n  fun clientReadsHeadersDataTrailers() {\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .sendResponse(\"ok\")\n        .exhaustResponse()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"h1\", \"v1\")\n        .addHeader(\"h2\", \"v2\")\n        .trailers(headersOf(\"trailers\", \"boom\"))\n        .socketHandler(body)\n        .build(),\n    )\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    call.execute().use { response ->\n      assertThat(response.headers)\n        .isEqualTo(headersOf(\"h1\", \"v1\", \"h2\", \"v2\"))\n      val responseBody = response.body.source()\n      assertThat(responseBody.readUtf8(2)).isEqualTo(\"ok\")\n      assertThat(responseBody.exhausted()).isTrue()\n      assertThat(response.trailers()).isEqualTo(headersOf(\"trailers\", \"boom\"))\n    }\n    body.awaitSuccess()\n  }\n\n  @Test\n  fun serverReadsHeadersData() {\n    assumeNotWindows()\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .exhaustResponse()\n        .receiveRequest(\"hey\\n\")\n        .receiveRequest(\"whats going on\\n\")\n        .exhaustRequest()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .addHeader(\"h1\", \"v1\")\n        .addHeader(\"h2\", \"v2\")\n        .socketHandler(body)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"POST\", AsyncRequestBody())\n        .build()\n    val call = client.newCall(request)\n    call.execute().use { response ->\n      val sink = (request.body as AsyncRequestBody?)!!.takeSink()\n      sink.writeUtf8(\"hey\\n\")\n      sink.writeUtf8(\"whats going on\\n\")\n      sink.close()\n    }\n    body.awaitSuccess()\n  }\n\n  @Test\n  fun requestBodyEndsAfterResponseBody() {\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .exhaustResponse()\n        .receiveRequest(\"request A\\n\")\n        .exhaustRequest()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .socketHandler(body)\n        .build(),\n    )\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(AsyncRequestBody())\n          .build(),\n      )\n    call.execute().use { response ->\n      val responseBody = response.body.source()\n      assertTrue(responseBody.exhausted())\n      val requestBody = (call.request().body as AsyncRequestBody?)!!.takeSink()\n      requestBody.writeUtf8(\"request A\\n\")\n      requestBody.close()\n    }\n    body.awaitSuccess()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      SecureConnectStart::class,\n      SecureConnectEnd::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      RequestBodyStart::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      RequestBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun duplexWith100Continue() {\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .receiveRequest(\"request body\\n\")\n        .sendResponse(\"response body\\n\")\n        .exhaustRequest()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .add100Continue()\n        .socketHandler(body)\n        .build(),\n    )\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .header(\"Expect\", \"100-continue\")\n          .post(AsyncRequestBody())\n          .build(),\n      )\n    call.execute().use { response ->\n      val requestBody = (call.request().body as AsyncRequestBody?)!!.takeSink()\n      requestBody.writeUtf8(\"request body\\n\")\n      requestBody.flush()\n      val responseBody = response.body.source()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response body\")\n      requestBody.close()\n      assertThat(responseBody.readUtf8Line()).isNull()\n    }\n    body.awaitSuccess()\n  }\n\n  /**\n   * Duplex calls that have follow-ups are weird. By the time we know there's a follow-up we've\n   * already split off another thread to stream the request body. Because we permit at most one\n   * exchange at a time we break the request stream out from under that writer.\n   */\n  @Test\n  fun duplexWithRedirect() {\n    enableProtocol(Protocol.HTTP_2)\n    val duplexResponseSent = CountDownLatch(1)\n    val requestHeadersEndListener =\n      object : EventListener() {\n        override fun requestHeadersEnd(\n          call: Call,\n          request: Request,\n        ) {\n          // Wait for the server to send the duplex response before acting on the 301 response\n          // and resetting the stream.\n          duplexResponseSent.await()\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .eventListener(eventRecorder.eventListener + requestHeadersEndListener)\n        .build()\n    val body =\n      MockSocketHandler()\n        .sendResponse(\"/a has moved!\\n\", duplexResponseSent)\n        .requestIOException()\n        .exhaustResponse()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .code(HttpURLConnection.HTTP_MOVED_PERM)\n        .addHeader(\"Location: /b\")\n        .socketHandler(body)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"this is /b\")\n        .build(),\n    )\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(AsyncRequestBody())\n          .build(),\n      )\n    call.execute().use { response ->\n      val responseBody = response.body.source()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"this is /b\")\n    }\n    val requestBody = (call.request().body as AsyncRequestBody?)!!.takeSink()\n    assertFailsWith<IOException> {\n      requestBody.writeUtf8(\"request body\\n\")\n      requestBody.flush()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"stream was reset: CANCEL\")\n    }\n    body.awaitSuccess()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      SecureConnectStart::class,\n      SecureConnectEnd::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      RequestBodyStart::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n      RequestFailed::class,\n    )\n    assertThat(eventRecorder.findEvent<FollowUpDecision>()).all {\n      prop(FollowUpDecision::nextRequest).isNotNull()\n    }\n  }\n\n  /**\n   * Auth requires follow-ups. Unlike redirects, the auth follow-up also has a request body. This\n   * test makes a single call with two duplex requests!\n   */\n  @Test\n  fun duplexWithAuthChallenge() {\n    enableProtocol(Protocol.HTTP_2)\n    val credential = basic(\"jesse\", \"secret\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(credential, null))\n        .build()\n    val body1 =\n      MockSocketHandler()\n        .sendResponse(\"please authenticate!\\n\")\n        .requestIOException()\n        .exhaustResponse()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .code(HttpURLConnection.HTTP_UNAUTHORIZED)\n        .socketHandler(body1)\n        .build(),\n    )\n    val body =\n      MockSocketHandler()\n        .sendResponse(\"response body\\n\")\n        .exhaustResponse()\n        .receiveRequest(\"request body\\n\")\n        .exhaustRequest()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .socketHandler(body)\n        .build(),\n    )\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(AsyncRequestBody())\n          .build(),\n      )\n    val response2 = call.execute()\n\n    // First duplex request is detached with violence.\n    val requestBody1 = (call.request().body as AsyncRequestBody?)!!.takeSink()\n    assertFailsWith<IOException> {\n      requestBody1.writeUtf8(\"not authenticated\\n\")\n      requestBody1.flush()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"stream was reset: CANCEL\")\n    }\n    body1.awaitSuccess()\n\n    // Second duplex request proceeds normally.\n    val requestBody2 = (call.request().body as AsyncRequestBody?)!!.takeSink()\n    requestBody2.writeUtf8(\"request body\\n\")\n    requestBody2.close()\n    val responseBody2 = response2.body.source()\n    assertThat(responseBody2.readUtf8Line())\n      .isEqualTo(\"response body\")\n    assertTrue(responseBody2.exhausted())\n    body.awaitSuccess()\n\n    // No more requests attempted!\n    (call.request().body as AsyncRequestBody?)!!.assertNoMoreSinks()\n  }\n\n  @Test\n  fun fullCallTimeoutAppliesToSetup() {\n    enableProtocol(Protocol.HTTP_2)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(AsyncRequestBody())\n        .build()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n      assertTrue(call.isCanceled())\n    }\n  }\n\n  @Test\n  fun fullCallTimeoutDoesNotApplyOnceConnected() {\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .sendResponse(\"response A\\n\")\n        .sleep(750, TimeUnit.MILLISECONDS)\n        .sendResponse(\"response B\\n\")\n        .receiveRequest(\"request C\\n\")\n        .exhaustResponse()\n        .exhaustRequest()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .socketHandler(body)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(AsyncRequestBody())\n        .build()\n    val call = client.newCall(request)\n    call\n      .timeout()\n      .timeout(500, TimeUnit.MILLISECONDS) // Long enough for the first TLS handshake.\n    call.execute().use { response ->\n      val requestBody = (call.request().body as AsyncRequestBody?)!!.takeSink()\n      val responseBody = response.body.source()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response A\")\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"response B\")\n      requestBody.writeUtf8(\"request C\\n\")\n      requestBody.close()\n      assertThat(responseBody.readUtf8Line()).isNull()\n    }\n    body.awaitSuccess()\n  }\n\n  @Test\n  fun duplexWithRewriteInterceptors() {\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .receiveRequest(\"REQUEST A\\n\")\n        .sendResponse(\"response B\\n\")\n        .exhaustRequest()\n        .exhaustResponse()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .socketHandler(body)\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(UppercaseRequestInterceptor())\n        .addInterceptor(UppercaseResponseInterceptor())\n        .build()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(AsyncRequestBody())\n          .build(),\n      )\n    call.execute().use { response ->\n      val requestBody = (call.request().body as AsyncRequestBody?)!!.takeSink()\n      requestBody.writeUtf8(\"request A\\n\")\n      requestBody.flush()\n      val responseBody = response.body.source()\n      assertThat(responseBody.readUtf8Line())\n        .isEqualTo(\"RESPONSE B\")\n      requestBody.close()\n      assertThat(responseBody.readUtf8Line()).isNull()\n    }\n    body.awaitSuccess()\n  }\n\n  /**\n   * OkHttp currently doesn't implement failing the request body stream independently of failing the\n   * corresponding response body stream. This is necessary if we want servers to be able to stop\n   * inbound data and send an early 400 before the request body completes.\n   *\n   * This test sends a slow request that is canceled by the server. It expects the response to still\n   * be readable after the request stream is canceled.\n   */\n  @Disabled\n  @Test\n  fun serverCancelsRequestBodyAndSendsResponseBody() {\n    client =\n      client\n        .newBuilder()\n        .retryOnConnectionFailure(false)\n        .build()\n    val log: BlockingQueue<String?> = LinkedBlockingQueue()\n    enableProtocol(Protocol.HTTP_2)\n    val body =\n      MockSocketHandler()\n        .sendResponse(\"success!\")\n        .exhaustResponse()\n        .cancelStream()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .socketHandler(body)\n        .build(),\n    )\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(\n            object : RequestBody() {\n              override fun contentType(): MediaType? = null\n\n              override fun writeTo(sink: BufferedSink) {\n                try {\n                  for (i in 0..9) {\n                    sink.writeUtf8(\".\")\n                    sink.flush()\n                    Thread.sleep(100)\n                  }\n                } catch (e: IOException) {\n                  log.add(e.toString())\n                  throw e\n                } catch (e: Exception) {\n                  log.add(e.toString())\n                }\n              }\n            },\n          ).build(),\n      )\n    call.execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"success!\")\n    }\n    body.awaitSuccess()\n    assertThat(log.take()!!)\n      .contains(\"StreamResetException: stream was reset: CANCEL\")\n  }\n\n  /**\n   * We delay sending the last byte of the request body 1500 ms. The 1000 ms read timeout should\n   * only elapse 1000 ms after the request body is sent.\n   */\n  @Test\n  fun headersReadTimeoutDoesNotStartUntilLastRequestBodyByteFire() {\n    enableProtocol(Protocol.HTTP_2)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(1500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(DelayedRequestBody(\"hello\".toRequestBody(null), 1500, TimeUnit.MILLISECONDS))\n        .build()\n    client =\n      client\n        .newBuilder()\n        .readTimeout(1000, TimeUnit.MILLISECONDS)\n        .build()\n    val call = client.newCall(request)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n    }\n  }\n\n  /** Same as the previous test, but the server stalls sending the response body.  */\n  @Test\n  fun bodyReadTimeoutDoesNotStartUntilLastRequestBodyByteFire() {\n    enableProtocol(Protocol.HTTP_2)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(1500, TimeUnit.MILLISECONDS)\n        .body(\"this should never be received\")\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(DelayedRequestBody(\"hello\".toRequestBody(null), 1500, TimeUnit.MILLISECONDS))\n        .build()\n    client =\n      client\n        .newBuilder()\n        .readTimeout(1000, TimeUnit.MILLISECONDS)\n        .build()\n    val call = client.newCall(request)\n    val response = call.execute()\n    assertFailsWith<IOException> {\n      response.body.string()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n    }\n  }\n\n  /**\n   * We delay sending the last byte of the request body 1500 ms. The 1000 ms read timeout shouldn't\n   * elapse because it shouldn't start until the request body is sent.\n   */\n  @Test\n  fun headersReadTimeoutDoesNotStartUntilLastRequestBodyByteNoFire() {\n    enableProtocol(Protocol.HTTP_2)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(DelayedRequestBody(\"hello\".toRequestBody(null), 1500, TimeUnit.MILLISECONDS))\n        .build()\n    client =\n      client\n        .newBuilder()\n        .readTimeout(1000, TimeUnit.MILLISECONDS)\n        .build()\n    val call = client.newCall(request)\n    val response = call.execute()\n    assertThat(response.isSuccessful).isTrue()\n  }\n\n  /**\n   * We delay sending the last byte of the request body 1500 ms. The 1000 ms read timeout shouldn't\n   * elapse because it shouldn't start until the request body is sent.\n   */\n  @Test\n  fun bodyReadTimeoutDoesNotStartUntilLastRequestBodyByteNoFire() {\n    enableProtocol(Protocol.HTTP_2)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(500, TimeUnit.MILLISECONDS)\n        .body(\"success\")\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(DelayedRequestBody(\"hello\".toRequestBody(null), 1500, TimeUnit.MILLISECONDS))\n        .build()\n    client =\n      client\n        .newBuilder()\n        .readTimeout(1000, TimeUnit.MILLISECONDS)\n        .build()\n    val call = client.newCall(request)\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"success\")\n  }\n\n  /**\n   * Tests that use this will fail unless boot classpath is set. Ex. `-Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317`\n   */\n  private fun enableProtocol(protocol: Protocol) {\n    enableTls()\n    client =\n      client\n        .newBuilder()\n        .protocols(listOf(protocol, Protocol.HTTP_1_1))\n        .build()\n    server.protocols = client.protocols\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n\n  private inner class DelayedRequestBody(\n    private val delegate: RequestBody,\n    delay: Long,\n    timeUnit: TimeUnit,\n  ) : RequestBody() {\n    private val delayMillis = timeUnit.toMillis(delay)\n\n    override fun contentType() = delegate.contentType()\n\n    override fun isDuplex() = true\n\n    override fun writeTo(sink: BufferedSink) {\n      executorService.schedule({\n        try {\n          delegate.writeTo(sink)\n          sink.close()\n        } catch (e: IOException) {\n          throw RuntimeException(e)\n        }\n      }, delayMillis, TimeUnit.MILLISECONDS)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/EventListenerTest.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport app.cash.burst.Burst\nimport assertk.all\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.doesNotContain\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isIn\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isNotEmpty\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isSameInstanceAs\nimport assertk.assertions.prop\nimport java.io.File\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.HttpURLConnection\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.UnknownHostException\nimport java.time.Duration\nimport java.util.Arrays\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CallEvent.CacheConditionalHit\nimport okhttp3.CallEvent.CacheHit\nimport okhttp3.CallEvent.CacheMiss\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallFailed\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.Canceled\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectFailed\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.FollowUpDecision\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.RequestBodyEnd\nimport okhttp3.CallEvent.RequestBodyStart\nimport okhttp3.CallEvent.RequestFailed\nimport okhttp3.CallEvent.RequestHeadersEnd\nimport okhttp3.CallEvent.RequestHeadersStart\nimport okhttp3.CallEvent.ResponseBodyEnd\nimport okhttp3.CallEvent.ResponseBodyStart\nimport okhttp3.CallEvent.ResponseFailed\nimport okhttp3.CallEvent.ResponseHeadersEnd\nimport okhttp3.CallEvent.ResponseHeadersStart\nimport okhttp3.CallEvent.RetryDecision\nimport okhttp3.CallEvent.SatisfactionFailure\nimport okhttp3.CallEvent.SecureConnectEnd\nimport okhttp3.CallEvent.SecureConnectStart\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okhttp3.internal.DoubleInetAddressDns\nimport okhttp3.internal.RecordingOkAuthenticator\nimport okhttp3.internal.connection.RealConnectionPool.Companion.get\nimport okhttp3.logging.HttpLoggingInterceptor\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule\nimport okio.Buffer\nimport okio.BufferedSink\nimport org.hamcrest.BaseMatcher\nimport org.hamcrest.CoreMatchers\nimport org.hamcrest.Description\nimport org.hamcrest.Matcher\nimport org.hamcrest.MatcherAssert\nimport org.junit.Assume.assumeThat\nimport org.junit.Assume.assumeTrue\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Flaky // STDOUT logging enabled for test\n@Timeout(30)\n@Tag(\"Slow\")\n@Burst\nclass EventListenerTest(\n  val listenerInstalledOn: ListenerInstalledOn = ListenerInstalledOn.Client,\n) {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private val eventRecorder = EventRecorder()\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .apply {\n        if (listenerInstalledOn == ListenerInstalledOn.Client) {\n          eventListenerFactory(clientTestRule.wrap(eventRecorder))\n        }\n      }.build()\n  private var socksProxy: SocksProxy? = null\n  private var cache: Cache? = null\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n    eventRecorder.forbidLock(get(client.connectionPool))\n    eventRecorder.forbidLock(client.dispatcher)\n  }\n\n  @AfterEach\n  fun tearDown() {\n    if (socksProxy != null) {\n      socksProxy!!.shutdown()\n    }\n    if (cache != null) {\n      cache!!.delete()\n    }\n  }\n\n  @Test\n  fun successfulCallEventSequence() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.body.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun successfulCallEventSequenceForIpAddress() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val ipAddress = InetAddress.getLoopbackAddress().hostAddress\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(\n            server\n              .url(\"/\")\n              .newBuilder()\n              .host(ipAddress!!)\n              .build(),\n          ).build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.body.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun successfulCallEventSequenceForEnqueue() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val completionLatch = CountDownLatch(1)\n    val callback: Callback =\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          completionLatch.countDown()\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          response.close()\n          completionLatch.countDown()\n        }\n      }\n    call.enqueue(callback)\n    completionLatch.await()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun failedCallEventSequence() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(2, TimeUnit.SECONDS)\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(250))\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isIn(\"timeout\", \"Read timed out\")\n    }\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseFailed::class,\n      RetryDecision::class,\n      ConnectionReleased::class,\n      CallFailed::class,\n    )\n    assertThat(eventRecorder.findEvent<RetryDecision>()).all {\n      prop(RetryDecision::retry).isFalse()\n    }\n  }\n\n  @Test\n  fun failedDribbledCallEventSequence() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"0123456789\")\n        .throttleBody(2, 100, TimeUnit.MILLISECONDS)\n        .onResponseBody(CloseSocket())\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .protocols(listOf<Protocol>(Protocol.HTTP_1_1))\n        .readTimeout(Duration.ofMillis(250))\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertFailsWith<IOException> {\n      response.body.string()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"unexpected end of stream\")\n    }\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseFailed::class,\n      ConnectionReleased::class,\n      CallFailed::class,\n    )\n    val responseFailed = eventRecorder.removeUpToEvent<ResponseFailed>()\n    assertThat(responseFailed.ioe.message).isEqualTo(\"unexpected end of stream\")\n  }\n\n  @Test\n  fun canceledCallEventSequence() {\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    call.cancel()\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Canceled\")\n    }\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      Canceled::class,\n      CallStart::class,\n      CallFailed::class,\n    )\n  }\n\n  @Test\n  fun cancelAsyncCall() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    call.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          response.close()\n        }\n      },\n    )\n    call.cancel()\n    assertThat(eventRecorder.recordedEventTypes()).contains(Canceled::class)\n  }\n\n  @Test\n  fun multipleCancelsEmitsOnlyOneEvent() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    call.cancel()\n    call.cancel()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(Canceled::class)\n  }\n\n  private fun assertSuccessfulEventOrder(\n    responseMatcher: Matcher<Response?>?,\n    emptyBody: Boolean = false,\n  ) {\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.string()\n    response.body.close()\n    assumeThat(response, responseMatcher)\n    var expectedEventTypes =\n      listOf(\n        CallStart::class,\n        ProxySelectStart::class,\n        ProxySelectEnd::class,\n        DnsStart::class,\n        DnsEnd::class,\n        ConnectStart::class,\n        SecureConnectStart::class,\n        SecureConnectEnd::class,\n        ConnectEnd::class,\n        ConnectionAcquired::class,\n        RequestHeadersStart::class,\n        RequestHeadersEnd::class,\n        ResponseHeadersStart::class,\n        ResponseHeadersEnd::class,\n      )\n    expectedEventTypes +=\n      when {\n        emptyBody -> {\n          listOf(\n            ResponseBodyStart::class,\n            ResponseBodyEnd::class,\n            FollowUpDecision::class,\n          )\n        }\n\n        else -> {\n          listOf(\n            FollowUpDecision::class,\n            ResponseBodyStart::class,\n            ResponseBodyEnd::class,\n          )\n        }\n      }\n    expectedEventTypes +=\n      listOf(\n        ConnectionReleased::class,\n        CallEnd::class,\n      )\n    assertThat(eventRecorder.recordedEventTypes()).isEqualTo(expectedEventTypes)\n  }\n\n  @Test\n  fun secondCallEventSequence() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    client\n      .newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      ).execute()\n      .close()\n    eventRecorder.removeUpToEvent<CallEnd>()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    response.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  private fun assertBytesReadWritten(\n    listener: EventRecorder,\n    requestHeaderLength: Matcher<Long?>?,\n    requestBodyBytes: Matcher<Long?>?,\n    responseHeaderLength: Matcher<Long?>?,\n    responseBodyBytes: Matcher<Long?>?,\n  ) {\n    if (requestHeaderLength != null) {\n      val responseHeadersEnd = listener.removeUpToEvent<RequestHeadersEnd>()\n      MatcherAssert.assertThat(\n        \"request header length\",\n        responseHeadersEnd.headerLength,\n        requestHeaderLength,\n      )\n    } else {\n      assertThat(listener.recordedEventTypes())\n        .doesNotContain(RequestHeadersEnd::class)\n    }\n    if (requestBodyBytes != null) {\n      val responseBodyEnd: RequestBodyEnd = listener.removeUpToEvent<RequestBodyEnd>()\n      MatcherAssert.assertThat(\n        \"request body bytes\",\n        responseBodyEnd.bytesWritten,\n        requestBodyBytes,\n      )\n    } else {\n      assertThat(listener.recordedEventTypes()).doesNotContain(RequestBodyEnd::class)\n    }\n    if (responseHeaderLength != null) {\n      val responseHeadersEnd: ResponseHeadersEnd =\n        listener.removeUpToEvent<ResponseHeadersEnd>()\n      MatcherAssert.assertThat(\n        \"response header length\",\n        responseHeadersEnd.headerLength,\n        responseHeaderLength,\n      )\n    } else {\n      assertThat(listener.recordedEventTypes())\n        .doesNotContain(ResponseHeadersEnd::class)\n    }\n    if (responseBodyBytes != null) {\n      val responseBodyEnd: ResponseBodyEnd = listener.removeUpToEvent<ResponseBodyEnd>()\n      MatcherAssert.assertThat(\n        \"response body bytes\",\n        responseBodyEnd.bytesRead,\n        responseBodyBytes,\n      )\n    } else {\n      assertThat(listener.recordedEventTypes()).doesNotContain(ResponseBodyEnd::class)\n    }\n  }\n\n  private fun greaterThan(value: Long): Matcher<Long?> =\n    object : BaseMatcher<Long?>() {\n      override fun describeTo(description: Description?) {\n        description!!.appendText(\"> $value\")\n      }\n\n      override fun matches(o: Any?): Boolean = (o as Long?)!! > value\n    }\n\n  private fun matchesProtocol(protocol: Protocol?): Matcher<Response?> =\n    object : BaseMatcher<Response?>() {\n      override fun describeTo(description: Description?) {\n        description!!.appendText(\"is HTTP/2\")\n      }\n\n      override fun matches(o: Any?): Boolean = (o as Response?)!!.protocol == protocol\n    }\n\n  @Test\n  fun successfulEmptyH2CallEventSequence() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    server.enqueue(MockResponse())\n    assertSuccessfulEventOrder(matchesProtocol(Protocol.HTTP_2), emptyBody = true)\n    assertBytesReadWritten(\n      eventRecorder,\n      CoreMatchers.any(Long::class.java),\n      null,\n      greaterThan(0L),\n      CoreMatchers.equalTo(0L),\n    )\n  }\n\n  @Test\n  fun successfulEmptyHttpsCallEventSequence() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_1_1)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    assertSuccessfulEventOrder(anyResponse)\n    assertBytesReadWritten(\n      eventRecorder,\n      CoreMatchers.any(Long::class.java),\n      null,\n      greaterThan(0L),\n      CoreMatchers.equalTo(3L),\n    )\n  }\n\n  @Test\n  fun successfulChunkedHttpsCallEventSequence() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_1_1)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(100, TimeUnit.MILLISECONDS)\n        .chunkedBody(\"Hello!\", 2)\n        .build(),\n    )\n    assertSuccessfulEventOrder(anyResponse)\n    assertBytesReadWritten(\n      eventRecorder,\n      CoreMatchers.any(Long::class.java),\n      null,\n      greaterThan(0L),\n      CoreMatchers.equalTo(6L),\n    )\n  }\n\n  @Test\n  fun successfulChunkedH2CallEventSequence() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(100, TimeUnit.MILLISECONDS)\n        .chunkedBody(\"Hello!\", 2)\n        .build(),\n    )\n    assertSuccessfulEventOrder(matchesProtocol(Protocol.HTTP_2))\n    assertBytesReadWritten(\n      eventRecorder,\n      CoreMatchers.any(Long::class.java),\n      null,\n      CoreMatchers.equalTo(0L),\n      greaterThan(6L),\n    )\n  }\n\n  @Test\n  fun successfulDnsLookup() {\n    server.enqueue(MockResponse())\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val dnsStart: DnsStart = eventRecorder.removeUpToEvent<DnsStart>()\n    assertThat(dnsStart.call).isSameInstanceAs(call)\n    assertThat(dnsStart.domainName).isEqualTo(server.hostName)\n    val dnsEnd: DnsEnd = eventRecorder.removeUpToEvent<DnsEnd>()\n    assertThat(dnsEnd.call).isSameInstanceAs(call)\n    assertThat(dnsEnd.domainName).isEqualTo(server.hostName)\n    assertThat(dnsEnd.inetAddressList.size).isEqualTo(1)\n  }\n\n  @Test\n  fun noDnsLookupOnPooledConnection() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n\n    // Seed the pool.\n    val call1 =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response1 = call1.execute()\n    assertThat(response1.code).isEqualTo(200)\n    response1.body.close()\n    eventRecorder.clearAllEvents()\n    val call2 =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response2 = call2.execute()\n    assertThat(response2.code).isEqualTo(200)\n    response2.body.close()\n    val recordedEvents = eventRecorder.recordedEventTypes()\n    assertThat(recordedEvents).doesNotContain(DnsStart::class)\n    assertThat(recordedEvents).doesNotContain(DnsEnd::class)\n  }\n\n  @Test\n  fun multipleDnsLookupsForSingleCall() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .setHeader(\"Location\", \"http://www.fakeurl:\" + server.port)\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    val dns = FakeDns()\n    dns[\"fakeurl\"] = client.dns.lookup(server.hostName)\n    dns[\"www.fakeurl\"] = client.dns.lookup(server.hostName)\n    client =\n      client\n        .newBuilder()\n        .dns(dns)\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(\"http://fakeurl:\" + server.port)\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    eventRecorder.removeUpToEvent<DnsStart>()\n    eventRecorder.removeUpToEvent<DnsEnd>()\n    eventRecorder.removeUpToEvent<DnsStart>()\n    eventRecorder.removeUpToEvent<DnsEnd>()\n  }\n\n  @Test\n  fun failedDnsLookup() {\n    client =\n      client\n        .newBuilder()\n        .dns(FakeDns())\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(\"http://fakeurl/\")\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    eventRecorder.removeUpToEvent<DnsStart>()\n    val callFailed: CallFailed = eventRecorder.removeUpToEvent<CallFailed>()\n    assertThat(callFailed.call).isSameInstanceAs(call)\n    assertThat(callFailed.ioe).isInstanceOf<UnknownHostException>()\n  }\n\n  @Test\n  fun emptyDnsLookup() {\n    val emptyDns = Dns { listOf() }\n    client =\n      client\n        .newBuilder()\n        .dns(emptyDns)\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(\"http://fakeurl/\")\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    eventRecorder.removeUpToEvent<DnsStart>()\n    val callFailed: CallFailed = eventRecorder.removeUpToEvent<CallFailed>()\n    assertThat(callFailed.call).isSameInstanceAs(call)\n    assertThat(callFailed.ioe).isInstanceOf(\n      UnknownHostException::class.java,\n    )\n  }\n\n  @Test\n  fun successfulConnect() {\n    server.enqueue(MockResponse())\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val address = client.dns.lookup(server.hostName)[0]\n    val expectedAddress = InetSocketAddress(address, server.port)\n    val connectStart = eventRecorder.removeUpToEvent<ConnectStart>()\n    assertThat(connectStart.call).isSameInstanceAs(call)\n    assertThat(connectStart.inetSocketAddress).isEqualTo(expectedAddress)\n    assertThat(connectStart.proxy).isEqualTo(Proxy.NO_PROXY)\n    val connectEnd = eventRecorder.removeUpToEvent<CallEvent.ConnectEnd>()\n    assertThat(connectEnd.call).isSameInstanceAs(call)\n    assertThat(connectEnd.inetSocketAddress).isEqualTo(expectedAddress)\n    assertThat(connectEnd.protocol).isEqualTo(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun failedConnect() {\n    enableTlsWithTunnel()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .failHandshake()\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    val address = client.dns.lookup(server.hostName)[0]\n    val expectedAddress = InetSocketAddress(address, server.port)\n    val connectStart = eventRecorder.removeUpToEvent<ConnectStart>()\n    assertThat(connectStart.call).isSameInstanceAs(call)\n    assertThat(connectStart.inetSocketAddress).isEqualTo(expectedAddress)\n    assertThat(connectStart.proxy).isEqualTo(Proxy.NO_PROXY)\n    val connectFailed = eventRecorder.removeUpToEvent<ConnectFailed>()\n    assertThat(connectFailed.call).isSameInstanceAs(call)\n    assertThat(connectFailed.inetSocketAddress).isEqualTo(expectedAddress)\n    assertThat(connectFailed.protocol).isNull()\n    assertThat(connectFailed.ioe).isNotNull()\n  }\n\n  @Test\n  fun multipleConnectsForSingleCall() {\n    enableTlsWithTunnel()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .failHandshake()\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    eventRecorder.removeUpToEvent<ConnectStart>()\n    eventRecorder.removeUpToEvent<ConnectFailed>()\n    eventRecorder.removeUpToEvent<ConnectStart>()\n    eventRecorder.removeUpToEvent<ConnectEnd>()\n  }\n\n  @Test\n  fun successfulHttpProxyConnect() {\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(\"http://www.fakeurl\")\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val address = client.dns.lookup(server.hostName)[0]\n    val expectedAddress = InetSocketAddress(address, server.port)\n    val connectStart: ConnectStart =\n      eventRecorder.removeUpToEvent<ConnectStart>()\n    assertThat(connectStart.call).isSameInstanceAs(call)\n    assertThat(connectStart.inetSocketAddress).isEqualTo(expectedAddress)\n    assertThat(connectStart.proxy).isEqualTo(\n      server.proxyAddress,\n    )\n    val connectEnd = eventRecorder.removeUpToEvent<ConnectEnd>()\n    assertThat(connectEnd.call).isSameInstanceAs(call)\n    assertThat(connectEnd.inetSocketAddress).isEqualTo(expectedAddress)\n    assertThat(connectEnd.protocol).isEqualTo(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun successfulSocksProxyConnect() {\n    server.enqueue(MockResponse())\n    socksProxy = SocksProxy()\n    socksProxy!!.play()\n    val proxy = socksProxy!!.proxy()\n    client =\n      client\n        .newBuilder()\n        .proxy(proxy)\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(\"http://\" + SocksProxy.HOSTNAME_THAT_ONLY_THE_PROXY_KNOWS + \":\" + server.port)\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val expectedAddress =\n      InetSocketAddress.createUnresolved(\n        SocksProxy.HOSTNAME_THAT_ONLY_THE_PROXY_KNOWS,\n        server.port,\n      )\n    val connectStart = eventRecorder.removeUpToEvent<ConnectStart>()\n    assertThat(connectStart.call).isSameInstanceAs(call)\n    assertThat(connectStart.inetSocketAddress).isEqualTo(expectedAddress)\n    assertThat(connectStart.proxy).isEqualTo(proxy)\n    val connectEnd = eventRecorder.removeUpToEvent<ConnectEnd>()\n    assertThat(connectEnd.call).isSameInstanceAs(call)\n    assertThat(connectEnd.inetSocketAddress).isEqualTo(expectedAddress)\n    assertThat(connectEnd.protocol).isEqualTo(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun authenticatingTunnelProxyConnect() {\n    enableTlsWithTunnel()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .code(407)\n        .addHeader(\"Proxy-Authenticate: Basic realm=\\\"localhost\\\"\")\n        .addHeader(\"Connection: close\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .proxyAuthenticator(RecordingOkAuthenticator(\"password\", \"Basic\"))\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    eventRecorder.removeUpToEvent<ConnectStart>()\n    val connectEnd = eventRecorder.removeUpToEvent<ConnectEnd>()\n    assertThat(connectEnd.protocol).isNull()\n    eventRecorder.removeUpToEvent<ConnectStart>()\n    eventRecorder.removeUpToEvent<ConnectEnd>()\n  }\n\n  @Test\n  fun successfulSecureConnect() {\n    enableTlsWithTunnel()\n    server.enqueue(MockResponse())\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val secureStart = eventRecorder.removeUpToEvent<SecureConnectStart>()\n    assertThat(secureStart.call).isSameInstanceAs(call)\n    val secureEnd = eventRecorder.removeUpToEvent<SecureConnectEnd>()\n    assertThat(secureEnd.call).isSameInstanceAs(call)\n    assertThat(secureEnd.handshake).isNotNull()\n  }\n\n  @Test\n  fun failedSecureConnect() {\n    enableTlsWithTunnel()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .failHandshake()\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    val secureStart = eventRecorder.removeUpToEvent<SecureConnectStart>()\n    assertThat(secureStart.call).isSameInstanceAs(call)\n    val callFailed = eventRecorder.removeUpToEvent<CallFailed>()\n    assertThat(callFailed.call).isSameInstanceAs(call)\n    assertThat(callFailed.ioe).isNotNull()\n  }\n\n  @Test\n  fun secureConnectWithTunnel() {\n    enableTlsWithTunnel()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val secureStart = eventRecorder.removeUpToEvent<SecureConnectStart>()\n    assertThat(secureStart.call).isSameInstanceAs(call)\n    val secureEnd = eventRecorder.removeUpToEvent<SecureConnectEnd>()\n    assertThat(secureEnd.call).isSameInstanceAs(call)\n    assertThat(secureEnd.handshake).isNotNull()\n  }\n\n  @Test\n  fun multipleSecureConnectsForSingleCall() {\n    enableTlsWithTunnel()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .failHandshake()\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    eventRecorder.removeUpToEvent<SecureConnectStart>()\n    eventRecorder.removeUpToEvent<ConnectFailed>()\n    eventRecorder.removeUpToEvent<SecureConnectStart>()\n    eventRecorder.removeUpToEvent<SecureConnectEnd>()\n  }\n\n  @Test\n  fun noSecureConnectsOnPooledConnection() {\n    enableTlsWithTunnel()\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n\n    // Seed the pool.\n    val call1 =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response1 = call1.execute()\n    assertThat(response1.code).isEqualTo(200)\n    response1.body.close()\n    eventRecorder.clearAllEvents()\n    val call2 =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response2 = call2.execute()\n    assertThat(response2.code).isEqualTo(200)\n    response2.body.close()\n    val recordedEvents = eventRecorder.recordedEventTypes()\n    assertThat(recordedEvents).doesNotContain(SecureConnectStart::class)\n    assertThat(recordedEvents).doesNotContain(SecureConnectEnd::class)\n  }\n\n  @Test\n  fun successfulConnectionFound() {\n    server.enqueue(MockResponse())\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.body.close()\n    val connectionAcquired = eventRecorder.removeUpToEvent<ConnectionAcquired>()\n    assertThat(connectionAcquired.call).isSameInstanceAs(call)\n    assertThat(connectionAcquired.connection).isNotNull()\n  }\n\n  @Test\n  fun noConnectionFoundOnFollowUp() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .addHeader(\"Location\", \"/foo\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABC\")\n    eventRecorder.removeUpToEvent<ConnectionAcquired>()\n    val remainingEvents = eventRecorder.recordedEventTypes()\n    assertThat(remainingEvents).doesNotContain(ConnectionAcquired::class)\n  }\n\n  @Test\n  fun pooledConnectionFound() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n\n    // Seed the pool.\n    val call1 =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response1 = call1.execute()\n    assertThat(response1.code).isEqualTo(200)\n    response1.body.close()\n    val connectionAcquired1 = eventRecorder.removeUpToEvent<ConnectionAcquired>()\n    eventRecorder.clearAllEvents()\n    val call2 =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response2 = call2.execute()\n    assertThat(response2.code).isEqualTo(200)\n    response2.body.close()\n    val connectionAcquired2 = eventRecorder.removeUpToEvent<ConnectionAcquired>()\n    assertThat(connectionAcquired2.connection).isSameInstanceAs(\n      connectionAcquired1.connection,\n    )\n  }\n\n  @Test\n  fun multipleConnectionsFoundForSingleCall() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(301)\n        .addHeader(\"Location\", \"/foo\")\n        .addHeader(\"Connection\", \"Close\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABC\")\n    eventRecorder.removeUpToEvent<ConnectionAcquired>()\n    eventRecorder.removeUpToEvent<ConnectionAcquired>()\n  }\n\n  @Test\n  fun responseBodyFailHttp1OverHttps() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_1_1)\n    responseBodyFail(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun responseBodyFailHttp2OverHttps() {\n    platform.assumeHttp2Support()\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    responseBodyFail(Protocol.HTTP_2)\n  }\n\n  @Test\n  fun responseBodyFailHttp() {\n    responseBodyFail(Protocol.HTTP_1_1)\n  }\n\n  private fun responseBodyFail(expectedProtocol: Protocol?) {\n    // Use a 2 MiB body so the disconnect won't happen until the client has read some data.\n    val responseBodySize = 2 * 1024 * 1024 // 2 MiB\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(Buffer().write(ByteArray(responseBodySize)))\n        .onResponseBody(CloseSocket())\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    if (expectedProtocol == Protocol.HTTP_2) {\n      // soft failure since client may not support depending on Platform\n      assumeThat(response, matchesProtocol(Protocol.HTTP_2))\n    }\n    assertThat(response.protocol).isEqualTo(expectedProtocol)\n    assertFailsWith<IOException> {\n      response.body.string()\n    }\n    val callFailed = eventRecorder.removeUpToEvent<CallFailed>()\n    assertThat(callFailed.ioe).isNotNull()\n  }\n\n  @Test\n  fun emptyResponseBody() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"\")\n        .bodyDelay(1, TimeUnit.SECONDS)\n        .onResponseBody(CloseSocket())\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    response.body.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun emptyResponseBodyConnectionClose() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Connection\", \"close\")\n        .body(\"\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    response.body.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun responseBodyClosedClosedWithoutReadingAllData() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .bodyDelay(1, TimeUnit.SECONDS)\n        .onResponseBody(CloseSocket())\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    response.body.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun requestBodyFailHttp1OverHttps() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_1_1)\n    requestBodyFail(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun requestBodyFailHttp2OverHttps() {\n    platform.assumeHttp2Support()\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    requestBodyFail(Protocol.HTTP_2)\n  }\n\n  @Test\n  fun requestBodyFailHttp() {\n    requestBodyFail(null)\n  }\n\n  private fun requestBodyFail(expectedProtocol: Protocol?) {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestBody(CloseSocket())\n        .build(),\n    )\n    val request = NonCompletingRequestBody()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(request)\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    if (expectedProtocol != null) {\n      val connectionAcquired = eventRecorder.removeUpToEvent<ConnectionAcquired>()\n      assertThat(connectionAcquired.connection.protocol())\n        .isEqualTo(expectedProtocol)\n    }\n    val callFailed = eventRecorder.removeUpToEvent<CallFailed>()\n    assertThat(callFailed.ioe).isNotNull()\n    assertThat(request.ioe).isNotNull()\n  }\n\n  private inner class NonCompletingRequestBody : RequestBody() {\n    private val chunk: ByteArray? = ByteArray(1024 * 1024)\n    var ioe: IOException? = null\n\n    override fun contentType(): MediaType? = \"text/plain\".toMediaType()\n\n    override fun contentLength(): Long = chunk!!.size * 8L\n\n    override fun writeTo(sink: BufferedSink) {\n      try {\n        var i = 0\n        while (i < contentLength()) {\n          sink.write(chunk!!)\n          sink.flush()\n          Thread.sleep(100)\n          i += chunk.size\n        }\n      } catch (e: IOException) {\n        ioe = e\n      } catch (e: InterruptedException) {\n        throw RuntimeException(e)\n      }\n    }\n  }\n\n  @Test\n  fun requestBodyMultipleFailuresReportedOnlyOnce() {\n    val requestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType() = \"text/plain\".toMediaType()\n\n        override fun contentLength(): Long = 1024 * 1024 * 256\n\n        override fun writeTo(sink: BufferedSink) {\n          var failureCount = 0\n          for (i in 0..1023) {\n            try {\n              sink.write(ByteArray(1024 * 256))\n              sink.flush()\n            } catch (e: IOException) {\n              failureCount++\n              if (failureCount == 3) throw e\n            }\n          }\n        }\n      }\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestBody(CloseSocket())\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(requestBody)\n          .build(),\n      )\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      RequestBodyStart::class,\n      RequestFailed::class,\n      ResponseFailed::class,\n      RetryDecision::class,\n      ConnectionReleased::class,\n      CallFailed::class,\n    )\n  }\n\n  @Test\n  fun requestBodySuccessHttp1OverHttps() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_1_1)\n    requestBodySuccess(\n      \"Hello\".toRequestBody(\"text/plain\".toMediaType()),\n      CoreMatchers.equalTo(5L),\n      CoreMatchers.equalTo(19L),\n    )\n  }\n\n  @Test\n  fun requestBodySuccessHttp2OverHttps() {\n    platform.assumeHttp2Support()\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    requestBodySuccess(\n      \"Hello\".toRequestBody(\"text/plain\".toMediaType()),\n      CoreMatchers.equalTo(5L),\n      CoreMatchers.equalTo(19L),\n    )\n  }\n\n  @Test\n  fun requestBodySuccessHttp() {\n    requestBodySuccess(\n      \"Hello\".toRequestBody(\"text/plain\".toMediaType()),\n      CoreMatchers.equalTo(5L),\n      CoreMatchers.equalTo(19L),\n    )\n  }\n\n  @Test\n  fun requestBodySuccessStreaming() {\n    val requestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType() = \"text/plain\".toMediaType()\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.write(ByteArray(8192))\n          sink.flush()\n        }\n      }\n    requestBodySuccess(requestBody, CoreMatchers.equalTo(8192L), CoreMatchers.equalTo(19L))\n  }\n\n  @Test\n  fun requestBodySuccessEmpty() {\n    requestBodySuccess(\n      \"\".toRequestBody(\"text/plain\".toMediaType()),\n      CoreMatchers.equalTo(0L),\n      CoreMatchers.equalTo(19L),\n    )\n  }\n\n  @Test\n  fun successfulCallEventSequenceWithListener() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          HttpLoggingInterceptor()\n            .setLevel(HttpLoggingInterceptor.Level.BODY),\n        ).build()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.body.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  private fun requestBodySuccess(\n    body: RequestBody?,\n    requestBodyBytes: Matcher<Long?>?,\n    responseHeaderLength: Matcher<Long?>?,\n  ) {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(200)\n        .body(\"World!\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(body!!)\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"World!\")\n    assertBytesReadWritten(\n      eventRecorder,\n      CoreMatchers.any(Long::class.java),\n      requestBodyBytes,\n      responseHeaderLength,\n      CoreMatchers.equalTo(6L),\n    )\n  }\n\n  @Test\n  fun timeToFirstByteHttp1OverHttps() {\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_1_1)\n    timeToFirstByte()\n  }\n\n  @Test\n  fun timeToFirstByteHttp2OverHttps() {\n    platform.assumeHttp2Support()\n    enableTlsWithTunnel()\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    timeToFirstByte()\n  }\n\n  /**\n   * Test to confirm that events are reported at the time they occur and no earlier and no later.\n   * This inserts a bunch of synthetic 250 ms delays into both client and server and confirms that\n   * the same delays make it back into the events.\n   *\n   * We've had bugs where we report an event when we request data rather than when the data actually\n   * arrives. https://github.com/square/okhttp/issues/5578\n   */\n  private fun timeToFirstByte() {\n    val applicationInterceptorDelay = 250L\n    val networkInterceptorDelay = 250L\n    val requestBodyDelay = 250L\n    val responseHeadersStartDelay = 250L\n    val responseBodyStartDelay = 250L\n    val responseBodyEndDelay = 250L\n\n    // Warm up the client so the timing part of the test gets a pooled connection.\n    server.enqueue(MockResponse())\n    val warmUpCall =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    warmUpCall.execute().use { warmUpResponse -> warmUpResponse.body.string() }\n    eventRecorder.clearAllEvents()\n\n    // Create a client with artificial delays.\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            try {\n              Thread.sleep(applicationInterceptorDelay)\n              return@Interceptor chain.proceed(chain.request())\n            } catch (e: InterruptedException) {\n              throw InterruptedIOException()\n            }\n          },\n        ).addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            try {\n              Thread.sleep(networkInterceptorDelay)\n              return@Interceptor chain.proceed(chain.request())\n            } catch (e: InterruptedException) {\n              throw InterruptedIOException()\n            }\n          },\n        ).build()\n\n    // Create a request body with artificial delays.\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(\n            object : RequestBody() {\n              override fun contentType(): MediaType? = null\n\n              override fun writeTo(sink: BufferedSink) {\n                try {\n                  Thread.sleep(requestBodyDelay)\n                  sink.writeUtf8(\"abc\")\n                } catch (e: InterruptedException) {\n                  throw InterruptedIOException()\n                }\n              }\n            },\n          ).build(),\n      )\n\n    // Create a response with artificial delays.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(responseHeadersStartDelay, TimeUnit.MILLISECONDS)\n        .bodyDelay(responseBodyStartDelay, TimeUnit.MILLISECONDS)\n        .throttleBody(5, responseBodyEndDelay, TimeUnit.MILLISECONDS)\n        .body(\"fghijk\")\n        .build(),\n    )\n    call.execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"fghijk\")\n    }\n\n    // Confirm the events occur when expected.\n    eventRecorder.takeEvent(CallStart::class.java, 0L)\n    eventRecorder.takeEvent(ConnectionAcquired::class.java, applicationInterceptorDelay)\n    eventRecorder.takeEvent(RequestHeadersStart::class.java, networkInterceptorDelay)\n    eventRecorder.takeEvent(RequestHeadersEnd::class.java, 0L)\n    eventRecorder.takeEvent(RequestBodyStart::class.java, 0L)\n    eventRecorder.takeEvent(RequestBodyEnd::class.java, requestBodyDelay)\n    eventRecorder.takeEvent(ResponseHeadersStart::class.java, responseHeadersStartDelay)\n    eventRecorder.takeEvent(ResponseHeadersEnd::class.java, 0L)\n    eventRecorder.takeEvent(FollowUpDecision::class.java, 0L)\n    eventRecorder.takeEvent(ResponseBodyStart::class.java, responseBodyStartDelay)\n    eventRecorder.takeEvent(ResponseBodyEnd::class.java, responseBodyEndDelay)\n    eventRecorder.takeEvent(ConnectionReleased::class.java, 0L)\n    eventRecorder.takeEvent(CallEnd::class.java, 0L)\n  }\n\n  private fun enableTlsWithTunnel() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n\n  @Test\n  fun redirectUsingSameConnectionEventSequence() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .addHeader(\"Location: /foo\")\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    val call = client.newCallWithListener(Request.Builder().url(server.url(\"/\")).build())\n    call.execute()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n    assertThat(eventRecorder.findEvent<FollowUpDecision>()).all {\n      prop(FollowUpDecision::nextRequest).isNotNull()\n    }\n  }\n\n  @Test\n  fun redirectUsingNewConnectionEventSequence() {\n    val otherServer = MockWebServer()\n    otherServer.start()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .addHeader(\"Location: \" + otherServer.url(\"/foo\"))\n        .build(),\n    )\n    otherServer.enqueue(MockResponse())\n    val call = client.newCallWithListener(Request.Builder().url(server.url(\"/\")).build())\n    call.execute()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n    assertThat(eventRecorder.findEvent<FollowUpDecision>()).all {\n      prop(FollowUpDecision::nextRequest).isNotNull()\n    }\n    otherServer.close()\n  }\n\n  @Test\n  fun applicationInterceptorProceedsMultipleTimes() {\n    server.enqueue(MockResponse.Builder().body(\"a\").build())\n    server.enqueue(MockResponse.Builder().body(\"b\").build())\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            chain!!\n              .proceed(chain.request())\n              .use { a -> assertThat(a.body.string()).isEqualTo(\"a\") }\n            chain.proceed(chain.request())\n          },\n        ).build()\n    val call = client.newCallWithListener(Request.Builder().url(server.url(\"/\")).build())\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"b\")\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun applicationInterceptorShortCircuit() {\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            Response\n              .Builder()\n              .request(chain!!.request())\n              .protocol(Protocol.HTTP_1_1)\n              .code(200)\n              .message(\"OK\")\n              .body(\"a\".toResponseBody(null))\n              .build()\n          },\n        ).build()\n    val call = client.newCallWithListener(Request.Builder().url(server.url(\"/\")).build())\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"a\")\n    assertThat(eventRecorder.recordedEventTypes())\n      .containsExactly(CallStart::class, CallEnd::class)\n  }\n\n  /** Response headers start, then the entire request body, then response headers end.  */\n  @Test\n  fun expectContinueStartsResponseHeadersEarly() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .add100Continue()\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Expect\", \"100-continue\")\n        .post(\"abc\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    val call = client.newCallWithListener(request)\n    call.execute()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      RequestBodyStart::class,\n      RequestBodyEnd::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun timeToFirstByteGapBetweenResponseHeaderStartAndEnd() {\n    val responseHeadersStartDelay = 250L\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .add100Continue()\n        .headersDelay(responseHeadersStartDelay, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Expect\", \"100-continue\")\n        .post(\"abc\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    val call = client.newCallWithListener(request)\n    call\n      .execute()\n      .use { response -> assertThat(response.body.string()).isEqualTo(\"\") }\n    eventRecorder.removeUpToEvent<ResponseHeadersStart>()\n    eventRecorder.takeEvent(RequestBodyStart::class.java, 0L)\n    eventRecorder.takeEvent(RequestBodyEnd::class.java, 0L)\n    eventRecorder.takeEvent(ResponseHeadersEnd::class.java, responseHeadersStartDelay)\n  }\n\n  @Test\n  fun cacheMiss() {\n    enableCache()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      CacheMiss::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun conditionalCache() {\n    enableCache()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"ETag\", \"v1\")\n        .body(\"abc\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .build(),\n    )\n    var call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    var response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.close()\n    eventRecorder.clearAllEvents()\n    call = call.cloneWithListener()\n    response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      CacheConditionalHit::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      CacheHit::class,\n      FollowUpDecision::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun conditionalCacheMiss() {\n    enableCache()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"ETag: v1\")\n        .body(\"abc\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_OK)\n        .addHeader(\"ETag: v2\")\n        .body(\"abd\")\n        .build(),\n    )\n    var call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    var response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    response.close()\n    eventRecorder.clearAllEvents()\n    call = call.cloneWithListener()\n    response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abd\")\n    response.close()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      CacheConditionalHit::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      CacheMiss::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun satisfactionFailure() {\n    enableCache()\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .cacheControl(CacheControl.FORCE_CACHE)\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(504)\n    response.close()\n    assertThat(eventRecorder.recordedEventTypes())\n      .containsExactly(\n        CallStart::class,\n        SatisfactionFailure::class,\n        FollowUpDecision::class,\n        CallEnd::class,\n      )\n    assertThat(eventRecorder.findEvent<FollowUpDecision>()).all {\n      prop(FollowUpDecision::nextRequest).isNull()\n    }\n  }\n\n  @Test\n  fun cacheHit() {\n    enableCache()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .addHeader(\"cache-control: public, max-age=300\")\n        .build(),\n    )\n    var call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    var response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.close()\n    eventRecorder.clearAllEvents()\n    call = call.cloneWithListener()\n    response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.close()\n    assertThat(eventRecorder.recordedEventTypes())\n      .containsExactly(\n        CallStart::class,\n        CacheHit::class,\n        FollowUpDecision::class,\n        CallEnd::class,\n      )\n  }\n\n  /** Make sure we didn't mess up our special case for [EventListener.NONE]. */\n  @Test\n  fun eventListenerPlusNoneAggregation() {\n    val a = EventRecorder(enforceOrder = false)\n    val aPlusNone = a.eventListener + EventListener.NONE\n\n    aPlusNone.callStart(FailingCall())\n    assertThat(a.takeEvent()).isInstanceOf<CallStart>()\n    assertThat(a.eventSequence).isEmpty()\n  }\n\n  /** Make sure we didn't mess up our special case for [EventListener.NONE]. */\n  @Test\n  fun nonePlusEventListenerAggregation() {\n    val a = EventRecorder(enforceOrder = false)\n    val nonePlusA = EventListener.NONE + a.eventListener\n\n    nonePlusA.callStart(FailingCall())\n    assertThat(a.takeEvent()).isInstanceOf<CallStart>()\n    assertThat(a.eventSequence).isEmpty()\n  }\n\n  /** Make sure we didn't mess up our special case for combining aggregates. */\n  @Test\n  fun moreThanTwoAggregation() {\n    val a = EventRecorder(enforceOrder = false)\n    val b = EventRecorder(enforceOrder = false)\n    val c = EventRecorder(enforceOrder = false)\n    val d = EventRecorder(enforceOrder = false)\n\n    val abcd = (a.eventListener + b.eventListener) + (c.eventListener + d.eventListener)\n    abcd.callStart(FailingCall())\n\n    assertThat(a.takeEvent()).isInstanceOf<CallStart>()\n    assertThat(a.eventSequence).isEmpty()\n    assertThat(b.takeEvent()).isInstanceOf<CallStart>()\n    assertThat(b.eventSequence).isEmpty()\n    assertThat(c.takeEvent()).isInstanceOf<CallStart>()\n    assertThat(c.eventSequence).isEmpty()\n    assertThat(d.takeEvent()).isInstanceOf<CallStart>()\n    assertThat(d.eventSequence).isEmpty()\n  }\n\n  /** Reflectively call every event function to confirm it is correctly forwarded. */\n  @Test\n  fun aggregateEventListenerIsComplete() {\n    val sampleValues = sampleValuesMap()\n\n    val solo = EventRecorder(enforceOrder = false)\n    val left = EventRecorder(enforceOrder = false)\n    val right = EventRecorder(enforceOrder = false)\n    val composite = left.eventListener + right.eventListener\n\n    for (method in EventListener::class.java.declaredMethods) {\n      if (method.name == \"plus\") continue\n\n      val args =\n        method.parameters\n          .map { sampleValues[it.type] ?: error(\"no sample value for ${it.type}\") }\n          .toTypedArray()\n\n      method.invoke(solo.eventListener, *args)\n      method.invoke(composite, *args)\n\n      val expectedEvent = solo.takeEvent()\n      assertThat(solo.eventSequence).isEmpty()\n\n      assertThat(left.takeEvent()::class).isEqualTo(expectedEvent::class)\n      assertThat(left.eventSequence).isEmpty()\n\n      assertThat(right.takeEvent()::class).isEqualTo(expectedEvent::class)\n      assertThat(right.eventSequence).isEmpty()\n    }\n  }\n\n  /** Listeners added with [Call.addEventListener] don't exist on clones of that call. */\n  @Test\n  fun clonedCallDoesNotHaveAddedEventListeners() {\n    assumeTrue(listenerInstalledOn != ListenerInstalledOn.Client)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val clone = call.clone() // Not cloneWithListener.\n\n    val response = clone.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.body.close()\n    assertThat(eventRecorder.recordedEventTypes()).isEmpty()\n  }\n\n  /** Listeners added with [OkHttpClient.Builder.eventListener] are also added to clones. */\n  @Test\n  fun clonedCallHasClientEventListeners() {\n    assumeTrue(listenerInstalledOn == ListenerInstalledOn.Client)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call =\n      client.newCallWithListener(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val clone = call.clone() // Not cloneWithListener().\n\n    val response = clone.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    response.body.close()\n    assertThat(eventRecorder.recordedEventTypes()).isNotEmpty()\n  }\n\n  /**\n   * Returns a map with sample values for each possible parameter of an [EventListener] function\n   * parameter.\n   */\n  private fun sampleValuesMap(): Map<Class<*>, Any> {\n    TestValueFactory().use { factory ->\n      val address = factory.newAddress(\"a\")\n      val route = factory.newRoute(address)\n      val pool = factory.newConnectionPool()\n      val url = \"https://example.com/\".toHttpUrl()\n      val request = Request(url = url)\n      val response =\n        Response\n          .Builder()\n          .request(request)\n          .protocol(Protocol.HTTP_1_1)\n          .code(200)\n          .message(\"OK\")\n          .build()\n      val handshake =\n        Handshake.get(\n          tlsVersion = TlsVersion.TLS_1_3,\n          cipherSuite = CipherSuite.TLS_AES_128_GCM_SHA256,\n          peerCertificates = listOf(),\n          localCertificates = listOf(),\n        )\n\n      return mapOf(\n        Boolean::class.java to false,\n        Call::class.java to FailingCall(),\n        Connection::class.java to factory.newConnection(pool, route),\n        Dispatcher::class.java to Dispatcher(),\n        Handshake::class.java to handshake,\n        HttpUrl::class.java to url,\n        IOException::class.java to IOException(\"boom\"),\n        InetSocketAddress::class.java to InetSocketAddress.createUnresolved(\"localhost\", 80),\n        List::class.java to listOf<Any?>(),\n        Long::class.java to 123L,\n        Protocol::class.java to Protocol.HTTP_2,\n        Proxy::class.java to Proxy.NO_PROXY,\n        Request::class.java to request,\n        Response::class.java to response,\n        String::class.java to \"hello\",\n      )\n    }\n  }\n\n  private fun enableCache(): Cache? {\n    cache = makeCache()\n    client = client.newBuilder().cache(cache).build()\n    return cache\n  }\n\n  private fun makeCache(): Cache {\n    val cacheDir = File.createTempFile(\"cache-\", \".dir\")\n    cacheDir.delete()\n    return Cache(cacheDir, (1024 * 1024).toLong())\n  }\n\n  private fun OkHttpClient.newCallWithListener(request: Request): Call =\n    newCall(request)\n      .apply {\n        addEventRecorder(eventRecorder)\n      }\n\n  private fun Call.cloneWithListener(): Call =\n    clone()\n      .apply {\n        addEventRecorder(eventRecorder)\n      }\n\n  private fun Call.addEventRecorder(eventRecorder: EventRecorder) {\n    when (listenerInstalledOn) {\n      ListenerInstalledOn.Call -> {\n        addEventListener(eventRecorder.eventListener)\n      }\n\n      ListenerInstalledOn.Relay -> {\n        addEventListener(EventListenerRelay(this, eventRecorder).eventListener)\n      }\n\n      ListenerInstalledOn.Client -> {} // listener is added elsewhere.\n    }\n  }\n\n  enum class ListenerInstalledOn {\n    Client,\n    Call,\n    Relay,\n  }\n\n  companion object {\n    val anyResponse = CoreMatchers.any(Response::class.java)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/FakeRoutePlanner.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.util.concurrent.LinkedBlockingDeque\nimport okhttp3.internal.concurrent.TaskFaker\nimport okhttp3.internal.connection.RealConnection\nimport okhttp3.internal.connection.RoutePlanner\nimport okhttp3.internal.connection.RoutePlanner.ConnectResult\n\nclass FakeRoutePlanner(\n  val factory: TestValueFactory = TestValueFactory(),\n  val taskFaker: TaskFaker = factory.taskFaker,\n) : RoutePlanner,\n  Closeable {\n  val pool = factory.newConnectionPool()\n  val events = LinkedBlockingDeque<String>()\n  var canceled = false\n  var autoGeneratePlans = false\n  var defaultConnectionIdleAtNanos = Long.MAX_VALUE\n  private var nextPlanId = 0\n  private var nextPlanIndex = 0\n  val plans = mutableListOf<FakePlan>()\n\n  override val deferredPlans = ArrayDeque<RoutePlanner.Plan>()\n\n  override val address = factory.newAddress(\"example.com\")\n\n  fun addPlan(): FakePlan =\n    FakePlan(nextPlanId++).also {\n      plans += it\n    }\n\n  override fun isCanceled() = canceled\n\n  override fun plan(): FakePlan {\n    // Return deferred plans preferentially. These don't require addPlan().\n    if (deferredPlans.isNotEmpty()) return deferredPlans.removeFirst() as FakePlan\n\n    if (nextPlanIndex >= plans.size && autoGeneratePlans) addPlan()\n\n    require(nextPlanIndex < plans.size) {\n      \"not enough plans! call addPlan() or set autoGeneratePlans=true in the test to set this up\"\n    }\n    val result = plans[nextPlanIndex++]\n    events += \"take plan ${result.id}\"\n\n    if (result.yieldBeforePlanReturns) {\n      taskFaker.yield()\n    }\n\n    val planningThrowable = result.planningThrowable\n    if (planningThrowable != null) throw planningThrowable\n\n    return result\n  }\n\n  override fun hasNext(failedConnection: RealConnection?): Boolean =\n    deferredPlans.isNotEmpty() || nextPlanIndex < plans.size || autoGeneratePlans\n\n  override fun sameHostAndPort(url: HttpUrl): Boolean = url.host == address.url.host && url.port == address.url.port\n\n  override fun close() {\n    factory.close()\n  }\n\n  inner class FakePlan(\n    val id: Int,\n  ) : RoutePlanner.Plan {\n    var planningThrowable: Throwable? = null\n    var canceled = false\n    var connectState = ConnectState.READY\n    val connection =\n      factory.newConnection(\n        pool = pool,\n        route = factory.newRoute(address),\n        idleAtNanos = defaultConnectionIdleAtNanos,\n      )\n    var retry: FakePlan? = null\n    var retryTaken = false\n    var yieldBeforePlanReturns = false\n\n    override val isReady: Boolean\n      get() = connectState == ConnectState.TLS_CONNECTED\n\n    var tcpConnectDelayNanos = 0L\n    var tcpConnectThrowable: Throwable? = null\n    var yieldBeforeTcpConnectReturns = false\n    var connectTcpNextPlan: FakePlan? = null\n    var tlsConnectDelayNanos = 0L\n    var tlsConnectThrowable: Throwable? = null\n    var connectTlsNextPlan: FakePlan? = null\n\n    fun createRetry(): FakePlan {\n      check(retry == null)\n      return FakePlan(nextPlanId++)\n        .also {\n          retry = it\n        }\n    }\n\n    fun createConnectTcpNextPlan(): FakePlan {\n      check(connectTcpNextPlan == null)\n      return FakePlan(nextPlanId++)\n        .also {\n          connectTcpNextPlan = it\n        }\n    }\n\n    fun createConnectTlsNextPlan(): FakePlan {\n      check(connectTlsNextPlan == null)\n      return FakePlan(nextPlanId++)\n        .also {\n          connectTlsNextPlan = it\n        }\n    }\n\n    override fun connectTcp(): ConnectResult {\n      check(connectState == ConnectState.READY)\n      events += \"plan $id TCP connecting...\"\n\n      taskFaker.sleep(tcpConnectDelayNanos)\n\n      if (yieldBeforeTcpConnectReturns) {\n        taskFaker.yield()\n      }\n\n      return when {\n        tcpConnectThrowable != null -> {\n          events += \"plan $id TCP connect failed\"\n          ConnectResult(this, nextPlan = connectTcpNextPlan, throwable = tcpConnectThrowable)\n        }\n\n        canceled -> {\n          events += \"plan $id TCP connect canceled\"\n          ConnectResult(this, nextPlan = connectTcpNextPlan, throwable = IOException(\"canceled\"))\n        }\n\n        connectTcpNextPlan != null -> {\n          events += \"plan $id needs follow-up\"\n          ConnectResult(this, nextPlan = connectTcpNextPlan)\n        }\n\n        else -> {\n          events += \"plan $id TCP connected\"\n          connectState = ConnectState.TCP_CONNECTED\n          ConnectResult(this)\n        }\n      }\n    }\n\n    override fun connectTlsEtc(): ConnectResult {\n      check(connectState == ConnectState.TCP_CONNECTED)\n      events += \"plan $id TLS connecting...\"\n\n      taskFaker.sleep(tlsConnectDelayNanos)\n\n      return when {\n        tlsConnectThrowable != null -> {\n          events += \"plan $id TLS connect failed\"\n          ConnectResult(this, nextPlan = connectTlsNextPlan, throwable = tlsConnectThrowable)\n        }\n\n        canceled -> {\n          events += \"plan $id TLS connect canceled\"\n          ConnectResult(this, nextPlan = connectTlsNextPlan, throwable = IOException(\"canceled\"))\n        }\n\n        connectTlsNextPlan != null -> {\n          events += \"plan $id needs follow-up\"\n          ConnectResult(this, nextPlan = connectTlsNextPlan)\n        }\n\n        else -> {\n          events += \"plan $id TLS connected\"\n          connectState = ConnectState.TLS_CONNECTED\n          ConnectResult(this)\n        }\n      }\n    }\n\n    override fun handleSuccess() = connection\n\n    override fun cancel() {\n      events += \"plan $id cancel\"\n      canceled = true\n    }\n\n    override fun retry(): FakePlan? {\n      check(!retryTaken)\n      retryTaken = true\n      return retry\n    }\n  }\n\n  enum class ConnectState {\n    READY,\n    TCP_CONNECTED,\n    TLS_CONNECTED,\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/FallbackTestClientSocketFactory.kt",
    "content": "/*\n * Copyright 2014 Square Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.FallbackTestClientSocketFactory.Companion.TLS_FALLBACK_SCSV\n\n/**\n * An SSLSocketFactory that delegates calls. Sockets created by the delegate are wrapped with ones\n * that will not accept the [TLS_FALLBACK_SCSV] cipher, thus bypassing server-side fallback\n * checks on platforms that support it. Unfortunately this wrapping will disable any\n * reflection-based calls to SSLSocket from Platform.\n */\nclass FallbackTestClientSocketFactory(\n  delegate: SSLSocketFactory,\n) : DelegatingSSLSocketFactory(delegate) {\n  override fun configureSocket(sslSocket: SSLSocket): SSLSocket = TlsFallbackScsvDisabledSSLSocket(sslSocket)\n\n  private class TlsFallbackScsvDisabledSSLSocket(\n    socket: SSLSocket,\n  ) : DelegatingSSLSocket(socket) {\n    override fun setEnabledCipherSuites(suites: Array<String>) {\n      val enabledCipherSuites = mutableListOf<String>()\n      for (suite in suites) {\n        if (suite != TLS_FALLBACK_SCSV) {\n          enabledCipherSuites.add(suite)\n        }\n      }\n      delegate!!.enabledCipherSuites = enabledCipherSuites.toTypedArray<String>()\n    }\n  }\n\n  companion object {\n    /**\n     * The cipher suite used during TLS connection fallback to indicate a fallback. See\n     * https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00\n     */\n    const val TLS_FALLBACK_SCSV = \"TLS_FALLBACK_SCSV\"\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/FastFallbackTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.hasMessage\nimport assertk.assertions.hasSize\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport java.net.Inet4Address\nimport java.net.Inet6Address\nimport java.net.InetAddress\nimport java.net.Socket\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport javax.net.SocketFactory\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.CloseStream\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectFailed\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.internal.http2.ErrorCode\nimport okhttp3.testing.Flaky\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junitpioneer.jupiter.RetryingTest\nimport org.opentest4j.TestAbortedException\n\n/**\n * This test binds two different web servers (IPv4 and IPv6) to the same port, but on different\n * local IP addresses. Requests made to `127.0.0.1` will reach the IPv4 server, and requests made to\n * `::1` will reach the IPv6 server.\n *\n * By orchestrating two different servers with the same port but different IP addresses, we can\n * test what OkHttp does when both are reachable, or if only one is reachable.\n *\n * This test only runs on host machines that have both IPv4 and IPv6 addresses for localhost.\n */\n@Timeout(30)\nclass FastFallbackTest {\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  // Don't use JUnit 5 test rules for these; otherwise we can't bind them to a single local IP.\n  private lateinit var localhostIpv4: InetAddress\n  private lateinit var localhostIpv6: InetAddress\n  private lateinit var serverIpv4: MockWebServer\n  private lateinit var serverIpv6: MockWebServer\n\n  private val eventRecorder = EventRecorder()\n  private lateinit var client: OkHttpClient\n  private lateinit var url: HttpUrl\n\n  /**\n   * This is mutable and order matters. By default, it contains [IPv4, IPv6]. Tests may manipulate\n   * it to prefer IPv6.\n   */\n  private var dnsResults = listOf<InetAddress>()\n\n  @BeforeEach\n  internal fun setUp() {\n    val inetAddresses = InetAddress.getAllByName(\"localhost\")\n    localhostIpv4 = inetAddresses.firstOrNull { it is Inet4Address }\n      ?: throw TestAbortedException()\n    localhostIpv6 = inetAddresses.firstOrNull { it is Inet6Address }\n      ?: throw TestAbortedException()\n\n    serverIpv4 = MockWebServer()\n    serverIpv4.start(localhostIpv4, 0) // Pick any available port.\n\n    serverIpv6 = MockWebServer()\n    serverIpv6.start(localhostIpv6, serverIpv4.port) // Pick the same port as the IPv4 server.\n\n    dnsResults =\n      listOf(\n        localhostIpv4,\n        localhostIpv6,\n      )\n\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n        .connectTimeout(60, TimeUnit.SECONDS) // Deliberately exacerbate slow fallbacks.\n        .dns { dnsResults }\n        .fastFallback(true)\n        .build()\n    url =\n      serverIpv4\n        .url(\"/\")\n        .newBuilder()\n        .host(\"localhost\")\n        .build()\n  }\n\n  @AfterEach\n  internal fun tearDown() {\n    serverIpv4.close()\n    serverIpv6.close()\n  }\n\n  @Test\n  fun callIpv6FirstEvenWhenIpv4IpIsListedFirst() {\n    dnsResults =\n      listOf(\n        localhostIpv4,\n        localhostIpv6,\n      )\n    serverIpv4.enqueue(\n      MockResponse(body = \"unexpected call to IPv4\"),\n    )\n    serverIpv6.enqueue(\n      MockResponse(body = \"hello from IPv6\"),\n    )\n\n    val call = client.newCall(Request(url))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"hello from IPv6\")\n\n    // In the process we made one successful connection attempt.\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectStart::class }).hasSize(1)\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectFailed::class }).hasSize(0)\n  }\n\n  @Test\n  fun callIpv6WhenBothServersAreReachable() {\n    // Flip DNS results to prefer IPv6.\n    dnsResults =\n      listOf(\n        localhostIpv6,\n        localhostIpv4,\n      )\n    serverIpv4.enqueue(\n      MockResponse(body = \"unexpected call to IPv4\"),\n    )\n    serverIpv6.enqueue(\n      MockResponse(body = \"hello from IPv6\"),\n    )\n\n    val call = client.newCall(Request(url))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"hello from IPv6\")\n\n    // In the process we made one successful connection attempt.\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectStart::class }).hasSize(1)\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectFailed::class }).hasSize(0)\n  }\n\n  @Test\n  fun reachesIpv4WhenIpv6IsDown() {\n    serverIpv6.close()\n    serverIpv4.enqueue(\n      MockResponse(body = \"hello from IPv4\"),\n    )\n\n    val call = client.newCall(Request(url))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"hello from IPv4\")\n\n    // In the process we made one successful connection attempt.\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectStart::class }).hasSize(2)\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectFailed::class }).hasSize(1)\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectEnd::class }).hasSize(1)\n  }\n\n  @Test\n  fun reachesIpv6WhenIpv4IsDown() {\n    serverIpv4.close()\n    serverIpv6.enqueue(\n      MockResponse(body = \"hello from IPv6\"),\n    )\n\n    val call = client.newCall(Request(url))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"hello from IPv6\")\n\n    // In the process we made two connection attempts including one failure.\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectStart::class }).hasSize(1)\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectEnd::class }).hasSize(1)\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectFailed::class }).hasSize(0)\n  }\n\n  @Test\n  fun failsWhenBothServersAreDown() {\n    serverIpv4.close()\n    serverIpv6.close()\n\n    val call = client.newCall(Request(url))\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n\n    // In the process we made two unsuccessful connection attempts.\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectStart::class }).hasSize(2)\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectFailed::class }).hasSize(2)\n  }\n\n  @RetryingTest(5)\n  @Flaky\n  fun reachesIpv4AfterUnreachableIpv6Address() {\n    dnsResults =\n      listOf(\n        TestUtil.UNREACHABLE_ADDRESS_IPV6.address,\n        localhostIpv4,\n      )\n    serverIpv6.close()\n    serverIpv4.enqueue(\n      MockResponse(body = \"hello from IPv4\"),\n    )\n\n    val call = client.newCall(Request(url))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"hello from IPv4\")\n\n    // In the process we made two connection attempts including one failure.\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectStart::class }).hasSize(2)\n    assertThat(eventRecorder.recordedEventTypes().filter { it == ConnectFailed::class }).hasSize(1)\n  }\n\n  @Test\n  fun timesOutWithFastFallbackDisabled() {\n    dnsResults =\n      listOf(\n        TestUtil.UNREACHABLE_ADDRESS_IPV4.address,\n        localhostIpv6,\n      )\n    serverIpv4.close()\n    serverIpv6.enqueue(\n      MockResponse(body = \"hello from IPv6\"),\n    )\n\n    client =\n      client\n        .newBuilder()\n        .fastFallback(false)\n        .callTimeout(1_000, TimeUnit.MILLISECONDS)\n        .build()\n    val call = client.newCall(Request(url))\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"timeout\")\n    }\n  }\n\n  /**\n   * This test reproduces a crash where OkHttp attempted to use a deferred connection when the call\n   * already had a healthy connection. It sets up a deferred connection by stalling the IPv6\n   * connect, and it sets up a same-connection retry with [ErrorCode.REFUSED_STREAM].\n   *\n   * https://github.com/square/okhttp/pull/7190\n   */\n  @Test\n  fun preferCallConnectionOverDeferredConnection() {\n    // Make sure we have enough connection options to permit retries.\n    dnsResults =\n      listOf(\n        localhostIpv4,\n        localhostIpv6,\n        TestUtil.UNREACHABLE_ADDRESS_IPV4.address,\n      )\n    serverIpv4.protocols = listOf(Protocol.H2_PRIOR_KNOWLEDGE)\n    serverIpv6.protocols = listOf(Protocol.H2_PRIOR_KNOWLEDGE)\n\n    // Yield the first IP address so the second IP address completes first.\n    val firstConnectLatch = CountDownLatch(1)\n    val socketFactory =\n      object : DelegatingSocketFactory(SocketFactory.getDefault()) {\n        var first = true\n\n        override fun createSocket(): Socket {\n          if (first) {\n            first = false\n            firstConnectLatch.await()\n          }\n          return super.createSocket()\n        }\n      }\n\n    client =\n      client\n        .newBuilder()\n        .protocols(listOf(Protocol.H2_PRIOR_KNOWLEDGE))\n        .socketFactory(socketFactory)\n        .addNetworkInterceptor(\n          Interceptor { chain ->\n            try {\n              chain.proceed(chain.request())\n            } finally {\n              firstConnectLatch.countDown()\n            }\n          },\n        ).build()\n\n    // Set up a same-connection retry.\n    serverIpv4.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    serverIpv4.enqueue(\n      MockResponse(body = \"this was the 2nd request on IPv4\"),\n    )\n    serverIpv6.enqueue(\n      MockResponse(body = \"unexpected call to IPv6\"),\n    )\n\n    // Confirm the retry succeeds on the same connection.\n    val call = client.newCall(Request(url))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"this was the 2nd request on IPv4\")\n    assertThat(serverIpv4.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(serverIpv4.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/FormBodyTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport java.nio.charset.StandardCharsets\nimport okio.Buffer\nimport org.junit.jupiter.api.Test\n\nclass FormBodyTest {\n  @Test\n  fun urlEncoding() {\n    val body =\n      FormBody\n        .Builder()\n        .add(\"a+=& b\", \"c+=& d\")\n        .add(\"space, the\", \"final frontier\")\n        .add(\"%25\", \"%25\")\n        .build()\n    assertThat(body.size).isEqualTo(3)\n    assertThat(body.encodedName(0)).isEqualTo(\"a%2B%3D%26+b\")\n    assertThat(body.encodedName(1)).isEqualTo(\"space%2C+the\")\n    assertThat(body.encodedName(2)).isEqualTo(\"%2525\")\n    assertThat(body.name(0)).isEqualTo(\"a+=& b\")\n    assertThat(body.name(1)).isEqualTo(\"space, the\")\n    assertThat(body.name(2)).isEqualTo(\"%25\")\n    assertThat(body.encodedValue(0)).isEqualTo(\"c%2B%3D%26+d\")\n    assertThat(body.encodedValue(1)).isEqualTo(\"final+frontier\")\n    assertThat(body.encodedValue(2)).isEqualTo(\"%2525\")\n    assertThat(body.value(0)).isEqualTo(\"c+=& d\")\n    assertThat(body.value(1)).isEqualTo(\"final frontier\")\n    assertThat(body.value(2)).isEqualTo(\"%25\")\n    assertThat(body.contentType().toString()).isEqualTo(\n      \"application/x-www-form-urlencoded\",\n    )\n    val expected = \"a%2B%3D%26+b=c%2B%3D%26+d&space%2C+the=final+frontier&%2525=%2525\"\n    assertThat(body.contentLength()).isEqualTo(expected.length.toLong())\n    val out = Buffer()\n    body.writeTo(out)\n    assertThat(out.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun addEncoded() {\n    val body =\n      FormBody\n        .Builder()\n        .addEncoded(\"a+=& b\", \"c+=& d\")\n        .addEncoded(\"e+=& f\", \"g+=& h\")\n        .addEncoded(\"%25\", \"%25\")\n        .build()\n    val expected = \"a+%3D%26+b=c+%3D%26+d&e+%3D%26+f=g+%3D%26+h&%25=%25\"\n    val out = Buffer()\n    body.writeTo(out)\n    assertThat(out.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun encodedPair() {\n    val body =\n      FormBody\n        .Builder()\n        .add(\"sim\", \"ple\")\n        .build()\n    val expected = \"sim=ple\"\n    assertThat(body.contentLength()).isEqualTo(expected.length.toLong())\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun encodeMultiplePairs() {\n    val body =\n      FormBody\n        .Builder()\n        .add(\"sim\", \"ple\")\n        .add(\"hey\", \"there\")\n        .add(\"help\", \"me\")\n        .build()\n    val expected = \"sim=ple&hey=there&help=me\"\n    assertThat(body.contentLength()).isEqualTo(expected.length.toLong())\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun buildEmptyForm() {\n    val body = FormBody.Builder().build()\n    val expected = \"\"\n    assertThat(body.contentLength()).isEqualTo(expected.length.toLong())\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun characterEncoding() {\n    // Browsers convert '\\u0000' to '%EF%BF%BD'.\n    assertThat(formEncode(0)).isEqualTo(\"%00\")\n    assertThat(formEncode(1)).isEqualTo(\"%01\")\n    assertThat(formEncode(2)).isEqualTo(\"%02\")\n    assertThat(formEncode(3)).isEqualTo(\"%03\")\n    assertThat(formEncode(4)).isEqualTo(\"%04\")\n    assertThat(formEncode(5)).isEqualTo(\"%05\")\n    assertThat(formEncode(6)).isEqualTo(\"%06\")\n    assertThat(formEncode(7)).isEqualTo(\"%07\")\n    assertThat(formEncode(8)).isEqualTo(\"%08\")\n    assertThat(formEncode(9)).isEqualTo(\"%09\")\n    // Browsers convert '\\n' to '\\r\\n'\n    assertThat(formEncode(10)).isEqualTo(\"%0A\")\n    assertThat(formEncode(11)).isEqualTo(\"%0B\")\n    assertThat(formEncode(12)).isEqualTo(\"%0C\")\n    // Browsers convert '\\r' to '\\r\\n'\n    assertThat(formEncode(13)).isEqualTo(\"%0D\")\n    assertThat(formEncode(14)).isEqualTo(\"%0E\")\n    assertThat(formEncode(15)).isEqualTo(\"%0F\")\n    assertThat(formEncode(16)).isEqualTo(\"%10\")\n    assertThat(formEncode(17)).isEqualTo(\"%11\")\n    assertThat(formEncode(18)).isEqualTo(\"%12\")\n    assertThat(formEncode(19)).isEqualTo(\"%13\")\n    assertThat(formEncode(20)).isEqualTo(\"%14\")\n    assertThat(formEncode(21)).isEqualTo(\"%15\")\n    assertThat(formEncode(22)).isEqualTo(\"%16\")\n    assertThat(formEncode(23)).isEqualTo(\"%17\")\n    assertThat(formEncode(24)).isEqualTo(\"%18\")\n    assertThat(formEncode(25)).isEqualTo(\"%19\")\n    assertThat(formEncode(26)).isEqualTo(\"%1A\")\n    assertThat(formEncode(27)).isEqualTo(\"%1B\")\n    assertThat(formEncode(28)).isEqualTo(\"%1C\")\n    assertThat(formEncode(29)).isEqualTo(\"%1D\")\n    assertThat(formEncode(30)).isEqualTo(\"%1E\")\n    assertThat(formEncode(31)).isEqualTo(\"%1F\")\n    // Browsers use '+' for space.\n    assertThat(formEncode(32)).isEqualTo(\"+\")\n    assertThat(formEncode(33)).isEqualTo(\"%21\")\n    assertThat(formEncode(34)).isEqualTo(\"%22\")\n    assertThat(formEncode(35)).isEqualTo(\"%23\")\n    assertThat(formEncode(36)).isEqualTo(\"%24\")\n    assertThat(formEncode(37)).isEqualTo(\"%25\")\n    assertThat(formEncode(38)).isEqualTo(\"%26\")\n    assertThat(formEncode(39)).isEqualTo(\"%27\")\n    assertThat(formEncode(40)).isEqualTo(\"%28\")\n    assertThat(formEncode(41)).isEqualTo(\"%29\")\n    assertThat(formEncode(42)).isEqualTo(\"*\")\n    assertThat(formEncode(43)).isEqualTo(\"%2B\")\n    assertThat(formEncode(44)).isEqualTo(\"%2C\")\n    assertThat(formEncode(45)).isEqualTo(\"-\")\n    assertThat(formEncode(46)).isEqualTo(\".\")\n    assertThat(formEncode(47)).isEqualTo(\"%2F\")\n    assertThat(formEncode(48)).isEqualTo(\"0\")\n    assertThat(formEncode(57)).isEqualTo(\"9\")\n    assertThat(formEncode(58)).isEqualTo(\"%3A\")\n    assertThat(formEncode(59)).isEqualTo(\"%3B\")\n    assertThat(formEncode(60)).isEqualTo(\"%3C\")\n    assertThat(formEncode(61)).isEqualTo(\"%3D\")\n    assertThat(formEncode(62)).isEqualTo(\"%3E\")\n    assertThat(formEncode(63)).isEqualTo(\"%3F\")\n    assertThat(formEncode(64)).isEqualTo(\"%40\")\n    assertThat(formEncode(65)).isEqualTo(\"A\")\n    assertThat(formEncode(90)).isEqualTo(\"Z\")\n    assertThat(formEncode(91)).isEqualTo(\"%5B\")\n    assertThat(formEncode(92)).isEqualTo(\"%5C\")\n    assertThat(formEncode(93)).isEqualTo(\"%5D\")\n    assertThat(formEncode(94)).isEqualTo(\"%5E\")\n    assertThat(formEncode(95)).isEqualTo(\"_\")\n    assertThat(formEncode(96)).isEqualTo(\"%60\")\n    assertThat(formEncode(97)).isEqualTo(\"a\")\n    assertThat(formEncode(122)).isEqualTo(\"z\")\n    assertThat(formEncode(123)).isEqualTo(\"%7B\")\n    assertThat(formEncode(124)).isEqualTo(\"%7C\")\n    assertThat(formEncode(125)).isEqualTo(\"%7D\")\n    assertThat(formEncode(126)).isEqualTo(\"%7E\")\n    assertThat(formEncode(127)).isEqualTo(\"%7F\")\n    assertThat(formEncode(128)).isEqualTo(\"%C2%80\")\n    assertThat(formEncode(255)).isEqualTo(\"%C3%BF\")\n  }\n\n  @Throws(IOException::class)\n  private fun formEncode(codePoint: Int): String {\n    // Wrap the codepoint with regular printable characters to prevent trimming.\n    val body =\n      FormBody\n        .Builder()\n        .add(\"a\", String(intArrayOf('b'.code, codePoint, 'c'.code), 0, 3))\n        .build()\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    buffer.skip(3) // Skip \"a=b\" prefix.\n    return buffer.readUtf8(buffer.size - 1) // Skip the \"c\" suffix.\n  }\n\n  @Test\n  fun manualCharset() {\n    val body =\n      FormBody\n        .Builder(StandardCharsets.ISO_8859_1)\n        .add(\"name\", \"Nicolás\")\n        .build()\n    val expected = \"name=Nicol%E1s\"\n    assertThat(body.contentLength()).isEqualTo(expected.length.toLong())\n    val out = Buffer()\n    body.writeTo(out)\n    assertThat(out.readUtf8()).isEqualTo(expected)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/HandshakeTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport java.io.IOException\nimport java.security.cert.Certificate\nimport kotlin.test.assertFailsWith\nimport okhttp3.Handshake.Companion.handshake\nimport okhttp3.tls.HeldCertificate\nimport org.junit.jupiter.api.Test\n\nclass HandshakeTest {\n  val serverRoot =\n    HeldCertificate\n      .Builder()\n      .certificateAuthority(1)\n      .build()\n  val serverIntermediate =\n    HeldCertificate\n      .Builder()\n      .certificateAuthority(0)\n      .signedBy(serverRoot)\n      .build()\n  val serverCertificate =\n    HeldCertificate\n      .Builder()\n      .signedBy(serverIntermediate)\n      .build()\n\n  @Test\n  fun createFromParts() {\n    val handshake =\n      Handshake.get(\n        tlsVersion = TlsVersion.TLS_1_3,\n        cipherSuite = CipherSuite.TLS_AES_128_GCM_SHA256,\n        peerCertificates = listOf(serverCertificate.certificate, serverIntermediate.certificate),\n        localCertificates = listOf(),\n      )\n\n    assertThat(handshake.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n    assertThat(handshake.cipherSuite).isEqualTo(CipherSuite.TLS_AES_128_GCM_SHA256)\n    assertThat(handshake.peerCertificates).containsExactly(\n      serverCertificate.certificate,\n      serverIntermediate.certificate,\n    )\n    assertThat(handshake.localPrincipal).isNull()\n    assertThat(handshake.peerPrincipal)\n      .isEqualTo(serverCertificate.certificate.subjectX500Principal)\n    assertThat(handshake.localCertificates).isEmpty()\n  }\n\n  @Test\n  fun createFromSslSession() {\n    val sslSession =\n      FakeSSLSession(\n        \"TLSv1.3\",\n        \"TLS_AES_128_GCM_SHA256\",\n        arrayOf(serverCertificate.certificate, serverIntermediate.certificate),\n        null,\n      )\n\n    val handshake = sslSession.handshake()\n\n    assertThat(handshake.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n    assertThat(handshake.cipherSuite).isEqualTo(CipherSuite.TLS_AES_128_GCM_SHA256)\n    assertThat(handshake.peerCertificates).containsExactly(\n      serverCertificate.certificate,\n      serverIntermediate.certificate,\n    )\n    assertThat(handshake.localPrincipal).isNull()\n    assertThat(handshake.peerPrincipal)\n      .isEqualTo(serverCertificate.certificate.subjectX500Principal)\n    assertThat(handshake.localCertificates).isEmpty()\n  }\n\n  @Test\n  fun sslWithNullNullNull() {\n    val sslSession =\n      FakeSSLSession(\n        \"TLSv1.3\",\n        \"SSL_NULL_WITH_NULL_NULL\",\n        arrayOf(serverCertificate.certificate, serverIntermediate.certificate),\n        null,\n      )\n\n    assertFailsWith<IOException> {\n      sslSession.handshake()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"cipherSuite == SSL_NULL_WITH_NULL_NULL\")\n    }\n  }\n\n  @Test\n  fun tlsWithNullNullNull() {\n    val sslSession =\n      FakeSSLSession(\n        \"TLSv1.3\",\n        \"TLS_NULL_WITH_NULL_NULL\",\n        arrayOf(serverCertificate.certificate, serverIntermediate.certificate),\n        null,\n      )\n\n    assertFailsWith<IOException> {\n      sslSession.handshake()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"cipherSuite == TLS_NULL_WITH_NULL_NULL\")\n    }\n  }\n\n  class FakeSSLSession(\n    private val protocol: String,\n    private val cipherSuite: String,\n    private val peerCertificates: Array<Certificate>?,\n    private val localCertificates: Array<Certificate>?,\n  ) : DelegatingSSLSession(null) {\n    override fun getProtocol() = protocol\n\n    override fun getCipherSuite() = cipherSuite\n\n    override fun getPeerCertificates() = peerCertificates\n\n    override fun getLocalCertificates() = localCertificates\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/HeadersChallengesTest.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport okhttp3.internal.http.parseChallenges\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\n\nclass HeadersChallengesTest {\n  /** See https://github.com/square/okhttp/issues/2780.  */\n  @Test fun testDigestChallengeWithStrictRfc2617Header() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\n          \"WWW-Authenticate\",\n          \"Digest realm=\\\"myrealm\\\", nonce=\\\"fjalskdflwejrlaskdfjlaskdjflaks\" +\n            \"jdflkasdf\\\", qop=\\\"auth\\\", stale=\\\"FALSE\\\"\",\n        ).build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"Digest\")\n    assertThat(challenges[0].realm).isEqualTo(\"myrealm\")\n    val expectedAuthParams = mutableMapOf<String, String>()\n    expectedAuthParams[\"realm\"] = \"myrealm\"\n    expectedAuthParams[\"nonce\"] = \"fjalskdflwejrlaskdfjlaskdjflaksjdflkasdf\"\n    expectedAuthParams[\"qop\"] = \"auth\"\n    expectedAuthParams[\"stale\"] = \"FALSE\"\n    assertThat(challenges[0].authParams).isEqualTo(expectedAuthParams)\n  }\n\n  @Test fun testDigestChallengeWithDifferentlyOrderedAuthParams() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\n          \"WWW-Authenticate\",\n          \"Digest qop=\\\"auth\\\", realm=\\\"myrealm\\\", nonce=\\\"fjalskdflwejrlask\" +\n            \"dfjlaskdjflaksjdflkasdf\\\", stale=\\\"FALSE\\\"\",\n        ).build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"Digest\")\n    assertThat(challenges[0].realm).isEqualTo(\"myrealm\")\n    val expectedAuthParams = mutableMapOf<String, String>()\n    expectedAuthParams[\"realm\"] = \"myrealm\"\n    expectedAuthParams[\"nonce\"] = \"fjalskdflwejrlaskdfjlaskdjflaksjdflkasdf\"\n    expectedAuthParams[\"qop\"] = \"auth\"\n    expectedAuthParams[\"stale\"] = \"FALSE\"\n    assertThat(challenges[0].authParams).isEqualTo(expectedAuthParams)\n  }\n\n  @Test fun testDigestChallengeWithDifferentlyOrderedAuthParams2() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\n          \"WWW-Authenticate\",\n          \"Digest qop=\\\"auth\\\", nonce=\\\"fjalskdflwejrlaskdfjlaskdjflaksjdflk\" +\n            \"asdf\\\", realm=\\\"myrealm\\\", stale=\\\"FALSE\\\"\",\n        ).build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"Digest\")\n    assertThat(challenges[0].realm).isEqualTo(\"myrealm\")\n    val expectedAuthParams = mutableMapOf<String, String>()\n    expectedAuthParams[\"realm\"] = \"myrealm\"\n    expectedAuthParams[\"nonce\"] = \"fjalskdflwejrlaskdfjlaskdjflaksjdflkasdf\"\n    expectedAuthParams[\"qop\"] = \"auth\"\n    expectedAuthParams[\"stale\"] = \"FALSE\"\n    assertThat(challenges[0].authParams).isEqualTo(expectedAuthParams)\n  }\n\n  @Test fun testDigestChallengeWithMissingRealm() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\n          \"WWW-Authenticate\",\n          \"Digest qop=\\\"auth\\\", underrealm=\\\"myrealm\\\", nonce=\\\"fjalskdflwej\" +\n            \"rlaskdfjlaskdjflaksjdflkasdf\\\", stale=\\\"FALSE\\\"\",\n        ).build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"Digest\")\n    assertThat(challenges[0].realm).isNull()\n    val expectedAuthParams = mutableMapOf<String, String>()\n    expectedAuthParams[\"underrealm\"] = \"myrealm\"\n    expectedAuthParams[\"nonce\"] = \"fjalskdflwejrlaskdfjlaskdjflaksjdflkasdf\"\n    expectedAuthParams[\"qop\"] = \"auth\"\n    expectedAuthParams[\"stale\"] = \"FALSE\"\n    assertThat(challenges[0].authParams).isEqualTo(expectedAuthParams)\n  }\n\n  @Test fun testDigestChallengeWithAdditionalSpaces() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\n          \"WWW-Authenticate\",\n          \"Digest qop=\\\"auth\\\",    realm=\\\"myrealm\\\", nonce=\\\"fjalskdflwejrl\" +\n            \"askdfjlaskdjflaksjdflkasdf\\\", stale=\\\"FALSE\\\"\",\n        ).build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"Digest\")\n    assertThat(challenges[0].realm).isEqualTo(\"myrealm\")\n    val expectedAuthParams = mutableMapOf<String, String>()\n    expectedAuthParams[\"realm\"] = \"myrealm\"\n    expectedAuthParams[\"nonce\"] = \"fjalskdflwejrlaskdfjlaskdjflaksjdflkasdf\"\n    expectedAuthParams[\"qop\"] = \"auth\"\n    expectedAuthParams[\"stale\"] = \"FALSE\"\n    assertThat(challenges[0].authParams).isEqualTo(expectedAuthParams)\n  }\n\n  @Test fun testDigestChallengeWithAdditionalSpacesBeforeFirstAuthParam() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\n          \"WWW-Authenticate\",\n          \"Digest    realm=\\\"myrealm\\\", nonce=\\\"fjalskdflwejrlaskdfjlaskdjfl\" +\n            \"aksjdflkasdf\\\", qop=\\\"auth\\\", stale=\\\"FALSE\\\"\",\n        ).build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"Digest\")\n    assertThat(challenges[0].realm).isEqualTo(\"myrealm\")\n    val expectedAuthParams = mutableMapOf<String, String>()\n    expectedAuthParams[\"realm\"] = \"myrealm\"\n    expectedAuthParams[\"nonce\"] = \"fjalskdflwejrlaskdfjlaskdjflaksjdflkasdf\"\n    expectedAuthParams[\"qop\"] = \"auth\"\n    expectedAuthParams[\"stale\"] = \"FALSE\"\n    assertThat(challenges[0].authParams).isEqualTo(expectedAuthParams)\n  }\n\n  @Test fun testDigestChallengeWithCamelCasedNames() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\n          \"WWW-Authenticate\",\n          \"DiGeSt qop=\\\"auth\\\", rEaLm=\\\"myrealm\\\", nonce=\\\"fjalskdflwejrlask\" +\n            \"dfjlaskdjflaksjdflkasdf\\\", stale=\\\"FALSE\\\"\",\n        ).build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"DiGeSt\")\n    assertThat(challenges[0].realm).isEqualTo(\"myrealm\")\n    val expectedAuthParams = mutableMapOf<String, String>()\n    expectedAuthParams[\"realm\"] = \"myrealm\"\n    expectedAuthParams[\"nonce\"] = \"fjalskdflwejrlaskdfjlaskdjflaksjdflkasdf\"\n    expectedAuthParams[\"qop\"] = \"auth\"\n    expectedAuthParams[\"stale\"] = \"FALSE\"\n    assertThat(challenges[0].authParams).isEqualTo(expectedAuthParams)\n  }\n\n  @Test fun testDigestChallengeWithCamelCasedNames2() {\n    // Strict RFC 2617 camelcased.\n    val headers =\n      Headers\n        .Builder()\n        .add(\n          \"WWW-Authenticate\",\n          \"DIgEsT rEaLm=\\\"myrealm\\\", nonce=\\\"fjalskdflwejrlaskdfjlaskdjflaks\" +\n            \"jdflkasdf\\\", qop=\\\"auth\\\", stale=\\\"FALSE\\\"\",\n        ).build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"DIgEsT\")\n    assertThat(challenges[0].realm).isEqualTo(\"myrealm\")\n    val expectedAuthParams = mutableMapOf<String, String>()\n    expectedAuthParams[\"realm\"] = \"myrealm\"\n    expectedAuthParams[\"nonce\"] = \"fjalskdflwejrlaskdfjlaskdjflaksjdflkasdf\"\n    expectedAuthParams[\"qop\"] = \"auth\"\n    expectedAuthParams[\"stale\"] = \"FALSE\"\n    assertThat(challenges[0].authParams).isEqualTo(expectedAuthParams)\n  }\n\n  @Test fun testDigestChallengeWithTokenFormOfAuthParam() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest realm=myrealm\")\n        .build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"Digest\")\n    assertThat(challenges[0].realm).isEqualTo(\"myrealm\")\n    assertThat(challenges[0].authParams)\n      .isEqualTo(mapOf(\"realm\" to \"myrealm\"))\n  }\n\n  @Test fun testDigestChallengeWithoutAuthParams() {\n    // Scheme only.\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest\")\n        .build()\n    val challenges = headers.parseChallenges(\"WWW-Authenticate\")\n    assertThat(challenges.size).isEqualTo(1)\n    assertThat(challenges[0].scheme).isEqualTo(\"Digest\")\n    assertThat(challenges[0].realm).isNull()\n    assertThat(challenges[0].authParams).isEqualTo(emptyMap<Any, Any>())\n  }\n\n  @Test fun basicChallenge() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate: Basic realm=\\\"protected area\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\"))\n      .isEqualTo(listOf(Challenge(\"Basic\", mapOf(\"realm\" to \"protected area\"))))\n  }\n\n  @Test fun basicChallengeWithCharset() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate: Basic realm=\\\"protected area\\\", charset=\\\"UTF-8\\\"\")\n        .build()\n    val expectedAuthParams = mutableMapOf<String?, String>()\n    expectedAuthParams[\"realm\"] = \"protected area\"\n    expectedAuthParams[\"charset\"] = \"UTF-8\"\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\"))\n      .isEqualTo(listOf(Challenge(\"Basic\", expectedAuthParams)))\n  }\n\n  @Test fun basicChallengeWithUnexpectedCharset() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate: Basic realm=\\\"protected area\\\", charset=\\\"US-ASCII\\\"\")\n        .build()\n    val expectedAuthParams = mutableMapOf<String?, String>()\n    expectedAuthParams[\"realm\"] = \"protected area\"\n    expectedAuthParams[\"charset\"] = \"US-ASCII\"\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\"))\n      .isEqualTo(listOf(Challenge(\"Basic\", expectedAuthParams)))\n  }\n\n  @Test fun separatorsBeforeFirstChallenge() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \" ,  , Basic realm=myrealm\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\"))\n      .isEqualTo(listOf(Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\"))))\n  }\n\n  @Test fun spacesAroundKeyValueSeparator() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Basic realm = \\\"myrealm\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\"))\n      .isEqualTo(listOf(Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\"))))\n  }\n\n  @Test fun multipleChallengesInOneHeader() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Basic realm = \\\"myrealm\\\",Digest\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\")),\n      Challenge(\"Digest\", mapOf()),\n    )\n  }\n\n  @Test fun multipleChallengesWithSameSchemeButDifferentRealmInOneHeader() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Basic realm = \\\"myrealm\\\",Basic realm=myotherrealm\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\")),\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myotherrealm\")),\n    )\n  }\n\n  @Test fun separatorsBeforeFirstAuthParam() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest, Basic ,,realm=\\\"myrealm\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\")),\n    )\n  }\n\n  @Test fun onlyCommaBetweenChallenges() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest,Basic realm=\\\"myrealm\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\")),\n    )\n  }\n\n  @Test fun multipleSeparatorsBetweenChallenges() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest,,,, Basic ,,realm=\\\"myrealm\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\")),\n    )\n  }\n\n  @Test fun unknownAuthParams() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest,,,, Basic ,,foo=bar,realm=\\\"myrealm\\\"\")\n        .build()\n    val expectedAuthParams = mutableMapOf<String?, String>()\n    expectedAuthParams[\"realm\"] = \"myrealm\"\n    expectedAuthParams[\"foo\"] = \"bar\"\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n      Challenge(\"Basic\", expectedAuthParams),\n    )\n  }\n\n  @Test fun escapedCharactersInQuotedString() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest,,,, Basic ,,,realm=\\\"my\\\\\\\\\\\\\\\"r\\\\ealm\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n      Challenge(\"Basic\", mapOf(\"realm\" to \"my\\\\\\\"realm\")),\n    )\n  }\n\n  @Test fun commaInQuotedStringAndBeforeFirstChallenge() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \",Digest,,,, Basic ,,,realm=\\\"my, realm,\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n      Challenge(\"Basic\", mapOf(\"realm\" to \"my, realm,\")),\n    )\n  }\n\n  @Test fun unescapedDoubleQuoteInQuotedStringWithEvenNumberOfBackslashesInFront() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest,,,, Basic ,,,realm=\\\"my\\\\\\\\\\\\\\\\\\\"r\\\\ealm\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n    )\n  }\n\n  @Test fun unescapedDoubleQuoteInQuotedString() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest,,,, Basic ,,,realm=\\\"my\\\"realm\\\"\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n    )\n  }\n\n  @Disabled(\"TODO(jwilson): reject parameters that use invalid characters\")\n  @Test\n  fun doubleQuoteInToken() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest,,,, Basic ,,,realm=my\\\"realm\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n    )\n  }\n\n  @Test fun token68InsteadOfAuthParams() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Other abc==\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\"))\n      .isEqualTo(\n        listOf(Challenge(\"Other\", mapOf(null to \"abc==\"))),\n      )\n  }\n\n  @Test fun token68AndAuthParams() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Other abc==, realm=myrealm\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Other\", mapOf(null to \"abc==\")),\n    )\n  }\n\n  @Test fun repeatedAuthParamKey() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Other realm=myotherrealm, realm=myrealm\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).isEqualTo(listOf<Any>())\n  }\n\n  @Test fun multipleAuthenticateHeaders() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Digest\")\n        .add(\"WWW-Authenticate\", \"Basic realm=myrealm\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Digest\", mapOf()),\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\")),\n    )\n  }\n\n  @Test fun multipleAuthenticateHeadersInDifferentOrder() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Basic realm=myrealm\")\n        .add(\"WWW-Authenticate\", \"Digest\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\")),\n      Challenge(\"Digest\", mapOf()),\n    )\n  }\n\n  @Test fun multipleBasicAuthenticateHeaders() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"WWW-Authenticate\", \"Basic realm=myrealm\")\n        .add(\"WWW-Authenticate\", \"Basic realm=myotherrealm\")\n        .build()\n    assertThat(headers.parseChallenges(\"WWW-Authenticate\")).containsExactly(\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myrealm\")),\n      Challenge(\"Basic\", mapOf(\"realm\" to \"myotherrealm\")),\n    )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/HeadersJvmTest.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport java.time.Instant\nimport java.util.Date\nimport kotlin.test.assertFailsWith\nimport okhttp3.Headers.Companion.toHeaders\nimport org.junit.jupiter.api.Test\n\nclass HeadersJvmTest {\n  @Test fun byteCount() {\n    assertThat(Headers.EMPTY.byteCount()).isEqualTo(0L)\n    assertThat(\n      Headers\n        .Builder()\n        .add(\"abc\", \"def\")\n        .build()\n        .byteCount(),\n    ).isEqualTo(10L)\n    assertThat(\n      Headers\n        .Builder()\n        .add(\"abc\", \"def\")\n        .add(\"ghi\", \"jkl\")\n        .build()\n        .byteCount(),\n    ).isEqualTo(20L)\n  }\n\n  @Test fun addDate() {\n    val expected = Date(0L)\n    val headers =\n      Headers\n        .Builder()\n        .add(\"testDate\", expected)\n        .build()\n    assertThat(headers[\"testDate\"]).isEqualTo(\"Thu, 01 Jan 1970 00:00:00 GMT\")\n    assertThat(headers.getDate(\"testDate\")).isEqualTo(Date(0L))\n  }\n\n  @Test fun addInstant() {\n    val expected = Instant.ofEpochMilli(0L)\n    val headers =\n      Headers\n        .Builder()\n        .add(\"Test-Instant\", expected)\n        .build()\n    assertThat(headers[\"Test-Instant\"]).isEqualTo(\"Thu, 01 Jan 1970 00:00:00 GMT\")\n    assertThat(headers.getInstant(\"Test-Instant\")).isEqualTo(expected)\n  }\n\n  @Test fun setDate() {\n    val expected = Date(1000)\n    val headers =\n      Headers\n        .Builder()\n        .add(\"testDate\", Date(0L))\n        .set(\"testDate\", expected)\n        .build()\n    assertThat(headers[\"testDate\"]).isEqualTo(\"Thu, 01 Jan 1970 00:00:01 GMT\")\n    assertThat(headers.getDate(\"testDate\")).isEqualTo(expected)\n  }\n\n  @Test fun setInstant() {\n    val expected = Instant.ofEpochMilli(1000L)\n    val headers =\n      Headers\n        .Builder()\n        .add(\"Test-Instant\", Instant.ofEpochMilli(0L))\n        .set(\"Test-Instant\", expected)\n        .build()\n    assertThat(headers[\"Test-Instant\"]).isEqualTo(\"Thu, 01 Jan 1970 00:00:01 GMT\")\n    assertThat(headers.getInstant(\"Test-Instant\")).isEqualTo(expected)\n  }\n\n  @Test fun addParsing() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"foo: bar\")\n        .add(\" foo: baz\") // Name leading whitespace is trimmed.\n        .add(\"foo : bak\") // Name trailing whitespace is trimmed.\n        .add(\"\\tkey\\t:\\tvalue\\t\") // '\\t' also counts as whitespace\n        .add(\"ping:  pong  \") // Value whitespace is trimmed.\n        .add(\"kit:kat\") // Space after colon is not required.\n        .build()\n    assertThat(headers.values(\"foo\")).containsExactly(\"bar\", \"baz\", \"bak\")\n    assertThat(headers.values(\"key\")).containsExactly(\"value\")\n    assertThat(headers.values(\"ping\")).containsExactly(\"pong\")\n    assertThat(headers.values(\"kit\")).containsExactly(\"kat\")\n  }\n\n  @Test fun addThrowsOnEmptyName() {\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\": bar\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\" : bar\")\n    }\n  }\n\n  @Test fun addThrowsOnNoColon() {\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\"foo bar\")\n    }\n  }\n\n  @Test fun addThrowsOnMultiColon() {\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\":status: 200 OK\")\n    }\n  }\n\n  @Test fun addUnsafeNonAsciiRejectsUnicodeName() {\n    assertFailsWith<IllegalArgumentException> {\n      Headers\n        .Builder()\n        .addUnsafeNonAscii(\"héader1\", \"value1\")\n        .build()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Unexpected char 0xe9 at 1 in header name: héader1\")\n    }\n  }\n\n  @Test fun addUnsafeNonAsciiAcceptsUnicodeValue() {\n    val headers =\n      Headers\n        .Builder()\n        .addUnsafeNonAscii(\"header1\", \"valué1\")\n        .build()\n    assertThat(headers.toString()).isEqualTo(\"header1: valué1\\n\")\n  }\n\n  // Fails on JS, ClassCastException: Illegal cast\n  @Test fun ofMapThrowsOnNull() {\n    assertFailsWith<NullPointerException> {\n      (mapOf(\"User-Agent\" to null) as Map<String, String>).toHeaders()\n    }\n  }\n\n  @Test fun toMultimapGroupsHeaders() {\n    val headers =\n      Headers.headersOf(\n        \"cache-control\",\n        \"no-cache\",\n        \"cache-control\",\n        \"no-store\",\n        \"user-agent\",\n        \"OkHttp\",\n      )\n    val headerMap = headers.toMultimap()\n    assertThat(headerMap[\"cache-control\"]!!.size).isEqualTo(2)\n    assertThat(headerMap[\"user-agent\"]!!.size).isEqualTo(1)\n  }\n\n  @Test fun toMultimapUsesCanonicalCase() {\n    val headers =\n      Headers.headersOf(\n        \"cache-control\",\n        \"no-store\",\n        \"Cache-Control\",\n        \"no-cache\",\n        \"User-Agent\",\n        \"OkHttp\",\n      )\n    val headerMap = headers.toMultimap()\n    assertThat(headerMap[\"cache-control\"]!!.size).isEqualTo(2)\n    assertThat(headerMap[\"user-agent\"]!!.size).isEqualTo(1)\n  }\n\n  @Test fun toMultimapAllowsCaseInsensitiveGet() {\n    val headers =\n      Headers.headersOf(\n        \"cache-control\",\n        \"no-store\",\n        \"Cache-Control\",\n        \"no-cache\",\n      )\n    val headerMap = headers.toMultimap()\n    assertThat(headerMap[\"cache-control\"]!!.size).isEqualTo(2)\n    assertThat(headerMap[\"Cache-Control\"]!!.size).isEqualTo(2)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/HeadersKotlinTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport java.time.Instant\nimport java.util.Date\nimport okhttp3.Headers.Companion.headersOf\nimport org.junit.jupiter.api.Test\n\nclass HeadersKotlinTest {\n  @Test fun getOperator() {\n    val headers = headersOf(\"a\", \"b\", \"c\", \"d\")\n    assertThat(headers[\"a\"]).isEqualTo(\"b\")\n    assertThat(headers[\"c\"]).isEqualTo(\"d\")\n    assertThat(headers[\"e\"]).isNull()\n  }\n\n  @Test fun iteratorOperator() {\n    val headers = headersOf(\"a\", \"b\", \"c\", \"d\")\n\n    val pairs = mutableListOf<Pair<String, String>>()\n    for ((name, value) in headers) {\n      pairs += name to value\n    }\n\n    assertThat(pairs).containsExactly(\"a\" to \"b\", \"c\" to \"d\")\n  }\n\n  @Test fun builderGetOperator() {\n    val builder = Headers.Builder()\n    builder.add(\"a\", \"b\")\n    builder.add(\"c\", \"d\")\n    assertThat(builder[\"a\"]).isEqualTo(\"b\")\n    assertThat(builder[\"c\"]).isEqualTo(\"d\")\n    assertThat(builder[\"e\"]).isNull()\n  }\n\n  @Test fun builderSetOperator() {\n    val builder = Headers.Builder()\n    builder[\"a\"] = \"b\"\n    builder[\"c\"] = \"d\"\n    builder[\"e\"] = Date(0L)\n    builder[\"g\"] = Instant.EPOCH\n    assertThat(builder[\"a\"]).isEqualTo(\"b\")\n    assertThat(builder[\"c\"]).isEqualTo(\"d\")\n    assertThat(builder[\"e\"]).isEqualTo(\"Thu, 01 Jan 1970 00:00:00 GMT\")\n    assertThat(builder[\"g\"]).isEqualTo(\"Thu, 01 Jan 1970 00:00:00 GMT\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/HeadersRequestTest.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.TestUtil.headerEntries\nimport okhttp3.internal.http2.Http2ExchangeCodec.Companion.http2HeadersList\nimport okhttp3.internal.http2.Http2ExchangeCodec.Companion.readHttp2HeadersList\nimport org.junit.jupiter.api.Test\n\nclass HeadersRequestTest {\n  @Test fun readNameValueBlockDropsForbiddenHeadersHttp2() {\n    val headerBlock =\n      headersOf(\n        \":status\",\n        \"200 OK\",\n        \":version\",\n        \"HTTP/1.1\",\n        \"connection\",\n        \"close\",\n      )\n    val request = Request.Builder().url(\"http://square.com/\").build()\n    val response = readHttp2HeadersList(headerBlock, Protocol.HTTP_2).request(request).build()\n    val headers = response.headers\n    assertThat(headers.size).isEqualTo(1)\n    assertThat(headers.name(0)).isEqualTo(\":version\")\n    assertThat(headers.value(0)).isEqualTo(\"HTTP/1.1\")\n  }\n\n  @Test fun http2HeadersListDropsForbiddenHeadersHttp2() {\n    val request =\n      Request\n        .Builder()\n        .url(\"http://square.com/\")\n        .header(\"Connection\", \"upgrade\")\n        .header(\"Upgrade\", \"websocket\")\n        .header(\"Host\", \"square.com\")\n        .header(\"TE\", \"gzip\")\n        .build()\n    val expected =\n      headerEntries(\n        \":method\",\n        \"GET\",\n        \":path\",\n        \"/\",\n        \":authority\",\n        \"square.com\",\n        \":scheme\",\n        \"http\",\n      )\n    assertThat(http2HeadersList(request)).isEqualTo(expected)\n  }\n\n  @Test fun http2HeadersListDontDropTeIfTrailersHttp2() {\n    val request =\n      Request\n        .Builder()\n        .url(\"http://square.com/\")\n        .header(\"TE\", \"trailers\")\n        .build()\n    val expected =\n      headerEntries(\n        \":method\",\n        \"GET\",\n        \":path\",\n        \"/\",\n        \":scheme\",\n        \"http\",\n        \"te\",\n        \"trailers\",\n      )\n    assertThat(http2HeadersList(request)).isEqualTo(expected)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/HeadersTest.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotEqualTo\nimport kotlin.test.Test\nimport kotlin.test.assertFailsWith\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.Headers.Companion.toHeaders\n\nclass HeadersTest {\n  @Test fun ofTrims() {\n    val headers = headersOf(\"\\t User-Agent \\n\", \" \\r OkHttp \")\n    assertThat(headers.name(0)).isEqualTo(\"User-Agent\")\n    assertThat(headers.value(0)).isEqualTo(\"OkHttp\")\n  }\n\n  @Test fun ofThrowsOddNumberOfHeaders() {\n    assertFailsWith<IllegalArgumentException> {\n      headersOf(\"User-Agent\", \"OkHttp\", \"Content-Length\")\n    }\n  }\n\n  @Test fun ofThrowsOnEmptyName() {\n    assertFailsWith<IllegalArgumentException> {\n      headersOf(\"\", \"OkHttp\")\n    }\n  }\n\n  @Test fun ofAcceptsEmptyValue() {\n    val headers = headersOf(\"User-Agent\", \"\")\n    assertThat(headers.value(0)).isEqualTo(\"\")\n  }\n\n  @Test fun ofMakesDefensiveCopy() {\n    val namesAndValues =\n      arrayOf(\n        \"User-Agent\",\n        \"OkHttp\",\n      )\n    val headers = headersOf(*namesAndValues)\n    namesAndValues[1] = \"Chrome\"\n    assertThat(headers.value(0)).isEqualTo(\"OkHttp\")\n  }\n\n  @Test fun ofRejectsNullChar() {\n    assertFailsWith<IllegalArgumentException> {\n      headersOf(\"User-Agent\", \"Square\\u0000OkHttp\")\n    }\n  }\n\n  @Test fun ofMapThrowsOnEmptyName() {\n    assertFailsWith<IllegalArgumentException> {\n      mapOf(\"\" to \"OkHttp\").toHeaders()\n    }\n  }\n\n  @Test fun ofMapThrowsOnBlankName() {\n    assertFailsWith<IllegalArgumentException> {\n      mapOf(\" \" to \"OkHttp\").toHeaders()\n    }\n  }\n\n  @Test fun ofMapAcceptsEmptyValue() {\n    val headers = mapOf(\"User-Agent\" to \"\").toHeaders()\n    assertThat(headers.value(0)).isEqualTo(\"\")\n  }\n\n  @Test fun ofMapTrimsKey() {\n    val headers = mapOf(\" User-Agent \" to \"OkHttp\").toHeaders()\n    assertThat(headers.name(0)).isEqualTo(\"User-Agent\")\n  }\n\n  @Test fun ofMapTrimsValue() {\n    val headers = mapOf(\"User-Agent\" to \" OkHttp \").toHeaders()\n    assertThat(headers.value(0)).isEqualTo(\"OkHttp\")\n  }\n\n  @Test fun ofMapMakesDefensiveCopy() {\n    val namesAndValues = mutableMapOf<String, String>()\n    namesAndValues[\"User-Agent\"] = \"OkHttp\"\n    val headers = namesAndValues.toHeaders()\n    namesAndValues[\"User-Agent\"] = \"Chrome\"\n    assertThat(headers.value(0)).isEqualTo(\"OkHttp\")\n  }\n\n  @Test fun ofMapRejectsNullCharInName() {\n    assertFailsWith<IllegalArgumentException> {\n      mapOf(\"User-\\u0000Agent\" to \"OkHttp\").toHeaders()\n    }\n  }\n\n  @Test fun ofMapRejectsNullCharInValue() {\n    assertFailsWith<IllegalArgumentException> {\n      mapOf(\"User-Agent\" to \"Square\\u0000OkHttp\").toHeaders()\n    }\n  }\n\n  @Test fun builderRejectsUnicodeInHeaderName() {\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\"héader1\", \"value1\")\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 1 in header name: héader1\")\n    }\n  }\n\n  @Test fun builderRejectsUnicodeInHeaderValue() {\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\"header1\", \"valué1\")\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 4 in header1 value: valué1\")\n    }\n  }\n\n  @Test fun varargFactoryRejectsUnicodeInHeaderName() {\n    assertFailsWith<IllegalArgumentException> {\n      headersOf(\"héader1\", \"value1\")\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 1 in header name: héader1\")\n    }\n  }\n\n  @Test fun varargFactoryRejectsUnicodeInHeaderValue() {\n    assertFailsWith<IllegalArgumentException> {\n      headersOf(\"header1\", \"valué1\")\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 4 in header1 value: valué1\")\n    }\n  }\n\n  @Test fun mapFactoryRejectsUnicodeInHeaderName() {\n    assertFailsWith<IllegalArgumentException> {\n      mapOf(\"héader1\" to \"value1\").toHeaders()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 1 in header name: héader1\")\n    }\n  }\n\n  @Test fun mapFactoryRejectsUnicodeInHeaderValue() {\n    assertFailsWith<IllegalArgumentException> {\n      mapOf(\"header1\" to \"valué1\").toHeaders()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 4 in header1 value: valué1\")\n    }\n  }\n\n  @Test fun sensitiveHeadersNotIncludedInExceptions() {\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\"Authorization\", \"valué1\")\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 4 in Authorization value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\"Cookie\", \"valué1\")\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 4 in Cookie value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\"Proxy-Authorization\", \"valué1\")\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 4 in Proxy-Authorization value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      Headers.Builder().add(\"Set-Cookie\", \"valué1\")\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Unexpected char 0xe9 at 4 in Set-Cookie value\")\n    }\n  }\n\n  @Test fun headersEquals() {\n    val headers1 =\n      Headers\n        .Builder()\n        .add(\"Connection\", \"close\")\n        .add(\"Transfer-Encoding\", \"chunked\")\n        .build()\n    val headers2 =\n      Headers\n        .Builder()\n        .add(\"Connection\", \"close\")\n        .add(\"Transfer-Encoding\", \"chunked\")\n        .build()\n    assertThat(headers2).isEqualTo(headers1)\n    assertThat(headers2.hashCode()).isEqualTo(headers1.hashCode())\n  }\n\n  @Test fun headersNotEquals() {\n    val headers1 =\n      Headers\n        .Builder()\n        .add(\"Connection\", \"close\")\n        .add(\"Transfer-Encoding\", \"chunked\")\n        .build()\n    val headers2 =\n      Headers\n        .Builder()\n        .add(\"Connection\", \"keep-alive\")\n        .add(\"Transfer-Encoding\", \"chunked\")\n        .build()\n    assertThat(headers2).isNotEqualTo(headers1)\n    assertThat(headers2.hashCode()).isNotEqualTo(headers1.hashCode().toLong())\n  }\n\n  @Test fun headersToString() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"A\", \"a\")\n        .add(\"B\", \"bb\")\n        .build()\n    assertThat(headers.toString()).isEqualTo(\"A: a\\nB: bb\\n\")\n  }\n\n  @Test fun headersToStringRedactsSensitiveHeaders() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"content-length\", \"99\")\n        .add(\"authorization\", \"peanutbutter\")\n        .add(\"proxy-authorization\", \"chocolate\")\n        .add(\"cookie\", \"drink=coffee\")\n        .add(\"set-cookie\", \"accessory=sugar\")\n        .add(\"user-agent\", \"OkHttp\")\n        .build()\n    assertThat(headers.toString()).isEqualTo(\n      \"\"\"\n      |content-length: 99\n      |authorization: ██\n      |proxy-authorization: ██\n      |cookie: ██\n      |set-cookie: ██\n      |user-agent: OkHttp\n      |\n      \"\"\".trimMargin(),\n    )\n  }\n\n  @Test fun headersAddAll() {\n    val sourceHeaders =\n      Headers\n        .Builder()\n        .add(\"A\", \"aa\")\n        .add(\"a\", \"aa\")\n        .add(\"B\", \"bb\")\n        .build()\n    val headers =\n      Headers\n        .Builder()\n        .add(\"A\", \"a\")\n        .addAll(sourceHeaders)\n        .add(\"C\", \"c\")\n        .build()\n    assertThat(headers.toString()).isEqualTo(\"A: a\\nA: aa\\na: aa\\nB: bb\\nC: c\\n\")\n  }\n\n  @Test fun nameIndexesAreStrict() {\n    val headers = headersOf(\"a\", \"b\", \"c\", \"d\")\n    assertFailsWith<IndexOutOfBoundsException> {\n      headers.name(-1)\n    }\n    assertThat(headers.name(0)).isEqualTo(\"a\")\n    assertThat(headers.name(1)).isEqualTo(\"c\")\n    assertFailsWith<IndexOutOfBoundsException> {\n      headers.name(2)\n    }\n  }\n\n  @Test fun valueIndexesAreStrict() {\n    val headers = headersOf(\"a\", \"b\", \"c\", \"d\")\n    assertFailsWith<IndexOutOfBoundsException> {\n      headers.value(-1)\n    }\n    assertThat(headers.value(0)).isEqualTo(\"b\")\n    assertThat(headers.value(1)).isEqualTo(\"d\")\n    assertFailsWith<IndexOutOfBoundsException> {\n      headers.value(2)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/HttpUrlJvmTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport java.net.URI\nimport java.net.URL\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.Test\n\n@Suppress(\"HttpUrlsUsage\") // Don't warn if we should be using https://.\nopen class HttpUrlJvmTest {\n  val platform = PlatformRule()\n\n  /** This one's ugly: the HttpUrl's host is non-empty, but the URI's host is null. */\n  @Test\n  fun hostContainsOnlyStrippedCharacters() {\n    val url = \"http://>/\".toHttpUrl()\n    assertThat(url.host).isEqualTo(\">\")\n    assertThat(url.toUri().host).isNull()\n  }\n\n  /**\n   * Strip unexpected characters when converting to URI (which is more strict).\n   * https://github.com/square/okhttp/issues/5667\n   */\n  @Test\n  fun hostToUriStripsCharacters() {\n    val httpUrl = \"http://example\\\".com/\".toHttpUrl()\n    assertThat(httpUrl.toUri().toString()).isEqualTo(\"http://example.com/\")\n  }\n\n  /** Confirm that URI retains other characters. https://github.com/square/okhttp/issues/5236 */\n  @Test\n  fun hostToUriStripsCharacters2() {\n    val httpUrl = \"http://\\${tracker}/\".toHttpUrl()\n    assertThat(httpUrl.toUri().toString()).isEqualTo(\"http://\\$tracker/\")\n  }\n\n  @Test\n  fun fragmentNonAsciiThatOffendsJavaNetUri() {\n    val url = \"http://host/#\\u0080\".toHttpUrl()\n    assertThat(url.toString()).isEqualTo(\"http://host/#\\u0080\")\n    assertThat(url.fragment).isEqualTo(\"\\u0080\")\n    assertThat(url.encodedFragment).isEqualTo(\"\\u0080\")\n    // Control characters may be stripped!\n    assertThat(url.toUri()).isEqualTo(URI(\"http://host/#\"))\n  }\n\n  @Test\n  fun toUriWithControlCharacters() {\n    // Percent-encoded in the path.\n    assertThat(\"http://host/a\\u0000b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/a%00b\"))\n    assertThat(\"http://host/a\\u0080b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/a%C2%80b\"))\n    assertThat(\"http://host/a\\u009fb\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/a%C2%9Fb\"))\n    // Percent-encoded in the query.\n    assertThat(\"http://host/?a\\u0000b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/?a%00b\"))\n    assertThat(\"http://host/?a\\u0080b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/?a%C2%80b\"))\n    assertThat(\"http://host/?a\\u009fb\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/?a%C2%9Fb\"))\n    // Stripped from the fragment.\n    assertThat(\"http://host/#a\\u0000b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/#a%00b\"))\n    assertThat(\"http://host/#a\\u0080b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/#ab\"))\n    assertThat(\"http://host/#a\\u009fb\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/#ab\"))\n  }\n\n  @Test\n  fun toUriWithSpaceCharacters() {\n    // Percent-encoded in the path.\n    assertThat(\"http://host/a\\u000bb\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/a%0Bb\"))\n    assertThat(\"http://host/a b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/a%20b\"))\n    assertThat(\"http://host/a\\u2009b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/a%E2%80%89b\"))\n    assertThat(\"http://host/a\\u3000b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/a%E3%80%80b\"))\n    // Percent-encoded in the query.\n    assertThat(\"http://host/?a\\u000bb\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/?a%0Bb\"))\n    assertThat(\"http://host/?a b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/?a%20b\"))\n    assertThat(\"http://host/?a\\u2009b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/?a%E2%80%89b\"))\n    assertThat(\"http://host/?a\\u3000b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/?a%E3%80%80b\"))\n    // Stripped from the fragment.\n    assertThat(\"http://host/#a\\u000bb\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/#a%0Bb\"))\n    assertThat(\"http://host/#a b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/#a%20b\"))\n    assertThat(\"http://host/#a\\u2009b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/#ab\"))\n    assertThat(\"http://host/#a\\u3000b\".toHttpUrl().toUri())\n      .isEqualTo(URI(\"http://host/#ab\"))\n  }\n\n  @Test\n  fun toUriWithNonHexPercentEscape() {\n    assertThat(\"http://host/%xx\".toHttpUrl().toUri()).isEqualTo(URI(\"http://host/%25xx\"))\n  }\n\n  @Test\n  fun toUriWithTruncatedPercentEscape() {\n    assertThat(\"http://host/%a\".toHttpUrl().toUri()).isEqualTo(URI(\"http://host/%25a\"))\n    assertThat(\"http://host/%\".toHttpUrl().toUri()).isEqualTo(URI(\"http://host/%25\"))\n  }\n\n  @Test\n  fun fromJavaNetUrl() {\n    val javaNetUrl = URL(\"http://username:password@host/path?query#fragment\")\n    val httpUrl = javaNetUrl.toHttpUrlOrNull()\n    assertThat(httpUrl.toString())\n      .isEqualTo(\"http://username:password@host/path?query#fragment\")\n  }\n\n  @Test\n  fun fromJavaNetUrlUnsupportedScheme() {\n    // java.net.MalformedURLException: unknown protocol: mailto\n    platform.assumeNotAndroid()\n\n    // Accessing an URL protocol that was not enabled. The URL protocol mailto is not tested and\n    // might not work as expected. It can be enabled by adding the --enable-url-protocols=mailto\n    // option to the native-image command.\n    platform.assumeNotGraalVMImage()\n    val javaNetUrl = URL(\"mailto:user@example.com\")\n    assertThat(javaNetUrl.toHttpUrlOrNull()).isNull()\n  }\n\n  @Test\n  fun fromUri() {\n    val uri = URI(\"http://username:password@host/path?query#fragment\")\n    val httpUrl = uri.toHttpUrlOrNull()\n    assertThat(httpUrl.toString())\n      .isEqualTo(\"http://username:password@host/path?query#fragment\")\n  }\n\n  @Test\n  fun fromUriUnsupportedScheme() {\n    val uri = URI(\"mailto:user@example.com\")\n    assertThat(uri.toHttpUrlOrNull()).isNull()\n  }\n\n  @Test\n  fun fromUriPartial() {\n    val uri = URI(\"/path\")\n    assertThat(uri.toHttpUrlOrNull()).isNull()\n  }\n\n  @Test\n  fun toJavaNetUrl() {\n    val httpUrl = \"http://username:password@host/path?query#fragment\".toHttpUrl()\n    val javaNetUrl = httpUrl.toUrl()\n    assertThat(javaNetUrl.toString())\n      .isEqualTo(\"http://username:password@host/path?query#fragment\")\n  }\n\n  @Test\n  fun toUri() {\n    val httpUrl = \"http://username:password@host/path?query#fragment\".toHttpUrl()\n    val uri = httpUrl.toUri()\n    assertThat(uri.toString())\n      .isEqualTo(\"http://username:password@host/path?query#fragment\")\n  }\n\n  @Test\n  fun toUriFragmentSpecialCharacters() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .fragment(\"=[]:;\\\"~|?#@^/$%*\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/#=[]:;\\\"~|?#@^/$%25*\")\n    assertThat(url.toUri().toString())\n      .isEqualTo(\"http://host/#=[]:;%22~%7C?%23@%5E/$%25*\")\n  }\n\n  @Test\n  fun toUriSpecialQueryCharacters() {\n    val httpUrl = \"http://host/?d=abc!@[]^`{}|\\\\\".toHttpUrl()\n    val uri = httpUrl.toUri()\n    assertThat(uri.toString()).isEqualTo(\"http://host/?d=abc!@[]%5E%60%7B%7D%7C%5C\")\n  }\n\n  @Test\n  fun toUriWithUsernameNoPassword() {\n    val httpUrl =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .username(\"user\")\n        .host(\"host\")\n        .build()\n    assertThat(httpUrl.toString()).isEqualTo(\"http://user@host/\")\n    assertThat(httpUrl.toUri().toString()).isEqualTo(\"http://user@host/\")\n  }\n\n  @Test\n  fun toUriUsernameSpecialCharacters() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .username(\"=[]:;\\\"~|?#@^/$%*\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\"http://%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/\")\n    assertThat(url.toUri().toString())\n      .isEqualTo(\"http://%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/\")\n  }\n\n  @Test\n  fun toUriPasswordSpecialCharacters() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .username(\"user\")\n        .password(\"=[]:;\\\"~|?#@^/$%*\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\"http://user:%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/\")\n    assertThat(url.toUri().toString())\n      .isEqualTo(\"http://user:%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/\")\n  }\n\n  @Test\n  fun toUriPathSpecialCharacters() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .addPathSegment(\"=[]:;\\\"~|?#@^/$%*\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\"http://host/=[]:;%22~%7C%3F%23@%5E%2F$%25*\")\n    assertThat(url.toUri().toString())\n      .isEqualTo(\"http://host/=%5B%5D:;%22~%7C%3F%23@%5E%2F$%25*\")\n  }\n\n  @Test\n  fun toUriQueryParameterNameSpecialCharacters() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .addQueryParameter(\"=[]:;\\\"~|?#@^/$%*\", \"a\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\"http://host/?%3D%5B%5D%3A%3B%22%7E%7C%3F%23%40%5E%2F%24%25*=a\")\n    assertThat(url.toUri().toString())\n      .isEqualTo(\"http://host/?%3D%5B%5D%3A%3B%22%7E%7C%3F%23%40%5E%2F%24%25*=a\")\n    assertThat(url.queryParameter(\"=[]:;\\\"~|?#@^/$%*\")).isEqualTo(\"a\")\n  }\n\n  @Test\n  fun toUriQueryParameterValueSpecialCharacters() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .addQueryParameter(\"a\", \"=[]:;\\\"~|?#@^/$%*\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\"http://host/?a=%3D%5B%5D%3A%3B%22%7E%7C%3F%23%40%5E%2F%24%25*\")\n    assertThat(url.toUri().toString())\n      .isEqualTo(\"http://host/?a=%3D%5B%5D%3A%3B%22%7E%7C%3F%23%40%5E%2F%24%25*\")\n    assertThat(url.queryParameter(\"a\")).isEqualTo(\"=[]:;\\\"~|?#@^/$%*\")\n  }\n\n  @Test\n  fun toUriQueryValueSpecialCharacters() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .query(\"=[]:;\\\"~|?#@^/$%*\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/?=[]:;%22~|?%23@^/$%25*\")\n    assertThat(url.toUri().toString())\n      .isEqualTo(\"http://host/?=[]:;%22~%7C?%23@%5E/$%25*\")\n  }\n\n  @Test\n  fun topPrivateDomain() {\n    assertThat(\"https://google.com\".toHttpUrl().topPrivateDomain())\n      .isEqualTo(\"google.com\")\n    assertThat(\"https://adwords.google.co.uk\".toHttpUrl().topPrivateDomain())\n      .isEqualTo(\"google.co.uk\")\n    assertThat(\"https://栃.栃木.jp\".toHttpUrl().topPrivateDomain())\n      .isEqualTo(\"xn--ewv.xn--4pvxs.jp\")\n    assertThat(\"https://xn--ewv.xn--4pvxs.jp\".toHttpUrl().topPrivateDomain())\n      .isEqualTo(\"xn--ewv.xn--4pvxs.jp\")\n    assertThat(\"https://co.uk\".toHttpUrl().topPrivateDomain()).isNull()\n    assertThat(\"https://square\".toHttpUrl().topPrivateDomain()).isNull()\n    assertThat(\"https://栃木.jp\".toHttpUrl().topPrivateDomain()).isNull()\n    assertThat(\"https://xn--4pvxs.jp\".toHttpUrl().topPrivateDomain()).isNull()\n    assertThat(\"https://localhost\".toHttpUrl().topPrivateDomain()).isNull()\n    assertThat(\"https://127.0.0.1\".toHttpUrl().topPrivateDomain()).isNull()\n\n    // https://github.com/square/okhttp/issues/6109\n    assertThat(\"http://a./\".toHttpUrl().topPrivateDomain()).isNull()\n    assertThat(\"http://squareup.com./\".toHttpUrl().topPrivateDomain())\n      .isEqualTo(\"squareup.com\")\n  }\n\n  @Test\n  fun fragmentNonAscii() {\n    val url = \"http://host/#Σ\".toHttpUrl()\n    assertThat(url.toUri().toString()).isEqualTo(\"http://host/#Σ\")\n  }\n\n  @Test\n  fun fragmentPercentEncodedNonAscii() {\n    val url = \"http://host/#%C2%80\".toHttpUrl()\n    assertThat(url.toUri().toString()).isEqualTo(\"http://host/#%C2%80\")\n  }\n\n  @Test\n  fun fragmentPercentEncodedPartialCodePoint() {\n    val url = \"http://host/#%80\".toHttpUrl()\n    assertThat(url.toUri().toString()).isEqualTo(\"http://host/#%80\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/HttpUrlTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.containsExactlyInAnyOrder\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport kotlin.test.Ignore\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\nimport kotlin.test.fail\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.UrlComponentEncodingTester.Encoding\n\n@Suppress(\"HttpUrlsUsage\") // Don't warn if we should be using https://.\nopen class HttpUrlTest {\n  protected open fun parse(url: String): HttpUrl = url.toHttpUrl()\n\n  protected open fun assertInvalid(\n    string: String,\n    exceptionMessage: String?,\n  ) {\n    try {\n      val result = string.toHttpUrl()\n      if (exceptionMessage != null) {\n        fail(\"Expected failure with $exceptionMessage but got $result\")\n      } else {\n        fail(\"Expected failure but got $result\")\n      }\n    } catch (iae: IllegalArgumentException) {\n      iae.printStackTrace()\n      if (exceptionMessage != null) {\n        assertThat(iae).hasMessage(exceptionMessage)\n      }\n    }\n  }\n\n  @Test\n  fun parseTrimsAsciiWhitespace() {\n    val expected = parse(\"http://host/\")\n    // Leading.\n    assertThat(parse(\"http://host/\\u000c\\n\\t \\r\")).isEqualTo(expected)\n    // Trailing.\n    assertThat(parse(\"\\r\\n\\u000c \\thttp://host/\")).isEqualTo(expected)\n    // Both.\n    assertThat(parse(\" http://host/ \")).isEqualTo(expected)\n    // Both.\n    assertThat(parse(\"    http://host/    \")).isEqualTo(expected)\n    assertThat(parse(\"http://host/\").resolve(\"   \")).isEqualTo(expected)\n    assertThat(parse(\"http://host/\").resolve(\"  .  \")).isEqualTo(expected)\n  }\n\n  @Test\n  fun parseHostAsciiNonPrintable() {\n    val host = \"host\\u0001\"\n    assertInvalid(\"http://$host/\", \"Invalid URL host: \\\"host\\u0001\\\"\")\n    // TODO make exception message escape non-printable characters\n  }\n\n  @Test\n  fun parseDoesNotTrimOtherWhitespaceCharacters() {\n    // Whitespace characters list from Google's Guava team: http://goo.gl/IcR9RD\n    // line tabulation\n    assertThat(parse(\"http://h/\\u000b\").encodedPath).isEqualTo(\"/%0B\")\n    // information separator 4\n    assertThat(parse(\"http://h/\\u001c\").encodedPath).isEqualTo(\"/%1C\")\n    // information separator 3\n    assertThat(parse(\"http://h/\\u001d\").encodedPath).isEqualTo(\"/%1D\")\n    // information separator 2\n    assertThat(parse(\"http://h/\\u001e\").encodedPath).isEqualTo(\"/%1E\")\n    // information separator 1\n    assertThat(parse(\"http://h/\\u001f\").encodedPath).isEqualTo(\"/%1F\")\n    // next line\n    assertThat(parse(\"http://h/\\u0085\").encodedPath).isEqualTo(\"/%C2%85\")\n    // non-breaking space\n    assertThat(parse(\"http://h/\\u00a0\").encodedPath).isEqualTo(\"/%C2%A0\")\n    // ogham space mark\n    assertThat(parse(\"http://h/\\u1680\").encodedPath).isEqualTo(\"/%E1%9A%80\")\n    // mongolian vowel separator\n    assertThat(parse(\"http://h/\\u180e\").encodedPath).isEqualTo(\"/%E1%A0%8E\")\n    // en quad\n    assertThat(parse(\"http://h/\\u2000\").encodedPath).isEqualTo(\"/%E2%80%80\")\n    // em quad\n    assertThat(parse(\"http://h/\\u2001\").encodedPath).isEqualTo(\"/%E2%80%81\")\n    // en space\n    assertThat(parse(\"http://h/\\u2002\").encodedPath).isEqualTo(\"/%E2%80%82\")\n    // em space\n    assertThat(parse(\"http://h/\\u2003\").encodedPath).isEqualTo(\"/%E2%80%83\")\n    // three-per-em space\n    assertThat(parse(\"http://h/\\u2004\").encodedPath).isEqualTo(\"/%E2%80%84\")\n    // four-per-em space\n    assertThat(parse(\"http://h/\\u2005\").encodedPath).isEqualTo(\"/%E2%80%85\")\n    // six-per-em space\n    assertThat(parse(\"http://h/\\u2006\").encodedPath).isEqualTo(\"/%E2%80%86\")\n    // figure space\n    assertThat(parse(\"http://h/\\u2007\").encodedPath).isEqualTo(\"/%E2%80%87\")\n    // punctuation space\n    assertThat(parse(\"http://h/\\u2008\").encodedPath).isEqualTo(\"/%E2%80%88\")\n    // thin space\n    assertThat(parse(\"http://h/\\u2009\").encodedPath).isEqualTo(\"/%E2%80%89\")\n    // hair space\n    assertThat(parse(\"http://h/\\u200a\").encodedPath).isEqualTo(\"/%E2%80%8A\")\n    // zero-width space\n    assertThat(parse(\"http://h/\\u200b\").encodedPath).isEqualTo(\"/%E2%80%8B\")\n    // zero-width non-joiner\n    assertThat(parse(\"http://h/\\u200c\").encodedPath).isEqualTo(\"/%E2%80%8C\")\n    // zero-width joiner\n    assertThat(parse(\"http://h/\\u200d\").encodedPath).isEqualTo(\"/%E2%80%8D\")\n    // left-to-right mark\n    assertThat(parse(\"http://h/\\u200e\").encodedPath).isEqualTo(\"/%E2%80%8E\")\n    // right-to-left mark\n    assertThat(parse(\"http://h/\\u200f\").encodedPath).isEqualTo(\"/%E2%80%8F\")\n    // line separator\n    assertThat(parse(\"http://h/\\u2028\").encodedPath).isEqualTo(\"/%E2%80%A8\")\n    // paragraph separator\n    assertThat(parse(\"http://h/\\u2029\").encodedPath).isEqualTo(\"/%E2%80%A9\")\n    // narrow non-breaking space\n    assertThat(parse(\"http://h/\\u202f\").encodedPath).isEqualTo(\"/%E2%80%AF\")\n    // medium mathematical space\n    assertThat(parse(\"http://h/\\u205f\").encodedPath).isEqualTo(\"/%E2%81%9F\")\n    // ideographic space\n    assertThat(parse(\"http://h/\\u3000\").encodedPath).isEqualTo(\"/%E3%80%80\")\n  }\n\n  @Test\n  fun newBuilderResolve() {\n    // Non-exhaustive tests because implementation is the same as resolve.\n    val base = parse(\"http://host/a/b\")\n    assertThat(base.newBuilder(\"https://host2\")!!.build())\n      .isEqualTo(parse(\"https://host2/\"))\n    assertThat(base.newBuilder(\"//host2\")!!.build())\n      .isEqualTo(parse(\"http://host2/\"))\n    assertThat(base.newBuilder(\"/path\")!!.build())\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.newBuilder(\"path\")!!.build())\n      .isEqualTo(parse(\"http://host/a/path\"))\n    assertThat(base.newBuilder(\"?query\")!!.build())\n      .isEqualTo(parse(\"http://host/a/b?query\"))\n    assertThat(base.newBuilder(\"#fragment\")!!.build())\n      .isEqualTo(parse(\"http://host/a/b#fragment\"))\n    assertThat(base.newBuilder(\"\")!!.build()).isEqualTo(parse(\"http://host/a/b\"))\n    assertThat(base.newBuilder(\"ftp://b\")).isNull()\n    assertThat(base.newBuilder(\"ht+tp://b\")).isNull()\n    assertThat(base.newBuilder(\"ht-tp://b\")).isNull()\n    assertThat(base.newBuilder(\"ht.tp://b\")).isNull()\n  }\n\n  @Test\n  fun redactedUrl() {\n    val baseWithPasswordAndUsername = parse(\"http://username:password@host/a/b#fragment\")\n    val baseWithUsernameOnly = parse(\"http://username@host/a/b#fragment\")\n    val baseWithPasswordOnly = parse(\"http://password@host/a/b#fragment\")\n    assertThat(baseWithPasswordAndUsername.redact()).isEqualTo(\"http://host/...\")\n    assertThat(baseWithUsernameOnly.redact()).isEqualTo(\"http://host/...\")\n    assertThat(baseWithPasswordOnly.redact()).isEqualTo(\"http://host/...\")\n  }\n\n  @Test\n  fun resolveNoScheme() {\n    val base = parse(\"http://host/a/b\")\n    assertThat(base.resolve(\"//host2\")).isEqualTo(parse(\"http://host2/\"))\n    assertThat(base.resolve(\"/path\")).isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"path\")).isEqualTo(parse(\"http://host/a/path\"))\n    assertThat(base.resolve(\"?query\")).isEqualTo(parse(\"http://host/a/b?query\"))\n    assertThat(base.resolve(\"#fragment\"))\n      .isEqualTo(parse(\"http://host/a/b#fragment\"))\n    assertThat(base.resolve(\"\")).isEqualTo(parse(\"http://host/a/b\"))\n    assertThat(base.resolve(\"\\\\path\")).isEqualTo(parse(\"http://host/path\"))\n  }\n\n  @Test\n  fun resolveUnsupportedScheme() {\n    val base = parse(\"http://a/\")\n    assertThat(base.resolve(\"ftp://b\")).isNull()\n    assertThat(base.resolve(\"ht+tp://b\")).isNull()\n    assertThat(base.resolve(\"ht-tp://b\")).isNull()\n    assertThat(base.resolve(\"ht.tp://b\")).isNull()\n  }\n\n  @Test\n  fun resolveSchemeLikePath() {\n    val base = parse(\"http://a/\")\n    assertThat(base.resolve(\"http//b/\")).isEqualTo(parse(\"http://a/http//b/\"))\n    assertThat(base.resolve(\"ht+tp//b/\")).isEqualTo(parse(\"http://a/ht+tp//b/\"))\n    assertThat(base.resolve(\"ht-tp//b/\")).isEqualTo(parse(\"http://a/ht-tp//b/\"))\n    assertThat(base.resolve(\"ht.tp//b/\")).isEqualTo(parse(\"http://a/ht.tp//b/\"))\n  }\n\n  /**\n   * https://tools.ietf.org/html/rfc3986#section-5.4.1\n   */\n  @Test\n  fun rfc3886NormalExamples() {\n    val url = parse(\"http://a/b/c/d;p?q\")\n    // No 'g:' scheme in HttpUrl.\n    assertThat(url.resolve(\"g:h\")).isNull()\n    assertThat(url.resolve(\"g\")).isEqualTo(parse(\"http://a/b/c/g\"))\n    assertThat(url.resolve(\"./g\")).isEqualTo(parse(\"http://a/b/c/g\"))\n    assertThat(url.resolve(\"g/\")).isEqualTo(parse(\"http://a/b/c/g/\"))\n    assertThat(url.resolve(\"/g\")).isEqualTo(parse(\"http://a/g\"))\n    assertThat(url.resolve(\"//g\")).isEqualTo(parse(\"http://g\"))\n    assertThat(url.resolve(\"?y\")).isEqualTo(parse(\"http://a/b/c/d;p?y\"))\n    assertThat(url.resolve(\"g?y\")).isEqualTo(parse(\"http://a/b/c/g?y\"))\n    assertThat(url.resolve(\"#s\")).isEqualTo(parse(\"http://a/b/c/d;p?q#s\"))\n    assertThat(url.resolve(\"g#s\")).isEqualTo(parse(\"http://a/b/c/g#s\"))\n    assertThat(url.resolve(\"g?y#s\")).isEqualTo(parse(\"http://a/b/c/g?y#s\"))\n    assertThat(url.resolve(\";x\")).isEqualTo(parse(\"http://a/b/c/;x\"))\n    assertThat(url.resolve(\"g;x\")).isEqualTo(parse(\"http://a/b/c/g;x\"))\n    assertThat(url.resolve(\"g;x?y#s\")).isEqualTo(parse(\"http://a/b/c/g;x?y#s\"))\n    assertThat(url.resolve(\"\")).isEqualTo(parse(\"http://a/b/c/d;p?q\"))\n    assertThat(url.resolve(\".\")).isEqualTo(parse(\"http://a/b/c/\"))\n    assertThat(url.resolve(\"./\")).isEqualTo(parse(\"http://a/b/c/\"))\n    assertThat(url.resolve(\"..\")).isEqualTo(parse(\"http://a/b/\"))\n    assertThat(url.resolve(\"../\")).isEqualTo(parse(\"http://a/b/\"))\n    assertThat(url.resolve(\"../g\")).isEqualTo(parse(\"http://a/b/g\"))\n    assertThat(url.resolve(\"../..\")).isEqualTo(parse(\"http://a/\"))\n    assertThat(url.resolve(\"../../\")).isEqualTo(parse(\"http://a/\"))\n    assertThat(url.resolve(\"../../g\")).isEqualTo(parse(\"http://a/g\"))\n  }\n\n  /**\n   * https://tools.ietf.org/html/rfc3986#section-5.4.2\n   */\n  @Test\n  fun rfc3886AbnormalExamples() {\n    val url = parse(\"http://a/b/c/d;p?q\")\n    assertThat(url.resolve(\"../../../g\")).isEqualTo(parse(\"http://a/g\"))\n    assertThat(url.resolve(\"../../../../g\")).isEqualTo(parse(\"http://a/g\"))\n    assertThat(url.resolve(\"/./g\")).isEqualTo(parse(\"http://a/g\"))\n    assertThat(url.resolve(\"/../g\")).isEqualTo(parse(\"http://a/g\"))\n    assertThat(url.resolve(\"g.\")).isEqualTo(parse(\"http://a/b/c/g.\"))\n    assertThat(url.resolve(\".g\")).isEqualTo(parse(\"http://a/b/c/.g\"))\n    assertThat(url.resolve(\"g..\")).isEqualTo(parse(\"http://a/b/c/g..\"))\n    assertThat(url.resolve(\"..g\")).isEqualTo(parse(\"http://a/b/c/..g\"))\n    assertThat(url.resolve(\"./../g\")).isEqualTo(parse(\"http://a/b/g\"))\n    assertThat(url.resolve(\"./g/.\")).isEqualTo(parse(\"http://a/b/c/g/\"))\n    assertThat(url.resolve(\"g/./h\")).isEqualTo(parse(\"http://a/b/c/g/h\"))\n    assertThat(url.resolve(\"g/../h\")).isEqualTo(parse(\"http://a/b/c/h\"))\n    assertThat(url.resolve(\"g;x=1/./y\")).isEqualTo(parse(\"http://a/b/c/g;x=1/y\"))\n    assertThat(url.resolve(\"g;x=1/../y\")).isEqualTo(parse(\"http://a/b/c/y\"))\n    assertThat(url.resolve(\"g?y/./x\")).isEqualTo(parse(\"http://a/b/c/g?y/./x\"))\n    assertThat(url.resolve(\"g?y/../x\")).isEqualTo(parse(\"http://a/b/c/g?y/../x\"))\n    assertThat(url.resolve(\"g#s/./x\")).isEqualTo(parse(\"http://a/b/c/g#s/./x\"))\n    assertThat(url.resolve(\"g#s/../x\")).isEqualTo(parse(\"http://a/b/c/g#s/../x\"))\n    // \"http:g\" also okay.\n    assertThat(url.resolve(\"http:g\")).isEqualTo(parse(\"http://a/b/c/g\"))\n  }\n\n  @Test\n  fun parseAuthoritySlashCountDoesntMatter() {\n    assertThat(parse(\"http:host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http://host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:/\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:///host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:\\\\//host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:/\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http://\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:\\\\\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:/\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:\\\\\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http:////host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n  }\n\n  @Test\n  fun resolveAuthoritySlashCountDoesntMatterWithDifferentScheme() {\n    val base = parse(\"https://a/b/c\")\n    assertThat(base.resolve(\"http:host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http://host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:/\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:///host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\//host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:/\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http://\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:/\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:////host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n  }\n\n  @Test\n  fun resolveAuthoritySlashCountMattersWithSameScheme() {\n    val base = parse(\"http://a/b/c\")\n    assertThat(base.resolve(\"http:host/path\"))\n      .isEqualTo(parse(\"http://a/b/host/path\"))\n    assertThat(base.resolve(\"http:/host/path\"))\n      .isEqualTo(parse(\"http://a/host/path\"))\n    assertThat(base.resolve(\"http:\\\\host/path\"))\n      .isEqualTo(parse(\"http://a/host/path\"))\n    assertThat(base.resolve(\"http://host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:/\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:///host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\//host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:/\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http://\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\\\\\/host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:/\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:\\\\\\\\\\\\host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(base.resolve(\"http:////host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n  }\n\n  @Test\n  fun username() {\n    assertThat(parse(\"http://@host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http://user@host/path\"))\n      .isEqualTo(parse(\"http://user@host/path\"))\n  }\n\n  /**\n   * Given multiple '@' characters, the last one is the delimiter.\n   */\n  @Test\n  fun authorityWithMultipleAtSigns() {\n    val httpUrl = parse(\"http://foo@bar@baz/path\")\n    assertThat(httpUrl.username).isEqualTo(\"foo@bar\")\n    assertThat(httpUrl.password).isEqualTo(\"\")\n    assertThat(httpUrl).isEqualTo(parse(\"http://foo%40bar@baz/path\"))\n  }\n\n  /**\n   * Given multiple ':' characters, the first one is the delimiter.\n   */\n  @Test\n  fun authorityWithMultipleColons() {\n    val httpUrl = parse(\"http://foo:pass1@bar:pass2@baz/path\")\n    assertThat(httpUrl.username).isEqualTo(\"foo\")\n    assertThat(httpUrl.password).isEqualTo(\"pass1@bar:pass2\")\n    assertThat(httpUrl).isEqualTo(parse(\"http://foo:pass1%40bar%3Apass2@baz/path\"))\n  }\n\n  @Test\n  fun usernameAndPassword() {\n    assertThat(parse(\"http://username:password@host/path\"))\n      .isEqualTo(parse(\"http://username:password@host/path\"))\n    assertThat(parse(\"http://username:@host/path\"))\n      .isEqualTo(parse(\"http://username@host/path\"))\n  }\n\n  @Test\n  fun passwordWithEmptyUsername() {\n    // Chrome doesn't mind, but Firefox rejects URLs with empty usernames and non-empty passwords.\n    assertThat(parse(\"http://:@host/path\"))\n      .isEqualTo(parse(\"http://host/path\"))\n    assertThat(parse(\"http://:password@@host/path\").encodedPassword)\n      .isEqualTo(\"password%40\")\n  }\n\n  @Test\n  fun unprintableCharactersArePercentEncoded() {\n    assertThat(parse(\"http://host/\\u0000\").encodedPath).isEqualTo(\"/%00\")\n    assertThat(parse(\"http://host/\\u0008\").encodedPath).isEqualTo(\"/%08\")\n    assertThat(parse(\"http://host/\\ufffd\").encodedPath).isEqualTo(\"/%EF%BF%BD\")\n  }\n\n  @Test\n  fun usernameCharacters() {\n    if (!isJvm) return // TODO: this test is broken on non-JVM platforms.\n    UrlComponentEncodingTester\n      .newInstance()\n      .override(\n        Encoding.PERCENT,\n        '['.code,\n        ']'.code,\n        '{'.code,\n        '}'.code,\n        '|'.code,\n        '^'.code,\n        '\\''.code,\n        ';'.code,\n        '='.code,\n        '@'.code,\n      ).override(\n        Encoding.SKIP,\n        ':'.code,\n        '/'.code,\n        '\\\\'.code,\n        '?'.code,\n        '#'.code,\n      ).test(UrlComponentEncodingTester.Component.USER)\n  }\n\n  @Test\n  fun passwordCharacters() {\n    if (!isJvm) return // TODO: this test is broken on non-JVM platforms.\n    UrlComponentEncodingTester\n      .newInstance()\n      .override(\n        Encoding.PERCENT,\n        '['.code,\n        ']'.code,\n        '{'.code,\n        '}'.code,\n        '|'.code,\n        '^'.code,\n        '\\''.code,\n        ':'.code,\n        ';'.code,\n        '='.code,\n        '@'.code,\n      ).override(\n        Encoding.SKIP,\n        '/'.code,\n        '\\\\'.code,\n        '?'.code,\n        '#'.code,\n      ).test(UrlComponentEncodingTester.Component.PASSWORD)\n  }\n\n  @Test\n  fun hostContainsIllegalCharacter() {\n    assertInvalid(\"http://\\n/\", \"Invalid URL host: \\\"\\n\\\"\")\n    assertInvalid(\"http:// /\", \"Invalid URL host: \\\" \\\"\")\n    assertInvalid(\"http://%20/\", \"Invalid URL host: \\\"%20\\\"\")\n  }\n\n  @Test\n  fun hostnameLowercaseCharactersMappedDirectly() {\n    assertThat(parse(\"http://abcd\").host).isEqualTo(\"abcd\")\n    assertThat(parse(\"http://σ\").host).isEqualTo(\"xn--4xa\")\n  }\n\n  @Test\n  fun hostnameUppercaseCharactersConvertedToLowercase() {\n    assertThat(parse(\"http://ABCD\").host).isEqualTo(\"abcd\")\n    assertThat(parse(\"http://Σ\").host).isEqualTo(\"xn--4xa\")\n  }\n\n  @Test\n  fun hostnameIgnoredCharacters() {\n    // The soft hyphen (­) should be ignored.\n    assertThat(parse(\"http://AB\\u00adCD\").host).isEqualTo(\"abcd\")\n  }\n\n  @Test\n  fun hostnameMultipleCharacterMapping() {\n    // Map the single character telephone symbol (℡) to the string \"tel\".\n    assertThat(parse(\"http://\\u2121\").host).isEqualTo(\"tel\")\n  }\n\n  @Test\n  fun hostnameMappingLastMappedCodePoint() {\n    assertThat(parse(\"http://\\uD87E\\uDE1D\").host).isEqualTo(\"xn--pu5l\")\n  }\n\n  // The java.net.IDN implementation doesn't ignore characters that it should.\n  @Ignore\n  @Test\n  fun hostnameMappingLastIgnoredCodePoint() {\n    assertThat(parse(\"http://ab\\uDB40\\uDDEFcd\").host).isEqualTo(\"abcd\")\n  }\n\n  @Test\n  fun hostnameMappingLastDisallowedCodePoint() {\n    assertInvalid(\"http://\\uDBFF\\uDFFF\", \"Invalid URL host: \\\"\\uDBFF\\uDFFF\\\"\")\n  }\n\n  @Test\n  fun hostnameUri() {\n    // Host names are special:\n    //\n    //  * Several characters are forbidden and must throw exceptions if used.\n    //  * They don't use percent escaping at all.\n    //  * They use punycode for internationalization.\n    //  * URI is much more strict than HttpUrl or URL on what's accepted.\n    //\n    // HttpUrl is quite lenient with what characters it accepts. In particular, characters like '{'\n    // and '\"' are permitted but unlikely to occur in real-world URLs. Unfortunately we can't just\n    // lock it down due to URL templating: \"http://{env}.{dc}.example.com\".\n    UrlComponentEncodingTester\n      .newInstance()\n      .nonPrintableAscii(Encoding.FORBIDDEN)\n      .nonAscii(Encoding.PUNYCODE)\n      .override(\n        Encoding.FORBIDDEN,\n        '\\t'.code,\n        '\\n'.code,\n        '\\u000c'.code,\n        '\\r'.code,\n        ' '.code,\n      ).override(\n        Encoding.FORBIDDEN,\n        '#'.code,\n        '%'.code,\n        '/'.code,\n        ':'.code,\n        '?'.code,\n        '@'.code,\n        '['.code,\n        '\\\\'.code,\n        ']'.code,\n      ).override(\n        // java.net.URL got stricter\n        Encoding.SKIP,\n        '\\\"'.code,\n        '<'.code,\n        '>'.code,\n        '^'.code,\n        '`'.code,\n        '{'.code,\n        '|'.code,\n        '}'.code,\n      ).test(UrlComponentEncodingTester.Component.HOST)\n  }\n\n  @Test\n  fun hostIpv6() {\n    // Square braces are absent from host()...\n    assertThat(parse(\"http://[::1]/\").host).isEqualTo(\"::1\")\n\n    // ... but they're included in toString().\n    assertThat(parse(\"http://[::1]/\").toString()).isEqualTo(\"http://[::1]/\")\n\n    // IPv6 colons don't interfere with port numbers or passwords.\n    assertThat(parse(\"http://[::1]:8080/\").port).isEqualTo(8080)\n    assertThat(parse(\"http://user:password@[::1]/\").password).isEqualTo(\"password\")\n    assertThat(parse(\"http://user:password@[::1]:8080/\").host).isEqualTo(\"::1\")\n\n    // Permit the contents of IPv6 addresses to be percent-encoded...\n    assertThat(parse(\"http://[%3A%3A%31]/\").host).isEqualTo(\"::1\")\n\n    // Including the Square braces themselves! (This is what Chrome does.)\n    assertThat(parse(\"http://%5B%3A%3A1%5D/\").host).isEqualTo(\"::1\")\n  }\n\n  @Test\n  fun hostIpv6AddressDifferentFormats() {\n    // Multiple representations of the same address; see http://tools.ietf.org/html/rfc5952.\n    val a3 = \"2001:db8::1:0:0:1\"\n    assertThat(parse(\"http://[2001:db8:0:0:1:0:0:1]\").host).isEqualTo(a3)\n    assertThat(parse(\"http://[2001:0db8:0:0:1:0:0:1]\").host).isEqualTo(a3)\n    assertThat(parse(\"http://[2001:db8::1:0:0:1]\").host).isEqualTo(a3)\n    assertThat(parse(\"http://[2001:db8::0:1:0:0:1]\").host).isEqualTo(a3)\n    assertThat(parse(\"http://[2001:0db8::1:0:0:1]\").host).isEqualTo(a3)\n    assertThat(parse(\"http://[2001:db8:0:0:1::1]\").host).isEqualTo(a3)\n    assertThat(parse(\"http://[2001:db8:0000:0:1::1]\").host).isEqualTo(a3)\n    assertThat(parse(\"http://[2001:DB8:0:0:1::1]\").host).isEqualTo(a3)\n  }\n\n  @Test\n  fun hostIpv6AddressLeadingCompression() {\n    assertThat(parse(\"http://[::0001]\").host).isEqualTo(\"::1\")\n    assertThat(parse(\"http://[0000::0001]\").host).isEqualTo(\"::1\")\n    assertThat(parse(\"http://[0000:0000:0000:0000:0000:0000:0000:0001]\").host)\n      .isEqualTo(\"::1\")\n    assertThat(parse(\"http://[0000:0000:0000:0000:0000:0000::0001]\").host)\n      .isEqualTo(\"::1\")\n  }\n\n  @Test\n  fun hostIpv6AddressTrailingCompression() {\n    assertThat(parse(\"http://[0001:0000::]\").host).isEqualTo(\"1::\")\n    assertThat(parse(\"http://[0001::0000]\").host).isEqualTo(\"1::\")\n    assertThat(parse(\"http://[0001::]\").host).isEqualTo(\"1::\")\n    assertThat(parse(\"http://[1::]\").host).isEqualTo(\"1::\")\n  }\n\n  @Test\n  fun hostIpv6AddressTooManyDigitsInGroup() {\n    assertInvalid(\n      \"http://[00000:0000:0000:0000:0000:0000:0000:0001]\",\n      \"Invalid URL host: \\\"[00000:0000:0000:0000:0000:0000:0000:0001]\\\"\",\n    )\n    assertInvalid(\"http://[::00001]\", \"Invalid URL host: \\\"[::00001]\\\"\")\n  }\n\n  @Test\n  fun hostIpv6AddressMisplacedColons() {\n    assertInvalid(\n      \"http://[:0000:0000:0000:0000:0000:0000:0000:0001]\",\n      \"Invalid URL host: \\\"[:0000:0000:0000:0000:0000:0000:0000:0001]\\\"\",\n    )\n    assertInvalid(\n      \"http://[:::0000:0000:0000:0000:0000:0000:0000:0001]\",\n      \"Invalid URL host: \\\"[:::0000:0000:0000:0000:0000:0000:0000:0001]\\\"\",\n    )\n    assertInvalid(\"http://[:1]\", \"Invalid URL host: \\\"[:1]\\\"\")\n    assertInvalid(\"http://[:::1]\", \"Invalid URL host: \\\"[:::1]\\\"\")\n    assertInvalid(\n      \"http://[0000:0000:0000:0000:0000:0000:0001:]\",\n      \"Invalid URL host: \\\"[0000:0000:0000:0000:0000:0000:0001:]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0000:0000:0000:0000:0000:0000:0000:0001:]\",\n      \"Invalid URL host: \\\"[0000:0000:0000:0000:0000:0000:0000:0001:]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0000:0000:0000:0000:0000:0000:0000:0001::]\",\n      \"Invalid URL host: \\\"[0000:0000:0000:0000:0000:0000:0000:0001::]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0000:0000:0000:0000:0000:0000:0000:0001:::]\",\n      \"Invalid URL host: \\\"[0000:0000:0000:0000:0000:0000:0000:0001:::]\\\"\",\n    )\n    assertInvalid(\"http://[1:]\", \"Invalid URL host: \\\"[1:]\\\"\")\n    assertInvalid(\"http://[1:::]\", \"Invalid URL host: \\\"[1:::]\\\"\")\n    assertInvalid(\"http://[1:::1]\", \"Invalid URL host: \\\"[1:::1]\\\"\")\n    assertInvalid(\n      \"http://[0000:0000:0000:0000::0000:0000:0000:0001]\",\n      \"Invalid URL host: \\\"[0000:0000:0000:0000::0000:0000:0000:0001]\\\"\",\n    )\n  }\n\n  @Test\n  fun hostIpv6AddressTooManyGroups() {\n    assertInvalid(\n      \"http://[0000:0000:0000:0000:0000:0000:0000:0000:0001]\",\n      \"Invalid URL host: \\\"[0000:0000:0000:0000:0000:0000:0000:0000:0001]\\\"\",\n    )\n  }\n\n  @Test\n  fun hostIpv6AddressTooMuchCompression() {\n    assertInvalid(\n      \"http://[0000::0000:0000:0000:0000::0001]\",\n      \"Invalid URL host: \\\"[0000::0000:0000:0000:0000::0001]\\\"\",\n    )\n    assertInvalid(\n      \"http://[::0000:0000:0000:0000::0001]\",\n      \"Invalid URL host: \\\"[::0000:0000:0000:0000::0001]\\\"\",\n    )\n  }\n\n  @Test\n  fun hostIpv6ScopedAddress() {\n    // java.net.InetAddress parses scoped addresses. These aren't valid in URLs.\n    assertInvalid(\"http://[::1%2544]\", \"Invalid URL host: \\\"[::1%2544]\\\"\")\n  }\n\n  @Test\n  fun hostIpv6AddressTooManyLeadingZeros() {\n    // Guava's been buggy on this case. https://github.com/google/guava/issues/3116\n    assertInvalid(\n      \"http://[2001:db8:0:0:1:0:0:00001]\",\n      \"Invalid URL host: \\\"[2001:db8:0:0:1:0:0:00001]\\\"\",\n    )\n  }\n\n  @Test\n  fun hostIpv6WithIpv4Suffix() {\n    assertThat(parse(\"http://[::1:255.255.255.255]/\").host)\n      .isEqualTo(\"::1:ffff:ffff\")\n    assertThat(parse(\"http://[0:0:0:0:0:1:0.0.0.0]/\").host).isEqualTo(\"::1:0:0\")\n  }\n\n  @Test\n  fun hostIpv6WithIpv4SuffixWithOctalPrefix() {\n    // Chrome interprets a leading '0' as octal; Firefox rejects them. (We reject them.)\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:0.0.0.000000]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:0.0.0.000000]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:0.010.0.010]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:0.010.0.010]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:0.0.0.000001]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:0.0.0.000001]\\\"\",\n    )\n  }\n\n  @Test\n  fun hostIpv6WithIpv4SuffixWithHexadecimalPrefix() {\n    // Chrome interprets a leading '0x' as hexadecimal; Firefox rejects them. (We reject them.)\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:0.0x10.0.0x10]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:0.0x10.0.0x10]\\\"\",\n    )\n  }\n\n  @Test\n  fun hostIpv6WithMalformedIpv4Suffix() {\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:0.0:0.0]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:0.0:0.0]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:0.0-0.0]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:0.0-0.0]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:.255.255.255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:.255.255.255]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:255..255.255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:255..255.255]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:255.255..255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:255.255..255]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:0:1:255.255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:0:1:255.255]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:256.255.255.255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:256.255.255.255]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:ff.255.255.255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:ff.255.255.255]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:0:1:255.255.255.255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:0:1:255.255.255.255]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:1:255.255.255.255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:1:255.255.255.255]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:1:0.0.0.0:1]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:1:0.0.0.0:1]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0.0.0.0:1:0:0:0:0:1]/\",\n      \"Invalid URL host: \\\"[0:0.0.0.0:1:0:0:0:0:1]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0.0.0.0:0:0:0:0:0:1]/\",\n      \"Invalid URL host: \\\"[0.0.0.0:0:0:0:0:0:1]\\\"\",\n    )\n  }\n\n  @Test\n  fun hostIpv6WithIncompleteIpv4Suffix() {\n    // To Chrome & Safari these are well-formed; Firefox disagrees. (We're consistent with Firefox).\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:255.255.255.]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:255.255.255.]\\\"\",\n    )\n    assertInvalid(\n      \"http://[0:0:0:0:0:1:255.255.255]/\",\n      \"Invalid URL host: \\\"[0:0:0:0:0:1:255.255.255]\\\"\",\n    )\n  }\n\n  @Test\n  fun hostIpv6Malformed() {\n    assertInvalid(\"http://[::g]/\", \"Invalid URL host: \\\"[::g]\\\"\")\n  }\n\n  @Test\n  fun hostIpv6CanonicalForm() {\n    assertThat(parse(\"http://[abcd:ef01:2345:6789:abcd:ef01:2345:6789]/\").host)\n      .isEqualTo(\"abcd:ef01:2345:6789:abcd:ef01:2345:6789\")\n    assertThat(parse(\"http://[a:0:0:0:b:0:0:0]/\").host).isEqualTo(\"a::b:0:0:0\")\n    assertThat(parse(\"http://[a:b:0:0:c:0:0:0]/\").host).isEqualTo(\"a:b:0:0:c::\")\n    assertThat(parse(\"http://[a:b:0:0:0:c:0:0]/\").host).isEqualTo(\"a:b::c:0:0\")\n    assertThat(parse(\"http://[a:0:0:0:b:0:0:0]/\").host).isEqualTo(\"a::b:0:0:0\")\n    assertThat(parse(\"http://[0:0:0:a:b:0:0:0]/\").host).isEqualTo(\"::a:b:0:0:0\")\n    assertThat(parse(\"http://[0:0:0:a:0:0:0:b]/\").host).isEqualTo(\"::a:0:0:0:b\")\n    assertThat(parse(\"http://[0:a:b:c:d:e:f:1]/\").host).isEqualTo(\"0:a:b:c:d:e:f:1\")\n    assertThat(parse(\"http://[a:b:c:d:e:f:1:0]/\").host).isEqualTo(\"a:b:c:d:e:f:1:0\")\n    assertThat(parse(\"http://[FF01:0:0:0:0:0:0:101]/\").host).isEqualTo(\"ff01::101\")\n    assertThat(parse(\"http://[2001:db8::1]/\").host).isEqualTo(\"2001:db8::1\")\n    assertThat(parse(\"http://[2001:db8:0:0:0:0:2:1]/\").host).isEqualTo(\"2001:db8::2:1\")\n    assertThat(parse(\"http://[2001:db8:0:1:1:1:1:1]/\").host).isEqualTo(\"2001:db8:0:1:1:1:1:1\")\n    assertThat(parse(\"http://[2001:db8:0:0:1:0:0:1]/\").host).isEqualTo(\"2001:db8::1:0:0:1\")\n    assertThat(parse(\"http://[2001:0:0:1:0:0:0:1]/\").host).isEqualTo(\"2001:0:0:1::1\")\n    assertThat(parse(\"http://[1:0:0:0:0:0:0:0]/\").host).isEqualTo(\"1::\")\n    assertThat(parse(\"http://[0:0:0:0:0:0:0:1]/\").host).isEqualTo(\"::1\")\n    assertThat(parse(\"http://[0:0:0:0:0:0:0:0]/\").host).isEqualTo(\"::\")\n    assertThat(parse(\"http://[::ffff:c0a8:1fe]/\").host).isEqualTo(\"192.168.1.254\")\n  }\n\n  /**\n   * The builder permits square braces but does not require them.\n   */\n  @Test\n  fun hostIpv6Builder() {\n    val base = parse(\"http://example.com/\")\n    assertThat(\n      base\n        .newBuilder()\n        .host(\"[::1]\")\n        .build()\n        .toString(),\n    ).isEqualTo(\"http://[::1]/\")\n    assertThat(\n      base\n        .newBuilder()\n        .host(\"[::0001]\")\n        .build()\n        .toString(),\n    ).isEqualTo(\"http://[::1]/\")\n    assertThat(\n      base\n        .newBuilder()\n        .host(\"::1\")\n        .build()\n        .toString(),\n    ).isEqualTo(\"http://[::1]/\")\n    assertThat(\n      base\n        .newBuilder()\n        .host(\"::0001\")\n        .build()\n        .toString(),\n    ).isEqualTo(\"http://[::1]/\")\n  }\n\n  @Test\n  fun pathCharacters() {\n    if (!isJvm) return // TODO: this test is broken on non-JVM platforms.\n    UrlComponentEncodingTester\n      .newInstance()\n      .override(\n        Encoding.PERCENT,\n        '^'.code,\n        '{'.code,\n        '}'.code,\n        '|'.code,\n      ).override(\n        Encoding.SKIP,\n        '\\\\'.code,\n        '?'.code,\n        '#'.code,\n      ).test(UrlComponentEncodingTester.Component.PATH)\n  }\n\n  @Test\n  fun queryCharacters() {\n    if (!isJvm) return // TODO: this test is broken on non-JVM platforms.\n    UrlComponentEncodingTester\n      .newInstance()\n      .override(Encoding.IDENTITY, '?'.code, '`'.code)\n      .override(Encoding.PERCENT, '\\''.code)\n      .override(Encoding.SKIP, '#'.code, '+'.code)\n      .test(UrlComponentEncodingTester.Component.QUERY)\n  }\n\n  @Test\n  fun queryValueCharacters() {\n    if (!isJvm) return // TODO: this test is broken on non-JVM platforms.\n    UrlComponentEncodingTester\n      .newInstance()\n      .override(Encoding.IDENTITY, '?'.code, '`'.code)\n      .override(Encoding.PERCENT, '\\''.code)\n      .override(Encoding.SKIP, '#'.code, '+'.code)\n      .test(UrlComponentEncodingTester.Component.QUERY_VALUE)\n  }\n\n  @Test\n  fun fragmentCharacters() {\n    UrlComponentEncodingTester\n      .newInstance()\n      .override(\n        Encoding.IDENTITY,\n        ' '.code,\n        '\"'.code,\n        '#'.code,\n        '<'.code,\n        '>'.code,\n        '?'.code,\n        '`'.code,\n      ).nonAscii(Encoding.IDENTITY)\n      .test(UrlComponentEncodingTester.Component.FRAGMENT)\n  }\n\n  @Test\n  fun fragmentNonAscii() {\n    val url = parse(\"http://host/#Σ\")\n    assertThat(url.toString()).isEqualTo(\"http://host/#Σ\")\n    assertThat(url.fragment).isEqualTo(\"Σ\")\n    assertThat(url.encodedFragment).isEqualTo(\"Σ\")\n  }\n\n  @Test\n  fun fragmentPercentEncodedNonAscii() {\n    val url = parse(\"http://host/#%C2%80\")\n    assertThat(url.toString()).isEqualTo(\"http://host/#%C2%80\")\n    assertThat(url.fragment).isEqualTo(\"\\u0080\")\n    assertThat(url.encodedFragment).isEqualTo(\"%C2%80\")\n  }\n\n  @Test\n  fun fragmentPercentEncodedPartialCodePoint() {\n    val url = parse(\"http://host/#%80\")\n    assertThat(url.toString()).isEqualTo(\"http://host/#%80\")\n    // Unicode replacement character.\n    assertThat(url.fragment).isEqualTo(\"\\ufffd\")\n    assertThat(url.encodedFragment).isEqualTo(\"%80\")\n  }\n\n  @Test\n  fun relativePath() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(base.resolve(\"d/e/f\")).isEqualTo(parse(\"http://host/a/b/d/e/f\"))\n    assertThat(base.resolve(\"../../d/e/f\")).isEqualTo(parse(\"http://host/d/e/f\"))\n    assertThat(base.resolve(\"..\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\"../..\")).isEqualTo(parse(\"http://host/\"))\n    assertThat(base.resolve(\"../../..\")).isEqualTo(parse(\"http://host/\"))\n    assertThat(base.resolve(\".\")).isEqualTo(parse(\"http://host/a/b/\"))\n    assertThat(base.resolve(\"././..\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\"c/d/../e/../\")).isEqualTo(parse(\"http://host/a/b/c/\"))\n    assertThat(base.resolve(\"..e/\")).isEqualTo(parse(\"http://host/a/b/..e/\"))\n    assertThat(base.resolve(\"e/f../\")).isEqualTo(parse(\"http://host/a/b/e/f../\"))\n    assertThat(base.resolve(\"%2E.\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\".%2E\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\"%2E%2E\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\"%2e.\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\".%2e\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\"%2e%2e\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\"%2E\")).isEqualTo(parse(\"http://host/a/b/\"))\n    assertThat(base.resolve(\"%2e\")).isEqualTo(parse(\"http://host/a/b/\"))\n  }\n\n  @Test\n  fun relativePathWithTrailingSlash() {\n    val base = parse(\"http://host/a/b/c/\")\n    assertThat(base.resolve(\"..\")).isEqualTo(parse(\"http://host/a/b/\"))\n    assertThat(base.resolve(\"../\")).isEqualTo(parse(\"http://host/a/b/\"))\n    assertThat(base.resolve(\"../..\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\"../../\")).isEqualTo(parse(\"http://host/a/\"))\n    assertThat(base.resolve(\"../../..\")).isEqualTo(parse(\"http://host/\"))\n    assertThat(base.resolve(\"../../../\")).isEqualTo(parse(\"http://host/\"))\n    assertThat(base.resolve(\"../../../..\")).isEqualTo(parse(\"http://host/\"))\n    assertThat(base.resolve(\"../../../../\")).isEqualTo(parse(\"http://host/\"))\n    assertThat(base.resolve(\"../../../../a\")).isEqualTo(parse(\"http://host/a\"))\n    assertThat(base.resolve(\"../../../../a/..\")).isEqualTo(parse(\"http://host/\"))\n    assertThat(base.resolve(\"../../../../a/b/..\")).isEqualTo(parse(\"http://host/a/\"))\n  }\n\n  @Test\n  fun pathWithBackslash() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(base.resolve(\"d\\\\e\\\\f\")).isEqualTo(parse(\"http://host/a/b/d/e/f\"))\n    assertThat(base.resolve(\"../..\\\\d\\\\e\\\\f\"))\n      .isEqualTo(parse(\"http://host/d/e/f\"))\n    assertThat(base.resolve(\"..\\\\..\")).isEqualTo(parse(\"http://host/\"))\n  }\n\n  @Test\n  fun relativePathWithSameScheme() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(base.resolve(\"http:d/e/f\")).isEqualTo(parse(\"http://host/a/b/d/e/f\"))\n    assertThat(base.resolve(\"http:../../d/e/f\"))\n      .isEqualTo(parse(\"http://host/d/e/f\"))\n  }\n\n  @Test\n  fun decodeUsername() {\n    assertThat(parse(\"http://user@host/\").username).isEqualTo(\"user\")\n    assertThat(parse(\"http://%F0%9F%8D%A9@host/\").username).isEqualTo(\"\\uD83C\\uDF69\")\n  }\n\n  @Test\n  fun decodePassword() {\n    assertThat(parse(\"http://user:password@host/\").password).isEqualTo(\"password\")\n    assertThat(parse(\"http://user:@host/\").password).isEqualTo(\"\")\n    assertThat(parse(\"http://user:%F0%9F%8D%A9@host/\").password)\n      .isEqualTo(\"\\uD83C\\uDF69\")\n  }\n\n  @Test\n  fun decodeSlashCharacterInDecodedPathSegment() {\n    assertThat(parse(\"http://host/a%2Fb%2Fc\").pathSegments).containsExactly(\"a/b/c\")\n  }\n\n  @Test\n  fun decodeEmptyPathSegments() {\n    assertThat(parse(\"http://host/\").pathSegments).containsExactly(\"\")\n  }\n\n  @Test\n  fun percentDecode() {\n    assertThat(parse(\"http://host/%00\").pathSegments).containsExactly(\"\\u0000\")\n    assertThat(parse(\"http://host/a/%E2%98%83/c\").pathSegments)\n      .containsExactly(\"a\", \"\\u2603\", \"c\")\n    assertThat(parse(\"http://host/a/%F0%9F%8D%A9/c\").pathSegments)\n      .containsExactly(\"a\", \"\\uD83C\\uDF69\", \"c\")\n    assertThat(parse(\"http://host/a/%62/c\").pathSegments)\n      .containsExactly(\"a\", \"b\", \"c\")\n    assertThat(parse(\"http://host/a/%7A/c\").pathSegments)\n      .containsExactly(\"a\", \"z\", \"c\")\n    assertThat(parse(\"http://host/a/%7a/c\").pathSegments)\n      .containsExactly(\"a\", \"z\", \"c\")\n  }\n\n  @Test\n  fun malformedPercentEncoding() {\n    assertThat(parse(\"http://host/a%f/b\").pathSegments).containsExactly(\"a%f\", \"b\")\n    assertThat(parse(\"http://host/%/b\").pathSegments).containsExactly(\"%\", \"b\")\n    assertThat(parse(\"http://host/%\").pathSegments).containsExactly(\"%\")\n    assertThat(parse(\"http://github.com/%%30%30\").pathSegments)\n      .containsExactly(\"%00\")\n  }\n\n  @Test\n  fun malformedUtf8Encoding() {\n    // Replace a partial UTF-8 sequence with the Unicode replacement character.\n    assertThat(parse(\"http://host/a/%E2%98x/c\").pathSegments)\n      .containsExactly(\"a\", \"\\ufffdx\", \"c\")\n  }\n\n  @Test\n  fun incompleteUrlComposition() {\n    val noHost =\n      assertFailsWith<IllegalStateException> {\n        HttpUrl.Builder().scheme(\"http\").build()\n      }\n    assertThat(noHost.message).isEqualTo(\"host == null\")\n    val noScheme =\n      assertFailsWith<IllegalStateException> {\n        HttpUrl.Builder().host(\"host\").build()\n      }\n    assertThat(noScheme.message).isEqualTo(\"scheme == null\")\n  }\n\n  @Test\n  fun builderToString() {\n    assertThat(parse(\"https://host.com/path\").newBuilder().toString())\n      .isEqualTo(\"https://host.com/path\")\n  }\n\n  @Test\n  fun incompleteBuilderToString() {\n    assertThat(\n      HttpUrl\n        .Builder()\n        .scheme(\"https\")\n        .encodedPath(\"/path\")\n        .toString(),\n    ).isEqualTo(\"https:///path\")\n    assertThat(\n      HttpUrl\n        .Builder()\n        .host(\"host.com\")\n        .encodedPath(\"/path\")\n        .toString(),\n    ).isEqualTo(\"//host.com/path\")\n    assertThat(\n      HttpUrl\n        .Builder()\n        .host(\"host.com\")\n        .encodedPath(\"/path\")\n        .port(8080)\n        .toString(),\n    ).isEqualTo(\"//host.com:8080/path\")\n  }\n\n  @Test\n  fun changingSchemeChangesDefaultPort() {\n    assertThat(\n      parse(\"http://example.com\")\n        .newBuilder()\n        .scheme(\"https\")\n        .build()\n        .port,\n    ).isEqualTo(443)\n    assertThat(\n      parse(\"https://example.com\")\n        .newBuilder()\n        .scheme(\"http\")\n        .build()\n        .port,\n    ).isEqualTo(80)\n    assertThat(\n      parse(\"https://example.com:1234\")\n        .newBuilder()\n        .scheme(\"http\")\n        .build()\n        .port,\n    ).isEqualTo(1234)\n  }\n\n  @Test\n  fun composeEncodesWhitespace() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .username(\"a\\r\\n\\u000c\\t b\")\n        .password(\"c\\r\\n\\u000c\\t d\")\n        .host(\"host\")\n        .addPathSegment(\"e\\r\\n\\u000c\\t f\")\n        .query(\"g\\r\\n\\u000c\\t h\")\n        .fragment(\"i\\r\\n\\u000c\\t j\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\n      \"http://a%0D%0A%0C%09%20b:c%0D%0A%0C%09%20d@host\" +\n        \"/e%0D%0A%0C%09%20f?g%0D%0A%0C%09%20h#i%0D%0A%0C%09 j\",\n    )\n    assertThat(url.username).isEqualTo(\"a\\r\\n\\u000c\\t b\")\n    assertThat(url.password).isEqualTo(\"c\\r\\n\\u000c\\t d\")\n    assertThat(url.pathSegments[0]).isEqualTo(\"e\\r\\n\\u000c\\t f\")\n    assertThat(url.query).isEqualTo(\"g\\r\\n\\u000c\\t h\")\n    assertThat(url.fragment).isEqualTo(\"i\\r\\n\\u000c\\t j\")\n  }\n\n  @Test\n  fun composeFromUnencodedComponents() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .username(\"a:\\u0001@/\\\\?#%b\")\n        .password(\"c:\\u0001@/\\\\?#%d\")\n        .host(\"ef\")\n        .port(8080)\n        .addPathSegment(\"g:\\u0001@/\\\\?#%h\")\n        .query(\"i:\\u0001@/\\\\?#%j\")\n        .fragment(\"k:\\u0001@/\\\\?#%l\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\n        \"http://a%3A%01%40%2F%5C%3F%23%25b:c%3A%01%40%2F%5C%3F%23%25d@ef:8080/\" +\n          \"g:%01@%2F%5C%3F%23%25h?i:%01@/\\\\?%23%25j#k:%01@/\\\\?#%25l\",\n      )\n    assertThat(url.scheme).isEqualTo(\"http\")\n    assertThat(url.username).isEqualTo(\"a:\\u0001@/\\\\?#%b\")\n    assertThat(url.password).isEqualTo(\"c:\\u0001@/\\\\?#%d\")\n    assertThat(url.pathSegments).containsExactly(\"g:\\u0001@/\\\\?#%h\")\n    assertThat(url.query).isEqualTo(\"i:\\u0001@/\\\\?#%j\")\n    assertThat(url.fragment).isEqualTo(\"k:\\u0001@/\\\\?#%l\")\n    assertThat(url.encodedUsername).isEqualTo(\"a%3A%01%40%2F%5C%3F%23%25b\")\n    assertThat(url.encodedPassword).isEqualTo(\"c%3A%01%40%2F%5C%3F%23%25d\")\n    assertThat(url.encodedPath).isEqualTo(\"/g:%01@%2F%5C%3F%23%25h\")\n    assertThat(url.encodedQuery).isEqualTo(\"i:%01@/\\\\?%23%25j\")\n    assertThat(url.encodedFragment).isEqualTo(\"k:%01@/\\\\?#%25l\")\n  }\n\n  @Test\n  fun composeFromEncodedComponents() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .encodedUsername(\"a:\\u0001@/\\\\?#%25b\")\n        .encodedPassword(\"c:\\u0001@/\\\\?#%25d\")\n        .host(\"ef\")\n        .port(8080)\n        .addEncodedPathSegment(\"g:\\u0001@/\\\\?#%25h\")\n        .encodedQuery(\"i:\\u0001@/\\\\?#%25j\")\n        .encodedFragment(\"k:\\u0001@/\\\\?#%25l\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\n        \"http://a%3A%01%40%2F%5C%3F%23%25b:c%3A%01%40%2F%5C%3F%23%25d@ef:8080/\" +\n          \"g:%01@%2F%5C%3F%23%25h?i:%01@/\\\\?%23%25j#k:%01@/\\\\?#%25l\",\n      )\n    assertThat(url.scheme).isEqualTo(\"http\")\n    assertThat(url.username).isEqualTo(\"a:\\u0001@/\\\\?#%b\")\n    assertThat(url.password).isEqualTo(\"c:\\u0001@/\\\\?#%d\")\n    assertThat(url.pathSegments).containsExactly(\"g:\\u0001@/\\\\?#%h\")\n    assertThat(url.query).isEqualTo(\"i:\\u0001@/\\\\?#%j\")\n    assertThat(url.fragment).isEqualTo(\"k:\\u0001@/\\\\?#%l\")\n    assertThat(url.encodedUsername).isEqualTo(\"a%3A%01%40%2F%5C%3F%23%25b\")\n    assertThat(url.encodedPassword).isEqualTo(\"c%3A%01%40%2F%5C%3F%23%25d\")\n    assertThat(url.encodedPath).isEqualTo(\"/g:%01@%2F%5C%3F%23%25h\")\n    assertThat(url.encodedQuery).isEqualTo(\"i:%01@/\\\\?%23%25j\")\n    assertThat(url.encodedFragment).isEqualTo(\"k:%01@/\\\\?#%25l\")\n  }\n\n  @Test\n  fun composeWithEncodedPath() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .encodedPath(\"/a%2Fb/c\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/a%2Fb/c\")\n    assertThat(url.encodedPath).isEqualTo(\"/a%2Fb/c\")\n    assertThat(url.pathSegments).containsExactly(\"a/b\", \"c\")\n  }\n\n  @Test\n  fun composeMixingPathSegments() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .encodedPath(\"/a%2fb/c\")\n        .addPathSegment(\"d%25e\")\n        .addEncodedPathSegment(\"f%25g\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/a%2fb/c/d%2525e/f%25g\")\n    assertThat(url.encodedPath).isEqualTo(\"/a%2fb/c/d%2525e/f%25g\")\n    assertThat(url.encodedPathSegments)\n      .containsExactly(\"a%2fb\", \"c\", \"d%2525e\", \"f%25g\")\n    assertThat(url.pathSegments).containsExactly(\"a/b\", \"c\", \"d%25e\", \"f%g\")\n  }\n\n  @Test\n  fun composeWithAddSegment() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\"\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\"\")\n        .addPathSegment(\"d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\"..\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\"\")\n        .addPathSegment(\"..\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\"\")\n        .addPathSegment(\"\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/\")\n  }\n\n  @Test\n  fun addPathSegments() {\n    val base = parse(\"http://host/a/b/c\")\n\n    // Add a string with zero slashes: resulting URL gains one slash.\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d\")\n\n    // Add a string with one slash: resulting URL gains two slashes.\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"/\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c//\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d/\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"/d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c//d\")\n\n    // Add a string with two slashes: resulting URL gains three slashes.\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"//\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c///\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"/d/\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c//d/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d//\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d//\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"//d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c///d\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d/e/f\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d/e/f\")\n  }\n\n  @Test\n  fun addPathSegmentsOntoTrailingSlash() {\n    val base = parse(\"http://host/a/b/c/\")\n\n    // Add a string with zero slashes: resulting URL gains zero slashes.\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d\")\n\n    // Add a string with one slash: resulting URL gains one slash.\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"/\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c//\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d/\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"/d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c//d\")\n\n    // Add a string with two slashes: resulting URL gains two slashes.\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"//\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c///\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"/d/\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c//d/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d//\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d//\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"//d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c///d\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d/e/f\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/d/e/f\")\n  }\n\n  @Test\n  fun addPathSegmentsWithBackslash() {\n    val base = parse(\"http://host/\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"d\\\\e\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/d/e\")\n    assertThat(\n      base\n        .newBuilder()\n        .addEncodedPathSegments(\"d\\\\e\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/d/e\")\n  }\n\n  @Test\n  fun addPathSegmentsWithEmptyPaths() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegments(\"/d/e///f\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c//d/e///f\")\n  }\n\n  @Test\n  fun addEncodedPathSegments() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addEncodedPathSegments(\"d/e/%20/\\n\")\n        .build()\n        .encodedPath as Any,\n    ).isEqualTo(\"/a/b/c/d/e/%20/\")\n  }\n\n  @Test\n  fun addPathSegmentDotDoesNothing() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\".\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c\")\n  }\n\n  @Test\n  fun addPathSegmentEncodes() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\"%2e\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/%252e\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\"%2e%2e\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/%252e%252e\")\n  }\n\n  @Test\n  fun addPathSegmentDotDotPopsDirectory() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\"..\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/\")\n  }\n\n  @Test\n  fun addPathSegmentDotAndIgnoredCharacter() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addPathSegment(\".\\n\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c/.%0A\")\n  }\n\n  @Test\n  fun addEncodedPathSegmentDotAndIgnoredCharacter() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addEncodedPathSegment(\".\\n\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/c\")\n  }\n\n  @Test\n  fun addEncodedPathSegmentDotDotAndIgnoredCharacter() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .addEncodedPathSegment(\"..\\n\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/\")\n  }\n\n  @Test\n  fun setPathSegment() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setPathSegment(0, \"d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/d/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setPathSegment(1, \"d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/d/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setPathSegment(2, \"d\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/d\")\n  }\n\n  @Test\n  fun setPathSegmentEncodes() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setPathSegment(0, \"%25\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/%2525/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setPathSegment(0, \".\\n\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/.%0A/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setPathSegment(0, \"%2e\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/%252e/b/c\")\n  }\n\n  @Test\n  fun setPathSegmentAcceptsEmpty() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setPathSegment(0, \"\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"//b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setPathSegment(2, \"\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/a/b/\")\n  }\n\n  @Test\n  fun setPathSegmentRejectsDot() {\n    val base = parse(\"http://host/a/b/c\")\n    assertFailsWith<IllegalArgumentException> {\n      base.newBuilder().setPathSegment(0, \".\")\n    }\n  }\n\n  @Test\n  fun setPathSegmentRejectsDotDot() {\n    val base = parse(\"http://host/a/b/c\")\n    assertFailsWith<IllegalArgumentException> {\n      base.newBuilder().setPathSegment(0, \"..\")\n    }\n  }\n\n  @Test\n  fun setPathSegmentWithSlash() {\n    val base = parse(\"http://host/a/b/c\")\n    val url = base.newBuilder().setPathSegment(1, \"/\").build()\n    assertThat(url.encodedPath).isEqualTo(\"/a/%2F/c\")\n  }\n\n  @Test\n  fun setPathSegmentOutOfBounds() {\n    assertFailsWith<IndexOutOfBoundsException> {\n      HttpUrl.Builder().setPathSegment(1, \"a\")\n    }\n  }\n\n  @Test\n  fun setEncodedPathSegmentEncodes() {\n    val base = parse(\"http://host/a/b/c\")\n    assertThat(\n      base\n        .newBuilder()\n        .setEncodedPathSegment(0, \"%25\")\n        .build()\n        .encodedPath,\n    ).isEqualTo(\"/%25/b/c\")\n  }\n\n  @Test\n  fun setEncodedPathSegmentRejectsDot() {\n    val base = parse(\"http://host/a/b/c\")\n    assertFailsWith<IllegalArgumentException> {\n      base.newBuilder().setEncodedPathSegment(0, \".\")\n    }\n  }\n\n  @Test\n  fun setEncodedPathSegmentRejectsDotAndIgnoredCharacter() {\n    val base = parse(\"http://host/a/b/c\")\n    assertFailsWith<IllegalArgumentException> {\n      base.newBuilder().setEncodedPathSegment(0, \".\\n\")\n    }\n  }\n\n  @Test\n  fun setEncodedPathSegmentRejectsDotDot() {\n    val base = parse(\"http://host/a/b/c\")\n    assertFailsWith<IllegalArgumentException> {\n      base.newBuilder().setEncodedPathSegment(0, \"..\")\n    }\n  }\n\n  @Test\n  fun setEncodedPathSegmentRejectsDotDotAndIgnoredCharacter() {\n    val base = parse(\"http://host/a/b/c\")\n    assertFailsWith<IllegalArgumentException> {\n      base.newBuilder().setEncodedPathSegment(0, \"..\\n\")\n    }\n  }\n\n  @Test\n  fun setEncodedPathSegmentWithSlash() {\n    val base = parse(\"http://host/a/b/c\")\n    val url = base.newBuilder().setEncodedPathSegment(1, \"/\").build()\n    assertThat(url.encodedPath).isEqualTo(\"/a/%2F/c\")\n  }\n\n  @Test\n  fun setEncodedPathSegmentOutOfBounds() {\n    assertFailsWith<IndexOutOfBoundsException> {\n      HttpUrl.Builder().setEncodedPathSegment(1, \"a\")\n    }\n  }\n\n  @Test\n  fun removePathSegment() {\n    val base = parse(\"http://host/a/b/c\")\n    val url =\n      base\n        .newBuilder()\n        .removePathSegment(0)\n        .build()\n    assertThat(url.encodedPath).isEqualTo(\"/b/c\")\n  }\n\n  @Test\n  fun removePathSegmentDoesntRemovePath() {\n    val base = parse(\"http://host/a/b/c\")\n    val url =\n      base\n        .newBuilder()\n        .removePathSegment(0)\n        .removePathSegment(0)\n        .removePathSegment(0)\n        .build()\n    assertThat(url.pathSegments).containsExactly(\"\")\n    assertThat(url.encodedPath).isEqualTo(\"/\")\n  }\n\n  @Test\n  fun removePathSegmentOutOfBounds() {\n    assertFailsWith<IndexOutOfBoundsException> {\n      HttpUrl.Builder().removePathSegment(1)\n    }\n  }\n\n  @Test\n  fun queryCharactersEncodedWhenComposed() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .addQueryParameter(\"a\", \"!$(),/:;?@[]\\\\^`{|}~\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\"http://host/?a=%21%24%28%29%2C%2F%3A%3B%3F%40%5B%5D%5C%5E%60%7B%7C%7D%7E\")\n    assertThat(url.queryParameter(\"a\")).isEqualTo(\"!$(),/:;?@[]\\\\^`{|}~\")\n  }\n\n  /**\n   * When callers use `addEncodedQueryParameter()` we only encode what's strictly required. We\n   * retain the encoded (or non-encoded) state of the input.\n   */\n  @Test\n  fun queryCharactersNotReencodedWhenComposedWithAddEncoded() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .host(\"host\")\n        .addEncodedQueryParameter(\"a\", \"!$(),/:;?@[]\\\\^`{|}~\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/?a=!$(),/:;?@[]\\\\^`{|}~\")\n    assertThat(url.queryParameter(\"a\")).isEqualTo(\"!$(),/:;?@[]\\\\^`{|}~\")\n  }\n\n  /**\n   * When callers parse a URL with query components that aren't encoded, we shouldn't convert them\n   * into a canonical form because doing so could be semantically different.\n   */\n  @Test\n  fun queryCharactersNotReencodedWhenParsed() {\n    val url = parse(\"http://host/?a=!$(),/:;?@[]\\\\^`{|}~\")\n    assertThat(url.toString()).isEqualTo(\"http://host/?a=!$(),/:;?@[]\\\\^`{|}~\")\n    assertThat(url.queryParameter(\"a\")).isEqualTo(\"!$(),/:;?@[]\\\\^`{|}~\")\n  }\n\n  @Test\n  fun composeQueryWithComponents() {\n    val base = parse(\"http://host/\")\n    val url = base.newBuilder().addQueryParameter(\"a+=& b\", \"c+=& d\").build()\n    assertThat(url.toString())\n      .isEqualTo(\"http://host/?a%2B%3D%26%20b=c%2B%3D%26%20d\")\n    assertThat(url.queryParameterValue(0)).isEqualTo(\"c+=& d\")\n    assertThat(url.queryParameterName(0)).isEqualTo(\"a+=& b\")\n    assertThat(url.queryParameter(\"a+=& b\")).isEqualTo(\"c+=& d\")\n    assertThat(url.queryParameterNames).isEqualTo(setOf(\"a+=& b\"))\n    assertThat(url.queryParameterValues(\"a+=& b\")).isEqualTo(listOf(\"c+=& d\"))\n    assertThat(url.querySize).isEqualTo(1)\n    // Ambiguous! (Though working as designed.)\n    assertThat(url.query).isEqualTo(\"a+=& b=c+=& d\")\n    assertThat(url.encodedQuery).isEqualTo(\"a%2B%3D%26%20b=c%2B%3D%26%20d\")\n  }\n\n  @Test\n  fun composeQueryWithEncodedComponents() {\n    val base = parse(\"http://host/\")\n    val url =\n      base\n        .newBuilder()\n        .addEncodedQueryParameter(\"a+=& b\", \"c+=& d\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/?a+%3D%26%20b=c+%3D%26%20d\")\n    assertThat(url.queryParameter(\"a =& b\")).isEqualTo(\"c =& d\")\n  }\n\n  @Test\n  fun composeQueryRemoveQueryParameter() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .addQueryParameter(\"a+=& b\", \"c+=& d\")\n        .removeAllQueryParameters(\"a+=& b\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/\")\n    assertThat(url.queryParameter(\"a+=& b\")).isNull()\n  }\n\n  @Test\n  fun composeQueryRemoveEncodedQueryParameter() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .addEncodedQueryParameter(\"a+=& b\", \"c+=& d\")\n        .removeAllEncodedQueryParameters(\"a+=& b\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/\")\n    assertThat(url.queryParameter(\"a =& b\")).isNull()\n  }\n\n  @Test\n  fun composeQuerySetQueryParameter() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .addQueryParameter(\"a+=& b\", \"c+=& d\")\n        .setQueryParameter(\"a+=& b\", \"ef\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/?a%2B%3D%26%20b=ef\")\n    assertThat(url.queryParameter(\"a+=& b\")).isEqualTo(\"ef\")\n  }\n\n  @Test\n  fun composeQuerySetEncodedQueryParameter() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .addEncodedQueryParameter(\"a+=& b\", \"c+=& d\")\n        .setEncodedQueryParameter(\"a+=& b\", \"ef\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/?a+%3D%26%20b=ef\")\n    assertThat(url.queryParameter(\"a =& b\")).isEqualTo(\"ef\")\n  }\n\n  @Test\n  fun composeQueryMultipleEncodedValuesForParameter() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .addQueryParameter(\"a+=& b\", \"c+=& d\")\n        .addQueryParameter(\"a+=& b\", \"e+=& f\")\n        .build()\n    assertThat(url.toString())\n      .isEqualTo(\"http://host/?a%2B%3D%26%20b=c%2B%3D%26%20d&a%2B%3D%26%20b=e%2B%3D%26%20f\")\n    assertThat(url.querySize).isEqualTo(2)\n    assertThat(url.queryParameterNames).isEqualTo(setOf(\"a+=& b\"))\n    assertThat(url.queryParameterValues(\"a+=& b\"))\n      .containsExactly(\"c+=& d\", \"e+=& f\")\n  }\n\n  @Test\n  fun absentQueryIsZeroNameValuePairs() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .query(null)\n        .build()\n    assertThat(url.querySize).isEqualTo(0)\n  }\n\n  @Test\n  fun emptyQueryIsSingleNameValuePairWithEmptyKey() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .query(\"\")\n        .build()\n    assertThat(url.querySize).isEqualTo(1)\n    assertThat(url.queryParameterName(0)).isEqualTo(\"\")\n    assertThat(url.queryParameterValue(0)).isNull()\n  }\n\n  @Test\n  fun ampersandQueryIsTwoNameValuePairsWithEmptyKeys() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .query(\"&\")\n        .build()\n    assertThat(url.querySize).isEqualTo(2)\n    assertThat(url.queryParameterName(0)).isEqualTo(\"\")\n    assertThat(url.queryParameterValue(0)).isNull()\n    assertThat(url.queryParameterName(1)).isEqualTo(\"\")\n    assertThat(url.queryParameterValue(1)).isNull()\n  }\n\n  @Test\n  fun removeAllDoesNotRemoveQueryIfNoParametersWereRemoved() {\n    val url =\n      parse(\"http://host/\")\n        .newBuilder()\n        .query(\"\")\n        .removeAllQueryParameters(\"a\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/?\")\n  }\n\n  @Test\n  fun queryParametersWithoutValues() {\n    val url = parse(\"http://host/?foo&bar&baz\")\n    assertThat(url.querySize).isEqualTo(3)\n    assertThat(url.queryParameterNames).containsExactlyInAnyOrder(\"foo\", \"bar\", \"baz\")\n    assertThat(url.queryParameterValue(0)).isNull()\n    assertThat(url.queryParameterValue(1)).isNull()\n    assertThat(url.queryParameterValue(2)).isNull()\n    assertThat(url.queryParameterValues(\"foo\")).isEqualTo(listOf(null as String?))\n    assertThat(url.queryParameterValues(\"bar\")).isEqualTo(listOf(null as String?))\n    assertThat(url.queryParameterValues(\"baz\")).isEqualTo(listOf(null as String?))\n  }\n\n  @Test\n  fun queryParametersWithEmptyValues() {\n    val url = parse(\"http://host/?foo=&bar=&baz=\")\n    assertThat(url.querySize).isEqualTo(3)\n    assertThat(url.queryParameterNames).containsExactlyInAnyOrder(\"foo\", \"bar\", \"baz\")\n    assertThat(url.queryParameterValue(0)).isEqualTo(\"\")\n    assertThat(url.queryParameterValue(1)).isEqualTo(\"\")\n    assertThat(url.queryParameterValue(2)).isEqualTo(\"\")\n    assertThat(url.queryParameterValues(\"foo\")).isEqualTo(listOf(\"\"))\n    assertThat(url.queryParameterValues(\"bar\")).isEqualTo(listOf(\"\"))\n    assertThat(url.queryParameterValues(\"baz\")).isEqualTo(listOf(\"\"))\n  }\n\n  @Test\n  fun queryParametersWithRepeatedName() {\n    val url = parse(\"http://host/?foo[]=1&foo[]=2&foo[]=3\")\n    assertThat(url.querySize).isEqualTo(3)\n    assertThat(url.queryParameterNames).isEqualTo(setOf(\"foo[]\"))\n    assertThat(url.queryParameterValue(0)).isEqualTo(\"1\")\n    assertThat(url.queryParameterValue(1)).isEqualTo(\"2\")\n    assertThat(url.queryParameterValue(2)).isEqualTo(\"3\")\n    assertThat(url.queryParameterValues(\"foo[]\")).containsExactly(\"1\", \"2\", \"3\")\n  }\n\n  @Test\n  fun queryParameterLookupWithNonCanonicalEncoding() {\n    val url = parse(\"http://host/?%6d=m&+=%20\")\n    assertThat(url.queryParameterName(0)).isEqualTo(\"m\")\n    assertThat(url.queryParameterName(1)).isEqualTo(\" \")\n    assertThat(url.queryParameter(\"m\")).isEqualTo(\"m\")\n    assertThat(url.queryParameter(\" \")).isEqualTo(\" \")\n  }\n\n  @Test\n  fun parsedQueryDoesntIncludeFragment() {\n    val url = parse(\"http://host/?#fragment\")\n    assertThat(url.fragment).isEqualTo(\"fragment\")\n    assertThat(url.query).isEqualTo(\"\")\n    assertThat(url.encodedQuery).isEqualTo(\"\")\n  }\n\n  @Test\n  fun roundTripBuilder() {\n    val url =\n      HttpUrl\n        .Builder()\n        .scheme(\"http\")\n        .username(\"%\")\n        .password(\"%\")\n        .host(\"host\")\n        .addPathSegment(\"%\")\n        .query(\"%\")\n        .fragment(\"%\")\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://%25:%25@host/%25?%25#%25\")\n    assertThat(url.newBuilder().build().toString())\n      .isEqualTo(\"http://%25:%25@host/%25?%25#%25\")\n    assertThat(url.resolve(\"\").toString()).isEqualTo(\"http://%25:%25@host/%25?%25\")\n  }\n\n  /**\n   * Although HttpUrl prefers percent-encodings in uppercase, it should preserve the exact structure\n   * of the original encoding.\n   */\n  @Test\n  fun rawEncodingRetained() {\n    val urlString = \"http://%6d%6D:%6d%6D@host/%6d%6D?%6d%6D#%6d%6D\"\n    val url = parse(urlString)\n    assertThat(url.encodedUsername).isEqualTo(\"%6d%6D\")\n    assertThat(url.encodedPassword).isEqualTo(\"%6d%6D\")\n    assertThat(url.encodedPath).isEqualTo(\"/%6d%6D\")\n    assertThat(url.encodedPathSegments).containsExactly(\"%6d%6D\")\n    assertThat(url.encodedQuery).isEqualTo(\"%6d%6D\")\n    assertThat(url.encodedFragment).isEqualTo(\"%6d%6D\")\n    assertThat(url.toString()).isEqualTo(urlString)\n    assertThat(url.newBuilder().build().toString()).isEqualTo(urlString)\n    assertThat(url.resolve(\"\").toString())\n      .isEqualTo(\"http://%6d%6D:%6d%6D@host/%6d%6D?%6d%6D\")\n  }\n\n  @Test\n  fun clearFragment() {\n    val url =\n      parse(\"http://host/#fragment\")\n        .newBuilder()\n        .fragment(null)\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/\")\n    assertThat(url.fragment).isNull()\n    assertThat(url.encodedFragment).isNull()\n  }\n\n  @Test\n  fun clearEncodedFragment() {\n    val url =\n      parse(\"http://host/#fragment\")\n        .newBuilder()\n        .encodedFragment(null)\n        .build()\n    assertThat(url.toString()).isEqualTo(\"http://host/\")\n    assertThat(url.fragment).isNull()\n    assertThat(url.encodedFragment).isNull()\n  }\n\n  @Test\n  fun unparseableTopPrivateDomain() {\n    assertInvalid(\"http://a../\", \"Invalid URL host: \\\"a..\\\"\")\n    assertInvalid(\"http://..a/\", \"Invalid URL host: \\\"..a\\\"\")\n    assertInvalid(\"http://a..b/\", \"Invalid URL host: \\\"a..b\\\"\")\n    assertInvalid(\"http://.a/\", \"Invalid URL host: \\\".a\\\"\")\n    assertInvalid(\"http://../\", \"Invalid URL host: \\\"..\\\"\")\n  }\n\n  @Test\n  fun hostnameTelephone() {\n    // https://www.gosecure.net/blog/2020/10/27/weakness-in-java-tls-host-verification/\n\n    // Map the single character telephone symbol (℡) to the string \"tel\".\n    assertThat(parse(\"http://\\u2121\").host).isEqualTo(\"tel\")\n\n    // Map the Kelvin symbol (K) to the string \"k\".\n    assertThat(parse(\"http://\\u212A\").host).isEqualTo(\"k\")\n  }\n\n  @Test\n  fun quirks() {\n    assertThat(parse(\"http://facebook.com\").host).isEqualTo(\"facebook.com\")\n    assertThat(parse(\"http://facebooK.com\").host).isEqualTo(\"facebook.com\")\n    assertThat(parse(\"http://Facebook.com\").host).isEqualTo(\"facebook.com\")\n    assertThat(parse(\"http://FacebooK.com\").host).isEqualTo(\"facebook.com\")\n  }\n\n  @Test\n  fun trailingDotIsOkay() {\n    val name251 = \"a.\".repeat(125) + \"a\"\n    assertThat(parse(\"http://a./\").toString()).isEqualTo(\"http://a./\")\n    assertThat(parse(\"http://${name251}a./\").toString()).isEqualTo(\"http://${name251}a./\")\n    assertThat(parse(\"http://${name251}aa/\").toString()).isEqualTo(\"http://${name251}aa/\")\n    assertInvalid(\"http://${name251}aa./\", \"Invalid URL host: \\\"${name251}aa.\\\"\")\n  }\n\n  @Test\n  fun labelIsEmpty() {\n    assertInvalid(\"http:///\", \"Invalid URL host: \\\"\\\"\")\n    assertInvalid(\"http://a..b/\", \"Invalid URL host: \\\"a..b\\\"\")\n    assertInvalid(\"http://.a/\", \"Invalid URL host: \\\".a\\\"\")\n    assertInvalid(\"http://./\", \"Invalid URL host: \\\".\\\"\")\n    assertInvalid(\"http://../\", \"Invalid URL host: \\\"..\\\"\")\n    assertInvalid(\"http://.../\", \"Invalid URL host: \\\"...\\\"\")\n    assertInvalid(\"http://…/\", \"Invalid URL host: \\\"…\\\"\")\n  }\n\n  @Test\n  fun labelTooLong() {\n    val a63 = \"a\".repeat(63)\n    assertThat(parse(\"http://$a63/\").toString()).isEqualTo(\"http://$a63/\")\n    assertThat(parse(\"http://a.$a63/\").toString()).isEqualTo(\"http://a.$a63/\")\n    assertThat(parse(\"http://$a63.a/\").toString()).isEqualTo(\"http://$a63.a/\")\n    assertInvalid(\"http://a$a63/\", \"Invalid URL host: \\\"a$a63\\\"\")\n    assertInvalid(\"http://a.a$a63/\", \"Invalid URL host: \\\"a.a$a63\\\"\")\n    assertInvalid(\"http://a$a63.a/\", \"Invalid URL host: \\\"a$a63.a\\\"\")\n  }\n\n  @Test\n  fun labelTooLongDueToAsciiExpansion() {\n    val a60 = \"a\".repeat(60)\n    assertThat(parse(\"http://\\u2121$a60/\").toString()).isEqualTo(\"http://tel$a60/\")\n    assertInvalid(\"http://a\\u2121$a60/\", \"Invalid URL host: \\\"a\\u2121$a60\\\"\")\n  }\n\n  @Test\n  fun hostnameTooLong() {\n    val dotA126 = \"a.\".repeat(126)\n    assertThat(parse(\"http://a$dotA126/\").toString())\n      .isEqualTo(\"http://a$dotA126/\")\n    assertInvalid(\"http://aa$dotA126/\", \"Invalid URL host: \\\"aa$dotA126\\\"\")\n  }\n\n  /**\n   * UTS 46 Validity Criteria: Decoded punycode must be NFC.\n   *\n   * https://www.unicode.org/reports/tr46/#Validity_Criteria\n   */\n  @Test\n  fun hostnameInPunycodeNfcAndNfd() {\n    // café can be NFC (é is one code point) or NFD (e plus ´ as two code points).\n    val hostNfc = \"café.com\"\n    val hostNfcPunycode = \"xn--caf-dma.com\"\n    val hostNfd = \"café.com\"\n    val hostNfdPunycode = \"xn--cafe-yvc.com\"\n    assertEquals(hostNfcPunycode, \"http://$hostNfc/\".toHttpUrl().host)\n    assertEquals(hostNfcPunycode, \"http://$hostNfcPunycode/\".toHttpUrl().host)\n    assertEquals(hostNfcPunycode, \"http://$hostNfd/\".toHttpUrl().host)\n    if (isJvm) return // TODO: the rest of this test is broken on JVM platforms.\n    assertInvalid(\"http://$hostNfdPunycode/\", \"\"\"Invalid URL host: \"$hostNfdPunycode\"\"\"\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/InsecureForHostTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport javax.net.ssl.SSLException\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass InsecureForHostTest {\n  @RegisterExtension @JvmField\n  val platform = PlatformRule()\n\n  @RegisterExtension @JvmField\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setup() {\n    // BCX509ExtendedTrustManager not supported in TlsUtil.newTrustManager\n    platform.assumeNotBouncyCastle()\n  }\n\n  @Test fun `untrusted host in insecureHosts connects successfully`() {\n    val serverCertificates = platform.localhostHandshakeCertificates()\n    server.useHttps(serverCertificates.sslSocketFactory())\n    server.enqueue(MockResponse())\n\n    val clientCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addPlatformTrustedCertificates()\n        .addInsecureHost(server.hostName)\n        .build()\n\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n        .build()\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.handshake!!.cipherSuite).isNotNull()\n    assertThat(response.handshake!!.tlsVersion).isNotNull()\n    assertThat(response.handshake!!.localCertificates).isEmpty()\n    assertThat(response.handshake!!.localPrincipal).isNull()\n    assertThat(response.handshake!!.peerCertificates).isEmpty()\n    assertThat(response.handshake!!.peerPrincipal).isNull()\n  }\n\n  @Test fun `bad certificates host in insecureHosts fails with SSLException`() {\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .addSubjectAlternativeName(\"example.com\")\n        .build()\n    val serverCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(heldCertificate)\n        .build()\n    server.useHttps(serverCertificates.sslSocketFactory())\n    server.enqueue(MockResponse())\n\n    val clientCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addPlatformTrustedCertificates()\n        .addInsecureHost(server.hostName)\n        .build()\n\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n        .build()\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<SSLException> {\n      call.execute()\n    }\n  }\n\n  @Test fun `untrusted host not in insecureHosts fails with SSLException`() {\n    val serverCertificates = platform.localhostHandshakeCertificates()\n    server.useHttps(serverCertificates.sslSocketFactory())\n    server.enqueue(MockResponse())\n\n    val clientCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addPlatformTrustedCertificates()\n        .addInsecureHost(\"${server.hostName}2\")\n        .build()\n\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n        .build()\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<SSLException> {\n      call.execute()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/InterceptorOverridesTest.kt",
    "content": "/*\n * Copyright (c) 2025 OkHttp Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport app.cash.burst.Burst\nimport app.cash.burst.burstValues\nimport assertk.assertFailure\nimport assertk.assertThat\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isFailure\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNotSameInstanceAs\nimport assertk.assertions.isTrue\nimport java.io.FilterInputStream\nimport java.io.FilterOutputStream\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.Socket\nimport java.net.SocketAddress\nimport java.net.URI\nimport java.security.cert.X509Certificate\nimport java.util.Locale.getDefault\nimport java.util.concurrent.TimeUnit\nimport javax.net.SocketFactory\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLException\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509TrustManager\nimport kotlin.time.Duration.Companion.minutes\nimport kotlin.time.Duration.Companion.nanoseconds\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CertificatePinner.Companion.pin\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.internal.connection.ConnectionListener\nimport okhttp3.internal.platform.Platform\nimport okhttp3.testing.PlatformRule\nimport okio.BufferedSink\nimport okio.ForwardingFileSystem\nimport okio.IOException\nimport okio.Path\nimport okio.Path.Companion.toPath\nimport okio.fakefilesystem.FakeFileSystem\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Burst\nclass InterceptorOverridesTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  // Can't use test instance with overrides\n  private var client = OkHttpClient.Builder().build()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n\n  /**\n   * Test that we can override in a Application Interceptor, purely by seeing that the chain reports\n   * the override in a Network Interceptor.\n   */\n  @Test\n  fun testOverrideInApplicationInterceptor(\n    override: OverrideParam =\n      burstValues(\n        OverrideParam.Authenticator,\n        OverrideParam.Cache,\n        OverrideParam.CertificatePinner,\n        OverrideParam.ConnectTimeout,\n        OverrideParam.ConnectionPool,\n        OverrideParam.CookieJar,\n        OverrideParam.Dns,\n        OverrideParam.HostnameVerifier,\n        OverrideParam.Proxy,\n        OverrideParam.ProxyAuthenticator,\n        OverrideParam.ProxySelector,\n        OverrideParam.ReadTimeout,\n        OverrideParam.RetryOnConnectionFailure,\n        OverrideParam.SocketFactory,\n        OverrideParam.SslSocketFactory,\n        OverrideParam.WriteTimeout,\n        OverrideParam.X509TrustManager,\n      ),\n    isDefault: Boolean,\n  ) {\n    fun <T> Override<T>.testApplicationInterceptor(chain: Interceptor.Chain): Response {\n      val defaultValue = chain.value()\n      assertThat(isDefaultValue(chain.value())).isTrue()\n      val withOverride = chain.withOverride(nonDefaultValue)\n      assertThat(chain).isNotSameInstanceAs(withOverride)\n      assertThat(isDefaultValue(withOverride.value())).isFalse()\n\n      return if (isDefault) {\n        val withDefault = withOverride.withOverride(defaultValue)\n        assertThat(isDefaultValue(withDefault.value())).isTrue()\n        withOverride.proceed(chain.request())\n      } else {\n        withOverride.proceed(chain.request())\n      }\n    }\n\n    with(override.override) {\n      client =\n        client\n          .newBuilder()\n          .addInterceptor { chain ->\n            testApplicationInterceptor(chain)\n          }.addNetworkInterceptor { chain ->\n            assertThat(isDefaultValue(chain.value())).isFalse()\n            chain.proceed(chain.request())\n          }.build()\n\n      server.enqueue(\n        MockResponse(),\n      )\n      val response = client.newCall(Request(server.url(\"/\"))).execute()\n      response.close()\n    }\n  }\n\n  /**\n   * Test that we can't override in a Network Interceptor, which will throw an exception.\n   */\n  @Test\n  fun testOverrideInNetworkInterceptor(\n    override: OverrideParam =\n      burstValues(\n        OverrideParam.Authenticator,\n        OverrideParam.Cache,\n        OverrideParam.CertificatePinner,\n        OverrideParam.ConnectTimeout,\n        OverrideParam.ConnectionPool,\n        OverrideParam.CookieJar,\n        OverrideParam.Dns,\n        OverrideParam.HostnameVerifier,\n        OverrideParam.Proxy,\n        OverrideParam.ProxyAuthenticator,\n        OverrideParam.ProxySelector,\n        OverrideParam.ReadTimeout,\n        OverrideParam.RetryOnConnectionFailure,\n        OverrideParam.SocketFactory,\n        OverrideParam.SslSocketFactory,\n        OverrideParam.WriteTimeout,\n        OverrideParam.X509TrustManager,\n      ),\n  ) {\n    with(override.override) {\n      client =\n        client\n          .newBuilder()\n          .addNetworkInterceptor { chain ->\n            assertThat(isDefaultValue(chain.value())).isTrue()\n\n            assertFailure {\n              chain.withOverride(\n                nonDefaultValue,\n              )\n            }.hasMessage(\"${override.paramName} can't be adjusted in a network interceptor\")\n\n            chain.proceed(chain.request())\n          }.build()\n\n      server.enqueue(\n        MockResponse(),\n      )\n      val response = client.newCall(Request(server.url(\"/\"))).execute()\n      response.close()\n    }\n  }\n\n  /**\n   * Test that if we set a bad implementation on the OkHttpClient directly, that we can avoid the failure\n   * by setting a good override.\n   */\n  @Test\n  fun testOverrideBadImplementation(\n    override: OverrideParam =\n      burstValues(\n        OverrideParam.Authenticator,\n        OverrideParam.Cache,\n        OverrideParam.CertificatePinner,\n        OverrideParam.ConnectTimeout,\n        OverrideParam.ConnectionPool,\n        OverrideParam.CookieJar,\n        OverrideParam.Dns,\n        OverrideParam.HostnameVerifier,\n        OverrideParam.Proxy,\n        OverrideParam.ProxyAuthenticator,\n        OverrideParam.ProxySelector,\n        OverrideParam.ReadTimeout,\n        OverrideParam.RetryOnConnectionFailure,\n        OverrideParam.SocketFactory,\n        OverrideParam.SslSocketFactory,\n        OverrideParam.WriteTimeout,\n        OverrideParam.X509TrustManager,\n      ),\n    testItFails: Boolean = false,\n  ) {\n    when (override) {\n      OverrideParam.ProxyAuthenticator -> {\n        client = client.newBuilder().proxy(server.proxyAddress).build()\n\n        server.enqueue(\n          MockResponse\n            .Builder()\n            .code(407)\n            .headers(headersOf(\"Proxy-Authenticate\", \"Basic realm=\\\"localhost\\\"\"))\n            .inTunnel()\n            .build(),\n        )\n\n        overrideBadImplementation(override = override.override, testItFails = testItFails)\n      }\n\n      OverrideParam.Authenticator -> {\n        server.enqueue(\n          MockResponse.Builder().code(401).build(),\n        )\n\n        overrideBadImplementation(override = override.override, testItFails = testItFails)\n      }\n\n      OverrideParam.RetryOnConnectionFailure -> {\n        enableTls()\n        var first = true\n        client =\n          client\n            .newBuilder()\n            .connectionSpecs(listOf(ConnectionSpec.RESTRICTED_TLS, ConnectionSpec.MODERN_TLS))\n            .eventListener(\n              object : EventListener() {\n                override fun secureConnectEnd(\n                  call: Call,\n                  handshake: Handshake?,\n                ) {\n                  if (first) {\n                    first = false\n                    throw SSLException(\"\")\n                  }\n                }\n              },\n            ).build()\n\n        overrideBadImplementation(\n          override = Override.RetryOnConnectionFailureOverride,\n          testItFails = testItFails,\n          badValue = false,\n          goodValue = true,\n        )\n      }\n\n      OverrideParam.SslSocketFactory -> {\n        enableTls()\n        overrideBadImplementation(\n          override = Override.SslSocketFactoryOverride,\n          testItFails = testItFails,\n          goodValue = handshakeCertificates.sslSocketFactory(),\n        )\n      }\n\n      OverrideParam.X509TrustManager -> {\n        enableTls()\n        overrideBadImplementation(\n          override = Override.X509TrustManagerOverride,\n          testItFails = testItFails,\n          goodValue = handshakeCertificates.trustManager,\n        )\n      }\n\n      OverrideParam.HostnameVerifier -> {\n        enableTls()\n        overrideBadImplementation(override = override.override, testItFails = testItFails)\n      }\n\n      OverrideParam.WriteTimeout -> {\n        val body =\n          object : RequestBody() {\n            override fun contentType(): MediaType? = null\n\n            override fun writeTo(sink: BufferedSink) {\n              if (sink\n                  .timeout()\n                  .timeoutNanos()\n                  .nanoseconds.inWholeMilliseconds == 10L\n              ) {\n                throw IOException()\n              }\n            }\n          }\n        overrideBadImplementation(override = override.override, testItFails = testItFails, body = body)\n      }\n\n      OverrideParam.ReadTimeout -> {\n        client =\n          client\n            .newBuilder()\n            .socketFactory(\n              DelayingSocketFactory(onRead = {\n                Thread.sleep(100L)\n              }),\n            ).build()\n\n        overrideBadImplementation(override = override.override, testItFails = testItFails)\n      }\n\n      OverrideParam.ConnectTimeout -> {\n        client =\n          client\n            .newBuilder()\n            .socketFactory(\n              DelayingSocketFactory(onConnect = { timeout ->\n                if (timeout == 10) {\n                  throw IOException()\n                }\n              }),\n            ).build()\n\n        overrideBadImplementation(override = override.override, testItFails = testItFails)\n      }\n\n      OverrideParam.CertificatePinner -> {\n        enableTls()\n\n        val pinner =\n          CertificatePinner\n            .Builder()\n            .add(server.hostName, pin(handshakeCertificates.trustManager.acceptedIssuers.first()))\n            .build()\n\n        overrideBadImplementation(\n          override = Override.CertificatePinnerOverride,\n          testItFails = testItFails,\n          goodValue = pinner,\n        )\n      }\n\n      else -> {\n        overrideBadImplementation(override = override.override, testItFails = testItFails)\n      }\n    }\n  }\n\n  private fun <T> overrideBadImplementation(\n    override: Override<T>,\n    testItFails: Boolean,\n    badValue: T = override.badValue,\n    goodValue: T = override.nonDefaultValue,\n    body: RequestBody? = null,\n  ) {\n    with(override) {\n      client =\n        client\n          .newBuilder()\n          // Set the bad override directly on the client\n          .withOverride(badValue)\n          .addInterceptor { chain ->\n            // the only way to stop a bad override of a client is with a good override of an interceptor\n            chain\n              .run {\n                if (testItFails) {\n                  this\n                } else {\n                  withOverride(goodValue)\n                }\n              }.proceed(chain.request())\n          }.build()\n\n      server.enqueue(\n        MockResponse(),\n      )\n      val call = client.newCall(Request(server.url(\"/\"), body = body))\n      val result = runCatching { call.execute().body.bytes() }\n\n      if (testItFails) {\n        assertThat(result).isFailure()\n      } else {\n        result.getOrThrow()\n      }\n    }\n  }\n\n  enum class OverrideParam(\n    val override: Override<*>,\n  ) {\n    Authenticator(Override.AuthenticatorOverride),\n    Cache(Override.CacheOverride),\n    CertificatePinner(Override.CertificatePinnerOverride),\n    ConnectTimeout(\n      Override.ConnectTimeoutOverride,\n    ) {\n      override val paramName: String\n        get() = \"Timeouts\"\n    },\n    ConnectionPool(Override.ConnectionPoolOverride),\n    CookieJar(Override.CookieJarOverride),\n    Dns(Override.DnsOverride),\n    HostnameVerifier(\n      Override.HostnameVerifierOverride,\n    ),\n    Proxy(Override.ProxyOverride),\n    ProxyAuthenticator(Override.ProxyAuthenticatorOverride),\n    ProxySelector(Override.ProxySelectorOverride),\n    ReadTimeout(\n      Override.ReadTimeoutOverride,\n    ) {\n      override val paramName: String\n        get() = \"Timeouts\"\n    },\n    RetryOnConnectionFailure(Override.RetryOnConnectionFailureOverride),\n    SocketFactory(Override.SocketFactoryOverride),\n    SslSocketFactory(\n      Override.SslSocketFactoryOverride,\n    ),\n    WriteTimeout(Override.WriteTimeoutOverride) {\n      override val paramName: String\n        get() = \"Timeouts\"\n    },\n    X509TrustManager(Override.X509TrustManagerOverride), ;\n\n    open val paramName: String\n      get() = override.paramName ?: name.replaceFirstChar { it.lowercase(getDefault()) }\n  }\n\n  class DelayingSocketFactory(\n    val onConnect: Socket.(timeout: Int) -> Unit = {},\n    val onRead: Socket.() -> Unit = {},\n    val onWrite: Socket.() -> Unit = {},\n  ) : DelegatingSocketFactory(getDefault()) {\n    override fun createSocket(): Socket {\n      return object : Socket() {\n        override fun connect(\n          endpoint: SocketAddress?,\n          timeout: Int,\n        ) {\n          onConnect(timeout)\n          super.connect(endpoint, timeout)\n        }\n\n        override fun getInputStream(): InputStream {\n          return object : FilterInputStream(super.inputStream) {\n            override fun read(\n              b: ByteArray?,\n              off: Int,\n              len: Int,\n            ): Int {\n              onRead()\n              return super.read(b, off, len)\n            }\n          }\n        }\n\n        override fun getOutputStream(): OutputStream =\n          object : FilterOutputStream(super.outputStream) {\n            override fun write(\n              b: ByteArray?,\n              off: Int,\n              len: Int,\n            ) {\n              onWrite()\n              super.write(b, off, len)\n            }\n          }\n      }\n    }\n  }\n\n  sealed interface Override<T> {\n    fun Interceptor.Chain.value(): T\n\n    fun Interceptor.Chain.withOverride(value: T): Interceptor.Chain\n\n    fun OkHttpClient.Builder.withOverride(value: T): OkHttpClient.Builder\n\n    val paramName: String?\n      get() = null\n\n    val nonDefaultValue: T\n\n    val badValue: T\n\n    fun isDefaultValue(value: T): Boolean\n\n    object DnsOverride : Override<Dns> {\n      override fun Interceptor.Chain.value(): Dns = dns\n\n      override fun Interceptor.Chain.withOverride(value: Dns): Interceptor.Chain = withDns(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: Dns): OkHttpClient.Builder = dns(value)\n\n      override val nonDefaultValue: Dns = Dns { Dns.SYSTEM.lookup(it) }\n\n      override val badValue: Dns = Dns { TODO() }\n\n      override fun isDefaultValue(value: Dns): Boolean = value === Dns.SYSTEM\n    }\n\n    object SocketFactoryOverride : Override<SocketFactory> {\n      override fun Interceptor.Chain.value(): SocketFactory = socketFactory\n\n      override fun Interceptor.Chain.withOverride(value: SocketFactory): Interceptor.Chain = withSocketFactory(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: SocketFactory): OkHttpClient.Builder = socketFactory(value)\n\n      override val nonDefaultValue: SocketFactory = object : DelegatingSocketFactory(getDefault()) {}\n\n      override val badValue: SocketFactory =\n        object : DelegatingSocketFactory(getDefault()) {\n          override fun configureSocket(socket: Socket): Socket = TODO()\n        }\n\n      override fun isDefaultValue(value: SocketFactory): Boolean = value === SocketFactory.getDefault()\n    }\n\n    object AuthenticatorOverride : Override<Authenticator> {\n      override fun Interceptor.Chain.value(): Authenticator = authenticator\n\n      override fun Interceptor.Chain.withOverride(value: Authenticator): Interceptor.Chain = withAuthenticator(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: Authenticator): OkHttpClient.Builder = authenticator(value)\n\n      override val nonDefaultValue: Authenticator = Authenticator { route, response -> response.request }\n\n      override val badValue: Authenticator = Authenticator { route, response -> TODO() }\n\n      override fun isDefaultValue(value: Authenticator): Boolean = value === Authenticator.NONE\n    }\n\n    object CookieJarOverride : Override<CookieJar> {\n      override fun Interceptor.Chain.value(): CookieJar = cookieJar\n\n      override fun Interceptor.Chain.withOverride(value: CookieJar): Interceptor.Chain = withCookieJar(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: CookieJar): OkHttpClient.Builder = cookieJar(value)\n\n      override val nonDefaultValue: CookieJar =\n        object : CookieJar {\n          override fun saveFromResponse(\n            url: HttpUrl,\n            cookies: List<Cookie>,\n          ) {\n          }\n\n          override fun loadForRequest(url: HttpUrl): List<Cookie> = emptyList()\n        }\n\n      override val badValue: CookieJar =\n        object : CookieJar {\n          override fun saveFromResponse(\n            url: HttpUrl,\n            cookies: List<Cookie>,\n          ) {\n          }\n\n          override fun loadForRequest(url: HttpUrl): List<Cookie> = TODO()\n        }\n\n      override fun isDefaultValue(value: CookieJar): Boolean = value === CookieJar.NO_COOKIES\n    }\n\n    object CacheOverride : Override<Cache?> {\n      override fun Interceptor.Chain.value(): Cache? = cache\n\n      override fun Interceptor.Chain.withOverride(value: Cache?): Interceptor.Chain = withCache(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: Cache?): OkHttpClient.Builder = cache(value)\n\n      override val nonDefaultValue: Cache = Cache(FakeFileSystem(), \"/cash\".toPath(), 1)\n\n      override val badValue: Cache =\n        Cache(\n          object : ForwardingFileSystem(FakeFileSystem()) {\n            override fun onPathParameter(\n              path: Path,\n              functionName: String,\n              parameterName: String,\n            ): Path = TODO()\n          },\n          \"/cash\".toPath(),\n          1,\n        )\n\n      override fun isDefaultValue(value: Cache?): Boolean = value == null\n    }\n\n    object ProxyOverride : Override<Proxy?> {\n      override fun Interceptor.Chain.value(): java.net.Proxy? = proxy\n\n      override fun Interceptor.Chain.withOverride(value: java.net.Proxy?): Interceptor.Chain = withProxy(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: java.net.Proxy?): OkHttpClient.Builder = proxy(value)\n\n      override val nonDefaultValue: java.net.Proxy? = java.net.Proxy.NO_PROXY\n\n      override val badValue: java.net.Proxy? =\n        java.net.Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(\"proxy.example.com\", 1003))\n\n      override fun isDefaultValue(value: java.net.Proxy?): Boolean = value == null\n    }\n\n    object ProxySelectorOverride : Override<ProxySelector> {\n      override fun Interceptor.Chain.value(): ProxySelector = proxySelector\n\n      override fun Interceptor.Chain.withOverride(value: ProxySelector): Interceptor.Chain = withProxySelector(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: ProxySelector): OkHttpClient.Builder = proxySelector(value)\n\n      override val nonDefaultValue: ProxySelector =\n        object : ProxySelector() {\n          override fun select(uri: URI?): MutableList<Proxy> = mutableListOf(java.net.Proxy.NO_PROXY)\n\n          override fun connectFailed(\n            uri: URI?,\n            sa: SocketAddress?,\n            ioe: java.io.IOException?,\n          ) {\n          }\n        }\n\n      override val badValue: ProxySelector =\n        object : ProxySelector() {\n          override fun select(uri: URI?): MutableList<Proxy> = TODO()\n\n          override fun connectFailed(\n            uri: URI?,\n            sa: SocketAddress?,\n            ioe: java.io.IOException?,\n          ) {\n          }\n        }\n\n      override fun isDefaultValue(value: ProxySelector): Boolean = value === ProxySelector.getDefault()\n    }\n\n    object ProxyAuthenticatorOverride : Override<Authenticator> {\n      override fun Interceptor.Chain.value(): Authenticator = proxyAuthenticator\n\n      override fun Interceptor.Chain.withOverride(value: Authenticator): Interceptor.Chain = withProxyAuthenticator(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: Authenticator): OkHttpClient.Builder = proxyAuthenticator(value)\n\n      override val nonDefaultValue: Authenticator = Authenticator { route, response -> response.request }\n\n      override val badValue: Authenticator = Authenticator { route, response -> TODO() }\n\n      override fun isDefaultValue(value: Authenticator): Boolean = value === Authenticator.NONE\n    }\n\n    object SslSocketFactoryOverride : Override<SSLSocketFactory?> {\n      override fun Interceptor.Chain.value(): SSLSocketFactory? = sslSocketFactoryOrNull\n\n      override fun Interceptor.Chain.withOverride(value: SSLSocketFactory?): Interceptor.Chain =\n        withSslSocketFactory(value, x509TrustManagerOrNull)\n\n      override fun OkHttpClient.Builder.withOverride(value: SSLSocketFactory?): OkHttpClient.Builder =\n        sslSocketFactory(value!!, x509TrustManagerOrNull!!)\n\n      override val nonDefaultValue: SSLSocketFactory =\n        object :\n          DelegatingSSLSocketFactory(Platform.get().newSslSocketFactory(Platform.get().platformTrustManager())) {}\n\n      override val badValue: SSLSocketFactory =\n        object : DelegatingSSLSocketFactory(Platform.get().newSslSocketFactory(Platform.get().platformTrustManager())) {\n          override fun configureSocket(sslSocket: SSLSocket): SSLSocket = TODO()\n        }\n\n      override fun isDefaultValue(value: SSLSocketFactory?): Boolean = value !is DelegatingSSLSocketFactory\n    }\n\n    object X509TrustManagerOverride : Override<X509TrustManager?> {\n      override val paramName: String = \"sslSocketFactory\"\n\n      override fun Interceptor.Chain.value(): X509TrustManager? = x509TrustManagerOrNull\n\n      override fun Interceptor.Chain.withOverride(value: X509TrustManager?): Interceptor.Chain =\n        withSslSocketFactory(Platform.get().newSslSocketFactory(value!!), value)\n\n      override fun OkHttpClient.Builder.withOverride(value: X509TrustManager?): OkHttpClient.Builder =\n        sslSocketFactory(Platform.get().newSslSocketFactory(value!!), value)\n\n      override val nonDefaultValue: X509TrustManager =\n        object : X509TrustManager {\n          override fun checkClientTrusted(\n            x509Certificates: Array<X509Certificate>,\n            s: String,\n          ) {\n          }\n\n          override fun checkServerTrusted(\n            x509Certificates: Array<X509Certificate>,\n            s: String,\n          ) {\n          }\n\n          override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()\n        }\n\n      override val badValue: X509TrustManager =\n        object : X509TrustManager {\n          override fun checkClientTrusted(\n            x509Certificates: Array<X509Certificate>,\n            s: String,\n          ) {\n          }\n\n          override fun checkServerTrusted(\n            x509Certificates: Array<X509Certificate>,\n            s: String,\n          ) {\n            TODO()\n          }\n\n          override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()\n        }\n\n      override fun isDefaultValue(value: X509TrustManager?): Boolean =\n        !value\n          ?.javaClass\n          ?.name\n          .orEmpty()\n          .startsWith(\"okhttp\")\n    }\n\n    object HostnameVerifierOverride : Override<HostnameVerifier> {\n      override fun Interceptor.Chain.value(): HostnameVerifier = hostnameVerifier\n\n      override fun Interceptor.Chain.withOverride(value: HostnameVerifier): Interceptor.Chain = withHostnameVerifier(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: HostnameVerifier): OkHttpClient.Builder = hostnameVerifier(value)\n\n      override val nonDefaultValue: HostnameVerifier = HostnameVerifier { _, _ -> true }\n\n      override val badValue: HostnameVerifier = HostnameVerifier { _, _ -> TODO() }\n\n      override fun isDefaultValue(value: HostnameVerifier): Boolean = value === okhttp3.internal.tls.OkHostnameVerifier\n    }\n\n    object CertificatePinnerOverride : Override<CertificatePinner> {\n      override fun Interceptor.Chain.value(): CertificatePinner = certificatePinner\n\n      override fun Interceptor.Chain.withOverride(value: CertificatePinner): Interceptor.Chain = withCertificatePinner(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: CertificatePinner): OkHttpClient.Builder = certificatePinner(value)\n\n      override val nonDefaultValue: CertificatePinner =\n        CertificatePinner\n          .Builder()\n          .add(\"publicobject.com\", \"sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=\")\n          .build()\n\n      override val badValue: CertificatePinner =\n        CertificatePinner.Builder().add(\"localhost\", \"sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=\").build()\n\n      override fun isDefaultValue(value: CertificatePinner): Boolean = value.pins.isEmpty()\n    }\n\n    object ConnectionPoolOverride : Override<ConnectionPool> {\n      override fun Interceptor.Chain.value(): ConnectionPool = connectionPool\n\n      override fun Interceptor.Chain.withOverride(value: ConnectionPool): Interceptor.Chain = withConnectionPool(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: ConnectionPool): OkHttpClient.Builder = connectionPool(value)\n\n      override val nonDefaultValue: ConnectionPool = ConnectionPool(keepAliveDuration = 1, timeUnit = TimeUnit.MINUTES)\n\n      override val badValue: ConnectionPool =\n        ConnectionPool(\n          keepAliveDuration = 1,\n          timeUnit = TimeUnit.MINUTES,\n          connectionListener =\n            object : ConnectionListener() {\n              override fun connectStart(\n                route: Route,\n                call: Call,\n              ): Unit = TODO()\n            },\n        )\n\n      override fun isDefaultValue(value: ConnectionPool): Boolean = value.delegate.keepAliveDurationNs == 5.minutes.inWholeNanoseconds\n    }\n\n    object ConnectTimeoutOverride : Override<Int> {\n      override fun Interceptor.Chain.value(): Int = connectTimeoutMillis()\n\n      override fun Interceptor.Chain.withOverride(value: Int): Interceptor.Chain = withConnectTimeout(value, TimeUnit.MILLISECONDS)\n\n      override fun OkHttpClient.Builder.withOverride(value: Int): OkHttpClient.Builder =\n        connectTimeout(value.toLong(), TimeUnit.MILLISECONDS)\n\n      override val nonDefaultValue: Int = 5000\n\n      override val badValue: Int\n        get() = 10\n\n      override fun isDefaultValue(value: Int): Boolean = value == 10000\n    }\n\n    object ReadTimeoutOverride : Override<Int> {\n      override fun Interceptor.Chain.value(): Int = readTimeoutMillis()\n\n      override fun Interceptor.Chain.withOverride(value: Int): Interceptor.Chain = withReadTimeout(value, TimeUnit.MILLISECONDS)\n\n      override fun OkHttpClient.Builder.withOverride(value: Int): OkHttpClient.Builder = readTimeout(value.toLong(), TimeUnit.MILLISECONDS)\n\n      override val nonDefaultValue: Int = 5000\n\n      override val badValue: Int\n        get() = 10\n\n      override fun isDefaultValue(value: Int): Boolean = value == 10000\n    }\n\n    object WriteTimeoutOverride : Override<Int> {\n      override fun Interceptor.Chain.value(): Int = writeTimeoutMillis()\n\n      override fun Interceptor.Chain.withOverride(value: Int): Interceptor.Chain = withWriteTimeout(value, TimeUnit.MILLISECONDS)\n\n      override fun OkHttpClient.Builder.withOverride(value: Int): OkHttpClient.Builder = writeTimeout(value.toLong(), TimeUnit.MILLISECONDS)\n\n      override val nonDefaultValue: Int = 5000\n\n      override val badValue: Int\n        get() = 10\n\n      override fun isDefaultValue(value: Int): Boolean = value == 10000\n    }\n\n    object RetryOnConnectionFailureOverride : Override<Boolean> {\n      override fun Interceptor.Chain.value(): Boolean = retryOnConnectionFailure\n\n      override fun Interceptor.Chain.withOverride(value: Boolean): Interceptor.Chain = withRetryOnConnectionFailure(value)\n\n      override fun OkHttpClient.Builder.withOverride(value: Boolean): OkHttpClient.Builder = retryOnConnectionFailure(value)\n\n      override val nonDefaultValue: Boolean = false\n\n      override val badValue: Boolean\n        get() = false\n\n      override fun isDefaultValue(value: Boolean): Boolean = value\n    }\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/InterceptorTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isSameInstanceAs\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.net.SocketTimeoutException\nimport java.time.Duration\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.SynchronousQueue\nimport java.util.concurrent.ThreadPoolExecutor\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicReference\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okhttp3.TestUtil.assertSuppressed\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.ForwardingSink\nimport okio.ForwardingSource\nimport okio.GzipSink\nimport okio.Sink\nimport okio.Source\nimport okio.buffer\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Tag(\"Slow\")\nclass InterceptorTest {\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private var client = clientTestRule.newClient()\n  private val callback = RecordingCallback()\n\n  @Test\n  fun applicationInterceptorsCanShortCircuitResponses() {\n    server.close() // Accept no connections.\n    val request =\n      Request\n        .Builder()\n        .url(\"https://localhost:1/\")\n        .build()\n    val interceptorResponse =\n      Response\n        .Builder()\n        .request(request)\n        .protocol(Protocol.HTTP_1_1)\n        .code(200)\n        .message(\"Intercepted!\")\n        .body(\"abc\".toResponseBody(\"text/plain; charset=utf-8\".toMediaType()))\n        .build()\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(Interceptor { chain: Interceptor.Chain? -> interceptorResponse })\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response).isSameInstanceAs(interceptorResponse)\n  }\n\n  @Test\n  fun networkInterceptorsCannotShortCircuitResponses() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(500)\n        .build(),\n    )\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        Response\n          .Builder()\n          .request(chain.request())\n          .protocol(Protocol.HTTP_1_1)\n          .code(200)\n          .message(\"Intercepted!\")\n          .body(\"abc\".toResponseBody(\"text/plain; charset=utf-8\".toMediaType()))\n          .build()\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(interceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    assertFailsWith<IllegalStateException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"network interceptor $interceptor must call proceed() exactly once\",\n      )\n    }\n  }\n\n  @Test\n  fun networkInterceptorsCannotCallProceedMultipleTimes() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        chain.proceed(chain.request())\n        chain.proceed(chain.request())\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(interceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    assertFailsWith<IllegalStateException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"network interceptor $interceptor must call proceed() exactly once\",\n      )\n    }\n  }\n\n  @Test\n  fun networkInterceptorsCannotChangeServerAddress() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(500)\n        .build(),\n    )\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        val address = chain.connection()!!.route().address\n        val sameHost = address.url.host\n        val differentPort = address.url.port + 1\n        chain.proceed(\n          chain\n            .request()\n            .newBuilder()\n            .url(\"http://$sameHost:$differentPort/\")\n            .build(),\n        )\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(interceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    assertFailsWith<IllegalStateException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"network interceptor $interceptor must retain the same host and port\",\n      )\n    }\n  }\n\n  @Test\n  fun networkInterceptorsHaveConnectionAccess() {\n    server.enqueue(MockResponse())\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        val connection = chain.connection()\n        assertThat(connection).isNotNull()\n        chain.proceed(chain.request())\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(interceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    client.newCall(request).execute()\n  }\n\n  @Test\n  fun networkInterceptorsObserveNetworkHeaders() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(gzip(\"abcabcabc\"))\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        // The network request has everything: User-Agent, Host, Accept-Encoding.\n        val networkRequest = chain.request()\n        assertThat(networkRequest.header(\"User-Agent\")).isNotNull()\n        assertThat(networkRequest.header(\"Host\")).isEqualTo(\n          server.hostName + \":\" + server.port,\n        )\n        assertThat(networkRequest.header(\"Accept-Encoding\")).isNotNull()\n\n        // The network response also has everything, including the raw gzipped content.\n        val networkResponse = chain.proceed(networkRequest)\n        assertThat(networkResponse.header(\"Content-Encoding\")).isEqualTo(\"gzip\")\n        networkResponse\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(interceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n\n    // No extra headers in the application's request.\n    assertThat(request.header(\"User-Agent\")).isNull()\n    assertThat(request.header(\"Host\")).isNull()\n    assertThat(request.header(\"Accept-Encoding\")).isNull()\n\n    // No extra headers in the application's response.\n    val response = client.newCall(request).execute()\n    assertThat(request.header(\"Content-Encoding\")).isNull()\n    assertThat(response.body.string()).isEqualTo(\"abcabcabc\")\n  }\n\n  @Test\n  fun networkInterceptorsCanChangeRequestMethodFromGetToPost() {\n    server.enqueue(MockResponse())\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        val originalRequest = chain.request()\n        val mediaType = \"text/plain\".toMediaType()\n        val body = \"abc\".toRequestBody(mediaType)\n        chain.proceed(\n          originalRequest\n            .newBuilder()\n            .method(\"POST\", body)\n            .header(\"Content-Type\", mediaType.toString())\n            .header(\"Content-Length\", body.contentLength().toString())\n            .build(),\n        )\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(interceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .get()\n        .build()\n    client.newCall(request).execute()\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun applicationInterceptorsRewriteRequestToServer() {\n    rewriteRequestToServer(false)\n  }\n\n  @Test\n  fun networkInterceptorsRewriteRequestToServer() {\n    rewriteRequestToServer(true)\n  }\n\n  private fun rewriteRequestToServer(network: Boolean) {\n    server.enqueue(MockResponse())\n    addInterceptor(network) { chain: Interceptor.Chain ->\n      val originalRequest = chain.request()\n      chain.proceed(\n        originalRequest\n          .newBuilder()\n          .method(\"POST\", uppercase(originalRequest.body))\n          .addHeader(\"OkHttp-Intercepted\", \"yep\")\n          .build(),\n      )\n    }\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .addHeader(\"Original-Header\", \"foo\")\n        .method(\"PUT\", \"abc\".toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    client.newCall(request).execute()\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"ABC\")\n    assertThat(recordedRequest.headers[\"Original-Header\"]).isEqualTo(\"foo\")\n    assertThat(recordedRequest.headers[\"OkHttp-Intercepted\"]).isEqualTo(\"yep\")\n    assertThat(recordedRequest.method).isEqualTo(\"POST\")\n  }\n\n  @Test\n  fun applicationInterceptorsRewriteResponseFromServer() {\n    rewriteResponseFromServer(false)\n  }\n\n  @Test\n  fun networkInterceptorsRewriteResponseFromServer() {\n    rewriteResponseFromServer(true)\n  }\n\n  private fun rewriteResponseFromServer(network: Boolean) {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Original-Header: foo\")\n        .body(\"abc\")\n        .build(),\n    )\n    addInterceptor(network) { chain: Interceptor.Chain ->\n      val originalResponse = chain.proceed(chain.request())\n      originalResponse\n        .newBuilder()\n        .body(uppercase(originalResponse.body))\n        .addHeader(\"OkHttp-Intercepted\", \"yep\")\n        .build()\n    }\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"ABC\")\n    assertThat(response.header(\"OkHttp-Intercepted\")).isEqualTo(\"yep\")\n    assertThat(response.header(\"Original-Header\")).isEqualTo(\"foo\")\n  }\n\n  @Test\n  fun multipleApplicationInterceptors() {\n    multipleInterceptors(false)\n  }\n\n  @Test\n  fun multipleNetworkInterceptors() {\n    multipleInterceptors(true)\n  }\n\n  private fun multipleInterceptors(network: Boolean) {\n    server.enqueue(MockResponse())\n    addInterceptor(network) { chain: Interceptor.Chain ->\n      val originalRequest = chain.request()\n      val originalResponse =\n        chain.proceed(\n          originalRequest\n            .newBuilder()\n            .addHeader(\"Request-Interceptor\", \"Android\") // 1. Added first.\n            .build(),\n        )\n      originalResponse\n        .newBuilder()\n        .addHeader(\"Response-Interceptor\", \"Donut\") // 4. Added last.\n        .build()\n    }\n    addInterceptor(network) { chain: Interceptor.Chain ->\n      val originalRequest = chain.request()\n      val originalResponse =\n        chain.proceed(\n          originalRequest\n            .newBuilder()\n            .addHeader(\"Request-Interceptor\", \"Bob\") // 2. Added second.\n            .build(),\n        )\n      originalResponse\n        .newBuilder()\n        .addHeader(\"Response-Interceptor\", \"Cupcake\") // 3. Added third.\n        .build()\n    }\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.headers(\"Response-Interceptor\"))\n      .containsExactly(\"Cupcake\", \"Donut\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers.values(\"Request-Interceptor\"))\n      .containsExactly(\"Android\", \"Bob\")\n  }\n\n  @Test\n  fun asyncApplicationInterceptors() {\n    asyncInterceptors(false)\n  }\n\n  @Test\n  fun asyncNetworkInterceptors() {\n    asyncInterceptors(true)\n  }\n\n  private fun asyncInterceptors(network: Boolean) {\n    server.enqueue(MockResponse())\n    addInterceptor(network) { chain: Interceptor.Chain ->\n      val originalResponse = chain.proceed(chain.request())\n      originalResponse\n        .newBuilder()\n        .addHeader(\"OkHttp-Intercepted\", \"yep\")\n        .build()\n    }\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    client.newCall(request).enqueue(callback)\n    callback\n      .await(request.url)\n      .assertCode(200)\n      .assertHeader(\"OkHttp-Intercepted\", \"yep\")\n  }\n\n  @Test\n  fun applicationInterceptorsCanMakeMultipleRequestsToServer() {\n    server.enqueue(MockResponse.Builder().body(\"a\").build())\n    server.enqueue(MockResponse.Builder().body(\"b\").build())\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            val response1 = chain.proceed(chain.request())\n            response1.body.close()\n            chain.proceed(chain.request())\n          },\n        ).build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(\"b\").isEqualTo(response.body.string())\n  }\n\n  /** Make sure interceptors can interact with the OkHttp client.  */\n  @Test\n  fun interceptorMakesAnUnrelatedRequest() {\n    server.enqueue(MockResponse.Builder().body(\"a\").build()) // Fetched by interceptor.\n    server.enqueue(MockResponse.Builder().body(\"b\").build()) // Fetched directly.\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            if (chain.request().url.encodedPath == \"/b\") {\n              val requestA =\n                Request\n                  .Builder()\n                  .url(server.url(\"/a\"))\n                  .build()\n              val responseA = client.newCall(requestA).execute()\n              assertThat(responseA.body.string()).isEqualTo(\"a\")\n            }\n            chain.proceed(chain.request())\n          },\n        ).build()\n    val requestB =\n      Request\n        .Builder()\n        .url(server.url(\"/b\"))\n        .build()\n    val responseB = client.newCall(requestB).execute()\n    assertThat(responseB.body.string()).isEqualTo(\"b\")\n  }\n\n  /** Make sure interceptors can interact with the OkHttp client asynchronously.  */\n  @Test\n  fun interceptorMakesAnUnrelatedAsyncRequest() {\n    server.enqueue(MockResponse.Builder().body(\"a\").build()) // Fetched by interceptor.\n    server.enqueue(MockResponse.Builder().body(\"b\").build()) // Fetched directly.\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            if (chain.request().url.encodedPath == \"/b\") {\n              val requestA =\n                Request\n                  .Builder()\n                  .url(server.url(\"/a\"))\n                  .build()\n              try {\n                val callbackA = RecordingCallback()\n                client.newCall(requestA).enqueue(callbackA)\n                callbackA.await(requestA.url).assertBody(\"a\")\n              } catch (e: Exception) {\n                throw RuntimeException(e)\n              }\n            }\n            chain.proceed(chain.request())\n          },\n        ).build()\n    val requestB =\n      Request\n        .Builder()\n        .url(server.url(\"/b\"))\n        .build()\n    val callbackB = RecordingCallback()\n    client.newCall(requestB).enqueue(callbackB)\n    callbackB.await(requestB.url).assertBody(\"b\")\n  }\n\n  @Test\n  fun applicationInterceptorThrowsRuntimeExceptionSynchronous() {\n    interceptorThrowsRuntimeExceptionSynchronous(false)\n  }\n\n  @Test\n  fun networkInterceptorThrowsRuntimeExceptionSynchronous() {\n    interceptorThrowsRuntimeExceptionSynchronous(true)\n  }\n\n  /**\n   * When an interceptor throws an unexpected exception, synchronous callers can catch it and deal\n   * with it.\n   */\n  private fun interceptorThrowsRuntimeExceptionSynchronous(network: Boolean) {\n    addInterceptor(network) { chain: Interceptor.Chain? -> throw RuntimeException(\"boom!\") }\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    assertFailsWith<RuntimeException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"boom!\")\n    }\n  }\n\n  @Test\n  fun networkInterceptorModifiedRequestIsReturned() {\n    server.enqueue(MockResponse())\n    val modifyHeaderInterceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        val modifiedRequest =\n          chain\n            .request()\n            .newBuilder()\n            .header(\"User-Agent\", \"intercepted request\")\n            .build()\n        chain.proceed(modifiedRequest)\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(modifyHeaderInterceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"User-Agent\", \"user request\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.request.header(\"User-Agent\")).isNotNull()\n    assertThat(response.request.header(\"User-Agent\")).isEqualTo(\"user request\")\n    assertThat(response.networkResponse!!.request.header(\"User-Agent\")).isEqualTo(\n      \"intercepted request\",\n    )\n  }\n\n  @Test\n  fun applicationInterceptorThrowsRuntimeExceptionAsynchronous() {\n    interceptorThrowsRuntimeExceptionAsynchronous(false)\n  }\n\n  @Test\n  fun networkInterceptorThrowsRuntimeExceptionAsynchronous() {\n    interceptorThrowsRuntimeExceptionAsynchronous(true)\n  }\n\n  /**\n   * When an interceptor throws an unexpected exception, asynchronous calls are canceled. The\n   * exception goes to the uncaught exception handler.\n   */\n  private fun interceptorThrowsRuntimeExceptionAsynchronous(network: Boolean) {\n    val boom = RuntimeException(\"boom!\")\n    addInterceptor(network) { chain: Interceptor.Chain? -> throw boom }\n    val executor = ExceptionCatchingExecutor()\n    client =\n      client\n        .newBuilder()\n        .dispatcher(Dispatcher(executor))\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    call.enqueue(callback)\n    val recordedResponse = callback.await(server.url(\"/\"))\n    assertThat(recordedResponse.failure, \"canceled due to java.lang.RuntimeException: boom!\")\n    assertThat(recordedResponse.failure?.cause).isEqualTo(boom)\n    assertThat(call.isCanceled()).isTrue()\n    assertThat(executor.takeException()).isEqualTo(boom)\n  }\n\n  @Test\n  fun networkInterceptorReturnsConnectionOnEmptyBody() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onResponseEnd(ShutdownConnection)\n        .addHeader(\"Connection\", \"Close\")\n        .build(),\n    )\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        val response = chain.proceed(chain.request())\n        assertThat(chain.connection()).isNotNull()\n        response\n      }\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(interceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val response = client.newCall(request).execute()\n    response.body.close()\n  }\n\n  @Test\n  fun connectTimeout() {\n    val interceptor1 =\n      Interceptor { chainA: Interceptor.Chain ->\n        assertThat(chainA.connectTimeoutMillis()).isEqualTo(5000)\n        val chainB = chainA.withConnectTimeout(100, TimeUnit.MILLISECONDS)\n        assertThat(chainB.connectTimeoutMillis()).isEqualTo(100)\n        chainB.proceed(chainA.request())\n      }\n    val interceptor2 =\n      Interceptor { chain: Interceptor.Chain ->\n        assertThat(chain.connectTimeoutMillis()).isEqualTo(100)\n        chain.proceed(chain.request())\n      }\n    client =\n      client\n        .newBuilder()\n        .connectTimeout(Duration.ofSeconds(5))\n        .addInterceptor(interceptor1)\n        .addInterceptor(interceptor2)\n        .build()\n    val request1 =\n      Request\n        .Builder()\n        .url(\"http://\" + TestUtil.UNREACHABLE_ADDRESS_IPV4)\n        .build()\n    val call = client.newCall(request1)\n    val startNanos = System.nanoTime()\n    assertFailsWith<SocketTimeoutException> {\n      call.execute()\n    }\n    val elapsedNanos = System.nanoTime() - startNanos\n    org.junit.jupiter.api.Assertions.assertTrue(\n      elapsedNanos < TimeUnit.SECONDS.toNanos(5),\n      \"Timeout should have taken ~100ms but was \" + elapsedNanos / 1e6 + \" ms\",\n    )\n  }\n\n  @Test\n  fun chainWithReadTimeout() {\n    val interceptor1 =\n      Interceptor { chainA: Interceptor.Chain ->\n        assertThat(chainA.readTimeoutMillis()).isEqualTo(5000)\n        val chainB = chainA.withReadTimeout(100, TimeUnit.MILLISECONDS)\n        assertThat(chainB.readTimeoutMillis()).isEqualTo(100)\n        chainB.proceed(chainA.request())\n      }\n    val interceptor2 =\n      Interceptor { chain: Interceptor.Chain ->\n        assertThat(chain.readTimeoutMillis()).isEqualTo(100)\n        chain.proceed(chain.request())\n      }\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofSeconds(5))\n        .addInterceptor(interceptor1)\n        .addInterceptor(interceptor2)\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .throttleBody(1, 1, TimeUnit.SECONDS)\n        .build(),\n    )\n    val request1 =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request1)\n    val response = call.execute()\n    val body = response.body\n    assertFailsWith<SocketTimeoutException> {\n      body.string()\n    }\n  }\n\n  @Test\n  fun networkInterceptorCannotChangeReadTimeout() {\n    addInterceptor(true) { chain: Interceptor.Chain ->\n      chain\n        .withReadTimeout(\n          100,\n          TimeUnit.MILLISECONDS,\n        ).proceed(chain.request())\n    }\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val call = client.newCall(request1)\n    assertFailsWith<IllegalStateException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Timeouts can't be adjusted in a network interceptor\")\n    }\n  }\n\n  @Test\n  fun networkInterceptorCannotChangeWriteTimeout() {\n    addInterceptor(true) { chain: Interceptor.Chain ->\n      chain\n        .withWriteTimeout(\n          100,\n          TimeUnit.MILLISECONDS,\n        ).proceed(chain.request())\n    }\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val call = client.newCall(request1)\n    assertFailsWith<IllegalStateException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Timeouts can't be adjusted in a network interceptor\")\n    }\n  }\n\n  @Test\n  fun networkInterceptorCannotChangeConnectTimeout() {\n    addInterceptor(true) { chain: Interceptor.Chain ->\n      chain\n        .withConnectTimeout(\n          100,\n          TimeUnit.MILLISECONDS,\n        ).proceed(chain.request())\n    }\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val call = client.newCall(request1)\n    assertFailsWith<IllegalStateException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Timeouts can't be adjusted in a network interceptor\")\n    }\n  }\n\n  @Test\n  fun chainWithWriteTimeout() {\n    val interceptor1 =\n      Interceptor { chainA: Interceptor.Chain ->\n        assertThat(chainA.writeTimeoutMillis()).isEqualTo(5000)\n        val chainB = chainA.withWriteTimeout(100, TimeUnit.MILLISECONDS)\n        assertThat(chainB.writeTimeoutMillis()).isEqualTo(100)\n        chainB.proceed(chainA.request())\n      }\n    val interceptor2 =\n      Interceptor { chain: Interceptor.Chain ->\n        assertThat(chain.writeTimeoutMillis()).isEqualTo(100)\n        chain.proceed(chain.request())\n      }\n    client =\n      client\n        .newBuilder()\n        .writeTimeout(Duration.ofSeconds(5))\n        .addInterceptor(interceptor1)\n        .addInterceptor(interceptor2)\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .throttleBody(1, 1, TimeUnit.SECONDS)\n        .build(),\n    )\n    val data = ByteArray(2 * 1024 * 1024) // 2 MiB.\n    val request1 =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(data.toRequestBody(\"text/plain\".toMediaType()))\n        .build()\n    val call = client.newCall(request1)\n    assertFailsWith<SocketTimeoutException> {\n      call.execute() // we want this call to throw a SocketTimeoutException\n    }\n  }\n\n  @Test\n  fun chainCanCancelCall() {\n    val callRef = AtomicReference<Call>()\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        val call = chain.call()\n        callRef.set(call)\n        assertThat(call.isCanceled()).isFalse()\n        call.cancel()\n        assertThat(call.isCanceled()).isTrue()\n        chain.proceed(chain.request())\n      }\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(interceptor)\n        .build()\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n    assertThat(callRef.get()).isSameInstanceAs(call)\n  }\n\n  private fun uppercase(original: RequestBody?): RequestBody =\n    object : RequestBody() {\n      override fun contentType(): MediaType? = original!!.contentType()\n\n      override fun contentLength(): Long = original!!.contentLength()\n\n      override fun writeTo(sink: BufferedSink) {\n        val uppercase = uppercase(sink)\n        val bufferedSink = uppercase.buffer()\n        original!!.writeTo(bufferedSink)\n        bufferedSink.emit()\n      }\n    }\n\n  private fun uppercase(original: BufferedSink): Sink =\n    object : ForwardingSink(original) {\n      override fun write(\n        source: Buffer,\n        byteCount: Long,\n      ) {\n        original.writeUtf8(source.readUtf8(byteCount).uppercase())\n      }\n    }\n\n  private fun gzip(data: String): Buffer {\n    val result = Buffer()\n    val sink = GzipSink(result).buffer()\n    sink.writeUtf8(data)\n    sink.close()\n    return result\n  }\n\n  private fun addInterceptor(\n    network: Boolean,\n    interceptor: Interceptor,\n  ) {\n    val builder = client.newBuilder()\n    if (network) {\n      builder.addNetworkInterceptor(interceptor)\n    } else {\n      builder.addInterceptor(interceptor)\n    }\n    client = builder.build()\n  }\n\n  /** Catches exceptions that are otherwise headed for the uncaught exception handler.  */\n  private class ExceptionCatchingExecutor : ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, SynchronousQueue()) {\n    private val exceptions: BlockingQueue<Exception> = LinkedBlockingQueue()\n\n    override fun execute(runnable: Runnable) {\n      super.execute {\n        try {\n          runnable.run()\n        } catch (e: Exception) {\n          exceptions.add(e)\n        }\n      }\n    }\n\n    fun takeException(): Exception = exceptions.take()\n  }\n\n  companion object {\n    fun uppercase(original: ResponseBody): ResponseBody =\n      object : ResponseBody() {\n        override fun contentType() = original.contentType()\n\n        override fun contentLength() = original.contentLength()\n\n        override fun source() = uppercase(original.source()).buffer()\n      }\n\n    private fun uppercase(original: Source): Source {\n      return object : ForwardingSource(original) {\n        override fun read(\n          sink: Buffer,\n          byteCount: Long,\n        ): Long {\n          val mixedCase = Buffer()\n          val count = original.read(mixedCase, byteCount)\n          sink.writeUtf8(mixedCase.readUtf8().uppercase())\n          return count\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/JSSETest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotEmpty\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.TestUtil.assumeNetwork\nimport okhttp3.internal.connection\nimport okhttp3.testing.PlatformRule\nimport okhttp3.testing.PlatformVersion\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertNotEquals\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass JSSETest {\n  @JvmField @RegisterExtension\n  var platform = PlatformRule()\n\n  @JvmField @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n\n  var client = clientTestRule.newClient()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    // Default after JDK 14, but we are avoiding tests that assume special setup.\n    // System.setProperty(\"jdk.tls.client.enableSessionTicketExtension\", \"true\")\n    // System.setProperty(\"jdk.tls.server.enableSessionTicketExtension\", \"true\")\n\n    platform.assumeJdk9()\n  }\n\n  @Test\n  fun testTlsv13Works() {\n    // https://docs.oracle.com/en/java/javase/14/security/java-secure-socket-extension-jsse-reference-guide.html\n    // TODO test jdk.tls.client.enableSessionTicketExtension\n    // TODO check debugging information\n\n    enableTls()\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val request = Request(server.url(\"/\"))\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n      if (PlatformVersion.majorVersion > 11) {\n        assertEquals(TlsVersion.TLS_1_3, response.handshake?.tlsVersion)\n      }\n      if (PlatformVersion.majorVersion > 8) {\n        assertEquals(Protocol.HTTP_2, response.protocol)\n      }\n\n      assertThat(\n        response.connection\n          .socket()\n          .javaClass.name,\n      ).isEqualTo(\n        \"sun.security.ssl.SSLSocketImpl\",\n      )\n    }\n  }\n\n  @Test\n  fun testSupportedProtocols() {\n    val factory = SSLSocketFactory.getDefault()\n    assertThat(factory.javaClass.name).isEqualTo(\"sun.security.ssl.SSLSocketFactoryImpl\")\n    val s = factory.createSocket() as SSLSocket\n\n    when {\n      PlatformVersion.majorVersion > 11 -> {\n        assertThat(s.enabledProtocols.toList()).containsExactly(\n          \"TLSv1.3\",\n          \"TLSv1.2\",\n        )\n      }\n\n      // Not much we can guarantee on JDK 11.\n      PlatformVersion.majorVersion == 11 -> {\n        assertThat(s.enabledProtocols.toList()).contains(\n          \"TLSv1.2\",\n        )\n      }\n\n      // JDK 8 291 removed older versions\n      // See https://java.com/en/jre-jdk-cryptoroadmap.html\n      PlatformVersion.majorVersion == 8 -> {\n        assertThat(s.enabledProtocols.toList()).contains(\n          \"TLSv1.2\",\n        )\n      }\n\n      else -> {\n        assertThat(s.enabledProtocols.toList()).containsExactly(\n          \"TLSv1.3\",\n          \"TLSv1.2\",\n          \"TLSv1.1\",\n          \"TLSv1\",\n        )\n      }\n    }\n  }\n\n  @Test\n  @Disabled\n  fun testFacebook() {\n    val sessionIds = mutableListOf<String>()\n\n    assumeNetwork()\n\n    client =\n      client\n        .newBuilder()\n        .eventListenerFactory(\n          clientTestRule.wrap(\n            object : EventListener() {\n              override fun connectionAcquired(\n                call: Call,\n                connection: Connection,\n              ) {\n                val sslSocket = connection.socket() as SSLSocket\n\n                sessionIds.add(\n                  sslSocket.session.id\n                    .toByteString()\n                    .hex(),\n                )\n              }\n            },\n          ),\n        ).build()\n\n    val request = Request.Builder().url(\"https://facebook.com/robots.txt\").build()\n\n    client.newCall(request).execute().use {\n      assertThat(it.protocol).isEqualTo(Protocol.HTTP_2)\n      assertThat(it.handshake!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n    }\n\n    client.connectionPool.evictAll()\n    assertEquals(0, client.connectionPool.connectionCount())\n\n    client.newCall(request).execute().use {\n      assertThat(it.protocol).isEqualTo(Protocol.HTTP_2)\n      assertThat(it.handshake!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n    }\n\n    assertEquals(2, sessionIds.size)\n    assertNotEquals(sessionIds[0], sessionIds[1])\n    assertThat(sessionIds[0]).isNotEmpty()\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/KotlinDeprecationErrorTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.File\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.Socket\nimport java.net.URI\nimport java.net.URL\nimport java.nio.charset.Charset\nimport java.security.KeyPair\nimport java.security.Principal\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\nimport javax.net.ServerSocketFactory\nimport javax.net.SocketFactory\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509KeyManager\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.logging.HttpLoggingInterceptor\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport okhttp3.mockwebserver.PushPromise\nimport okhttp3.mockwebserver.RecordedRequest\nimport okhttp3.mockwebserver.SocketPolicy\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport okio.Buffer\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\n\n/**\n * Access every declaration that is deprecated with [DeprecationLevel.ERROR]. Although new Kotlin\n * code shouldn't use these, they're necessary for clients migrating from OkHttp 3.x and this test\n * ensures the symbols remain available and with the expected parameter and return types.\n */\n@Suppress(\n  \"DEPRECATION_ERROR\",\n  \"UNUSED_VALUE\",\n  \"UNUSED_VARIABLE\",\n  \"VARIABLE_WITH_REDUNDANT_INITIALIZER\",\n)\nclass KotlinDeprecationErrorTest {\n  private val factory = TestValueFactory()\n\n  @AfterEach\n  fun tearDown() {\n    factory.close()\n  }\n\n  @Test @Disabled\n  fun address() {\n    val address: Address = factory.newAddress()\n    val url: HttpUrl = address.url()\n    val dns: Dns = address.dns()\n    val socketFactory: SocketFactory = address.socketFactory()\n    val proxyAuthenticator: Authenticator = address.proxyAuthenticator()\n    val protocols: List<Protocol> = address.protocols()\n    val connectionSpecs: List<ConnectionSpec> = address.connectionSpecs()\n    val proxySelector: ProxySelector = address.proxySelector()\n    val proxy: Proxy? = address.proxy()\n    val sslSocketFactory: SSLSocketFactory? = address.sslSocketFactory()\n    val hostnameVerifier: HostnameVerifier? = address.hostnameVerifier()\n    val certificatePinner: CertificatePinner? = address.certificatePinner()\n  }\n\n  @Test @Disabled\n  fun cache() {\n    val cache = Cache(File(\"/cache/\"), Integer.MAX_VALUE.toLong())\n    val directory: File = cache.directory()\n  }\n\n  @Test @Disabled\n  fun cacheControl() {\n    val cacheControl: CacheControl = CacheControl.Builder().build()\n    val noCache: Boolean = cacheControl.noCache()\n    val noStore: Boolean = cacheControl.noStore()\n    val maxAgeSeconds: Int = cacheControl.maxAgeSeconds()\n    val sMaxAgeSeconds: Int = cacheControl.sMaxAgeSeconds()\n    val mustRevalidate: Boolean = cacheControl.mustRevalidate()\n    val maxStaleSeconds: Int = cacheControl.maxStaleSeconds()\n    val minFreshSeconds: Int = cacheControl.minFreshSeconds()\n    val onlyIfCached: Boolean = cacheControl.onlyIfCached()\n    val noTransform: Boolean = cacheControl.noTransform()\n    val immutable: Boolean = cacheControl.immutable()\n    val parse: CacheControl = CacheControl.parse(Headers.of())\n  }\n\n  @Test @Disabled\n  fun challenge() {\n    val challenge = Challenge(\"\", mapOf(\"\" to \"\"))\n    val scheme: String = challenge.scheme()\n    val authParams: Map<String?, String> = challenge.authParams()\n    val realm: String? = challenge.realm()\n    val charset: Charset = challenge.charset()\n  }\n\n  @Test @Disabled\n  fun cipherSuite() {\n    val cipherSuite: CipherSuite = CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n    val javaName: String = cipherSuite.javaName()\n  }\n\n  @Test @Disabled\n  fun connectionSpec() {\n    val connectionSpec: ConnectionSpec = ConnectionSpec.RESTRICTED_TLS\n    val tlsVersions: List<TlsVersion>? = connectionSpec.tlsVersions()\n    val cipherSuites: List<CipherSuite>? = connectionSpec.cipherSuites()\n    val supportsTlsExtensions: Boolean = connectionSpec.supportsTlsExtensions()\n  }\n\n  @Test @Disabled\n  fun cookie() {\n    val cookie: Cookie = Cookie.Builder().build()\n    val name: String = cookie.name()\n    val value: String = cookie.value()\n    val persistent: Boolean = cookie.persistent()\n    val expiresAt: Long = cookie.expiresAt()\n    val hostOnly: Boolean = cookie.hostOnly()\n    val domain: String = cookie.domain()\n    val path: String = cookie.path()\n    val httpOnly: Boolean = cookie.httpOnly()\n    val secure: Boolean = cookie.secure()\n  }\n\n  @Test @Disabled\n  fun formBody() {\n    val formBody: FormBody = FormBody.Builder().build()\n    val size: Int = formBody.size()\n  }\n\n  @Test @Disabled\n  fun handshake() {\n    val handshake: Handshake =\n      Handshake.get((localhost().sslSocketFactory().createSocket() as SSLSocket).session)\n    val tlsVersion: TlsVersion = handshake.tlsVersion()\n    val cipherSuite: CipherSuite = handshake.cipherSuite()\n    val peerCertificates: List<Certificate> = handshake.peerCertificates()\n    val peerPrincipal: Principal? = handshake.peerPrincipal()\n    val localCertificates: List<Certificate> = handshake.localCertificates()\n    val localPrincipal: Principal? = handshake.localPrincipal()\n  }\n\n  @Test @Disabled\n  fun headers() {\n    var headers: Headers = Headers.of(\"\", \"\")\n    headers = Headers.of(mapOf(\"\" to \"\"))\n    val size: Int = headers.size()\n  }\n\n  @Test @Disabled\n  fun httpLoggingInterceptor() {\n    val interceptor = HttpLoggingInterceptor()\n    val level = interceptor.getLevel()\n  }\n\n  @Test @Disabled\n  fun httpUrl() {\n    val httpUrl: HttpUrl = HttpUrl.get(\"\")\n    val url: URL = httpUrl.url()\n    val uri: URI = httpUrl.uri()\n    val scheme: String = httpUrl.scheme()\n    val encodedUsername: String = httpUrl.encodedUsername()\n    val username: String = httpUrl.username()\n    val encodedPassword: String = httpUrl.encodedPassword()\n    val password: String = httpUrl.password()\n    val host: String = httpUrl.host()\n    val port: Int = httpUrl.port()\n    val pathSize: Int = httpUrl.pathSize()\n    val encodedPath: String = httpUrl.encodedPath()\n    val encodedPathSegments: List<String> = httpUrl.encodedPathSegments()\n    val pathSegments: List<String> = httpUrl.pathSegments()\n    val encodedQuery: String? = httpUrl.encodedQuery()\n    val query: String? = httpUrl.query()\n    val querySize: Int = httpUrl.querySize()\n    val queryParameter: String? = httpUrl.queryParameter(\"\")\n    val queryParameterNames: Set<String> = httpUrl.queryParameterNames()\n    val encodedFragment: String? = httpUrl.encodedFragment()\n    val fragment: String? = httpUrl.fragment()\n    val getFromUrl: HttpUrl? = HttpUrl.get(URL(\"\"))\n    val getFromUri: HttpUrl? = HttpUrl.get(URI(\"\"))\n    val parse: HttpUrl? = HttpUrl.parse(\"\")\n  }\n\n  @Test @Disabled\n  fun handshakeCertificates() {\n    val handshakeCertificates = HandshakeCertificates.Builder().build()\n    val keyManager: X509KeyManager = handshakeCertificates.keyManager()\n    val trustManager: X509TrustManager = handshakeCertificates.trustManager()\n  }\n\n  @Test @Disabled\n  fun handshakeCertificatesBuilder() {\n    var builder: HandshakeCertificates.Builder = HandshakeCertificates.Builder()\n    val heldCertificate: HeldCertificate = HeldCertificate.Builder().build()\n    builder = builder.heldCertificate(heldCertificate, heldCertificate.certificate())\n    builder = builder.addTrustedCertificate(heldCertificate.certificate())\n  }\n\n  @Test @Disabled\n  fun heldCertificate() {\n    val heldCertificate: HeldCertificate = HeldCertificate.Builder().build()\n    val certificate: X509Certificate = heldCertificate.certificate()\n    val keyPair: KeyPair = heldCertificate.keyPair()\n  }\n\n  @Test @Disabled\n  fun mediaType() {\n    val mediaType: MediaType = MediaType.get(\"\")\n    val type: String = mediaType.type()\n    val subtype: String = mediaType.subtype()\n    val parse: MediaType? = MediaType.parse(\"\")\n  }\n\n  @Test @Disabled\n  fun mockResponse() {\n    val mockResponse = MockResponse()\n    var status: String = mockResponse.getStatus()\n    var headers: Headers = mockResponse.getHeaders()\n    var trailers: Headers = mockResponse.getTrailers()\n    var socketPolicy: SocketPolicy = mockResponse.getSocketPolicy()\n    var http2ErrorCode: Int = mockResponse.getHttp2ErrorCode()\n  }\n\n  @Test @Disabled\n  fun mockWebServer() {\n    val mockWebServer = MockWebServer()\n    var port: Int = mockWebServer.getPort()\n    mockWebServer.setServerSocketFactory(ServerSocketFactory.getDefault())\n    mockWebServer.setBodyLimit(0L)\n    mockWebServer.setProtocolNegotiationEnabled(false)\n    mockWebServer.setProtocols(listOf(Protocol.HTTP_1_1))\n    var requestCount: Int = mockWebServer.getRequestCount()\n  }\n\n  @Test @Disabled\n  fun multipartBody() {\n    val multipartBody: MultipartBody = MultipartBody.Builder().build()\n    val type: MediaType = multipartBody.type()\n    val boundary: String = multipartBody.boundary()\n    val size: Int = multipartBody.size()\n    val parts: List<MultipartBody.Part> = multipartBody.parts()\n  }\n\n  @Test @Disabled\n  fun multipartBodyPart() {\n    val multipartBody: MultipartBody = MultipartBody.Builder().build()\n    val part: MultipartBody.Part = multipartBody.part(0)\n    val headers: Headers? = part.headers()\n    val body: RequestBody = part.body()\n  }\n\n  @Test @Disabled\n  fun okHttpClient() {\n    val client = OkHttpClient()\n    val dispatcher: Dispatcher = client.dispatcher()\n    val proxy: Proxy? = client.proxy()\n    val protocols: List<Protocol> = client.protocols()\n    val connectionSpecs: List<ConnectionSpec> = client.connectionSpecs()\n    val interceptors: List<Interceptor> = client.interceptors()\n    val networkInterceptors: List<Interceptor> = client.networkInterceptors()\n    val eventListenerFactory: EventListener.Factory = client.eventListenerFactory()\n    val proxySelector: ProxySelector = client.proxySelector()\n    val cookieJar: CookieJar = client.cookieJar()\n    val cache: Cache? = client.cache()\n    val socketFactory: SocketFactory = client.socketFactory()\n    val sslSocketFactory: SSLSocketFactory = client.sslSocketFactory()\n    val hostnameVerifier: HostnameVerifier = client.hostnameVerifier()\n    val certificatePinner: CertificatePinner = client.certificatePinner()\n    val proxyAuthenticator: Authenticator = client.proxyAuthenticator()\n    val authenticator: Authenticator = client.authenticator()\n    val connectionPool: ConnectionPool = client.connectionPool()\n    val dns: Dns = client.dns()\n    val followSslRedirects: Boolean = client.followSslRedirects()\n    val followRedirects: Boolean = client.followRedirects()\n    val retryOnConnectionFailure: Boolean = client.retryOnConnectionFailure()\n    val callTimeoutMillis: Int = client.callTimeoutMillis()\n    val connectTimeoutMillis: Int = client.connectTimeoutMillis()\n    val readTimeoutMillis: Int = client.readTimeoutMillis()\n    val writeTimeoutMillis: Int = client.writeTimeoutMillis()\n    val pingIntervalMillis: Int = client.pingIntervalMillis()\n  }\n\n  @Test @Disabled\n  fun pushPromise() {\n    val pushPromise = PushPromise(\"\", \"\", Headers.of(), MockResponse())\n    val method: String = pushPromise.method()\n    val path: String = pushPromise.path()\n    val headers: Headers = pushPromise.headers()\n    val response: MockResponse = pushPromise.response()\n  }\n\n  @Test @Disabled\n  fun recordedRequest() {\n    val recordedRequest = RecordedRequest(\"\", Headers.of(), listOf(), 0L, Buffer(), 0, Socket())\n    var utf8Body: String = recordedRequest.utf8Body\n  }\n\n  @Test @Disabled\n  fun request() {\n    val request: Request = Request.Builder().build()\n    val url: HttpUrl = request.url()\n    val method: String = request.method()\n    val headers: Headers = request.headers()\n    val body: RequestBody? = request.body()\n    val cacheControl: CacheControl = request.cacheControl()\n  }\n\n  @Test @Disabled\n  fun response() {\n    val response: Response = Response.Builder().build()\n    val request: Request = response.request()\n    val protocol: Protocol = response.protocol()\n    val code: Int = response.code()\n    val message: String = response.message()\n    val handshake: Handshake? = response.handshake()\n    val headers: Headers = response.headers()\n    val body: ResponseBody = response.body()\n    val networkResponse: Response? = response.networkResponse()\n    val cacheResponse: Response? = response.cacheResponse()\n    val priorResponse: Response? = response.priorResponse()\n    val cacheControl: CacheControl = response.cacheControl()\n    val sentRequestAtMillis: Long = response.sentRequestAtMillis()\n    val receivedResponseAtMillis: Long = response.receivedResponseAtMillis()\n  }\n\n  @Test @Disabled\n  fun route() {\n    val route: Route = factory.newRoute()\n    val address: Address = route.address()\n    val proxy: Proxy = route.proxy()\n    val inetSocketAddress: InetSocketAddress = route.socketAddress()\n  }\n\n  @Test @Disabled\n  fun tlsVersion() {\n    val tlsVersion: TlsVersion = TlsVersion.TLS_1_3\n    val javaName: String = tlsVersion.javaName()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/KotlinSourceModernTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3\n\nimport java.io.File\nimport java.io.IOException\nimport java.math.BigInteger\nimport java.net.CookieHandler\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.Socket\nimport java.net.URI\nimport java.net.URL\nimport java.nio.charset.Charset\nimport java.security.KeyPair\nimport java.security.KeyPairGenerator\nimport java.security.Principal\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\nimport java.time.Duration\nimport java.time.Instant\nimport java.util.Date\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport java.util.concurrent.TimeUnit\nimport javax.net.ServerSocketFactory\nimport javax.net.SocketFactory\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.X509KeyManager\nimport javax.net.ssl.X509TrustManager\nimport kotlin.reflect.KClass\nimport okhttp3.Handshake.Companion.handshake\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.Headers.Companion.toHeaders\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.RequestBody.Companion.asRequestBody\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.ResponseBody.Companion.asResponseBody\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okhttp3.internal.authenticator.JavaNetAuthenticator\nimport okhttp3.internal.http2.Settings\nimport okhttp3.internal.proxy.NullProxySelector\nimport okhttp3.internal.tls.OkHostnameVerifier\nimport okhttp3.java.net.cookiejar.JavaNetCookieJar\nimport okhttp3.logging.HttpLoggingInterceptor\nimport okhttp3.logging.LoggingEventListener\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport okhttp3.mockwebserver.PushPromise\nimport okhttp3.mockwebserver.QueueDispatcher\nimport okhttp3.mockwebserver.RecordedRequest\nimport okhttp3.mockwebserver.SocketPolicy\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.Timeout\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assumptions.assumeFalse\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\n\n/**\n * Access every type, function, and property from Kotlin to defend against unexpected regressions in\n * modern 4.0.x kotlin source-compatibility.\n */\n@Suppress(\n  \"ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE\",\n  \"AssignedValueIsNeverRead\",\n  \"CanBeVal\",\n  \"DEPRECATION\",\n  \"IMPLICIT_NOTHING_AS_TYPE_PARAMETER\",\n  \"RedundantExplicitType\",\n  \"RedundantNullableReturnType\",\n  \"UNUSED_ANONYMOUS_PARAMETER\",\n  \"UNUSED_VALUE\",\n  \"UNUSED_VARIABLE\",\n  \"VARIABLE_WITH_REDUNDANT_INITIALIZER\",\n  \"VariableInitializerIsRedundant\",\n  \"VariableNeverRead\",\n  \"unused\",\n)\n@Disabled\nclass KotlinSourceModernTest {\n  private val factory = TestValueFactory()\n\n  @BeforeEach\n  fun disabled() {\n    assumeFalse(true)\n  }\n\n  @AfterEach\n  fun tearDown() {\n    factory.close()\n  }\n\n  @Test\n  fun address() {\n    val address: Address = factory.newAddress()\n    val url: HttpUrl = address.url\n    val dns: Dns = address.dns\n    val socketFactory: SocketFactory = address.socketFactory\n    val proxyAuthenticator: Authenticator = address.proxyAuthenticator\n    val protocols: List<Protocol> = address.protocols\n    val connectionSpecs: List<ConnectionSpec> = address.connectionSpecs\n    val proxySelector: ProxySelector = address.proxySelector\n    val sslSocketFactory: SSLSocketFactory? = address.sslSocketFactory\n    val hostnameVerifier: HostnameVerifier? = address.hostnameVerifier\n    val certificatePinner: CertificatePinner? = address.certificatePinner\n  }\n\n  @Test\n  fun authenticator() {\n    var authenticator: Authenticator = Authenticator { route, response -> TODO() }\n  }\n\n  @Test\n  fun cache() {\n    val cache = Cache(File(\"/cache/\"), Integer.MAX_VALUE.toLong())\n    cache.initialize()\n    cache.delete()\n    cache.evictAll()\n    val urls: MutableIterator<String> = cache.urls()\n    val writeAbortCount: Int = cache.writeAbortCount()\n    val writeSuccessCount: Int = cache.writeSuccessCount()\n    val size: Long = cache.size()\n    val maxSize: Long = cache.maxSize()\n    cache.flush()\n    cache.close()\n    val directory: File = cache.directory\n    val networkCount: Int = cache.networkCount()\n    val hitCount: Int = cache.hitCount()\n    val requestCount: Int = cache.requestCount()\n  }\n\n  @Test\n  fun cacheControl() {\n    val cacheControl: CacheControl = CacheControl.Builder().build()\n    val noCache: Boolean = cacheControl.noCache\n    val noStore: Boolean = cacheControl.noStore\n    val maxAgeSeconds: Int = cacheControl.maxAgeSeconds\n    val sMaxAgeSeconds: Int = cacheControl.sMaxAgeSeconds\n    val mustRevalidate: Boolean = cacheControl.mustRevalidate\n    val maxStaleSeconds: Int = cacheControl.maxStaleSeconds\n    val minFreshSeconds: Int = cacheControl.minFreshSeconds\n    val onlyIfCached: Boolean = cacheControl.onlyIfCached\n    val noTransform: Boolean = cacheControl.noTransform\n    val immutable: Boolean = cacheControl.immutable\n    val forceCache: CacheControl = CacheControl.FORCE_CACHE\n    val forceNetwork: CacheControl = CacheControl.FORCE_NETWORK\n    val parse: CacheControl = CacheControl.parse(headersOf())\n  }\n\n  @Test\n  fun cacheControlBuilder() {\n    var builder: CacheControl.Builder = CacheControl.Builder()\n    builder = builder.noCache()\n    builder = builder.noStore()\n    builder = builder.maxAge(0, TimeUnit.MILLISECONDS)\n    builder = builder.maxStale(0, TimeUnit.MILLISECONDS)\n    builder = builder.minFresh(0, TimeUnit.MILLISECONDS)\n    builder = builder.onlyIfCached()\n    builder = builder.noTransform()\n    builder = builder.immutable()\n    val cacheControl: CacheControl = builder.build()\n  }\n\n  @Test\n  fun call() {\n    val call: Call =\n      object : Call {\n        override fun request(): Request = TODO()\n\n        override fun execute(): Response = TODO()\n\n        override fun enqueue(responseCallback: Callback) = TODO()\n\n        override fun cancel() = TODO()\n\n        override fun isExecuted(): Boolean = TODO()\n\n        override fun isCanceled(): Boolean = TODO()\n\n        override fun timeout(): Timeout = TODO()\n\n        override fun addEventListener(eventListener: EventListener) = TODO()\n\n        override fun <T : Any> tag(type: KClass<T>): T? = TODO()\n\n        override fun <T> tag(type: Class<out T>): T? = TODO()\n\n        override fun <T : Any> tag(\n          type: KClass<T>,\n          computeIfAbsent: () -> T,\n        ): T = TODO()\n\n        override fun <T : Any> tag(\n          type: Class<T>,\n          computeIfAbsent: () -> T,\n        ): T = TODO()\n\n        override fun clone(): Call = TODO()\n      }\n  }\n\n  @Test\n  fun callback() {\n    val callback =\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) = TODO()\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) = TODO()\n      }\n  }\n\n  @Test\n  fun certificatePinner() {\n    val heldCertificate: HeldCertificate = HeldCertificate.Builder().build()\n    val certificate: X509Certificate = heldCertificate.certificate\n    val certificatePinner: CertificatePinner = CertificatePinner.Builder().build()\n    val certificates: List<Certificate> = listOf()\n    certificatePinner.check(\"\", listOf(certificate))\n    certificatePinner.check(\"\", arrayOf<Certificate>(certificate, certificate).toList())\n    val pin: String = CertificatePinner.pin(certificate)\n    val default: CertificatePinner = CertificatePinner.DEFAULT\n  }\n\n  @Test\n  fun certificatePinnerBuilder() {\n    val builder: CertificatePinner.Builder = CertificatePinner.Builder()\n    builder.add(\"\", \"pin1\", \"pin2\")\n  }\n\n  @Test\n  fun challenge() {\n    var challenge = Challenge(\"\", mapOf(\"\" to \"\"))\n    challenge = Challenge(\"\", \"\")\n    val scheme: String = challenge.scheme\n    val authParams: Map<String?, String> = challenge.authParams\n    val realm: String? = challenge.realm\n    val charset: Charset = challenge.charset\n    val utf8: Challenge = challenge.withCharset(Charsets.UTF_8)\n  }\n\n  @Test\n  fun cipherSuite() {\n    var cipherSuite: CipherSuite = CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n    cipherSuite = CipherSuite.forJavaName(\"\")\n    val javaName: String = cipherSuite.javaName\n  }\n\n  @Test\n  fun connection() {\n    val connection =\n      object : Connection {\n        override fun route(): Route = TODO()\n\n        override fun socket(): Socket = TODO()\n\n        override fun handshake(): Handshake? = TODO()\n\n        override fun protocol(): Protocol = TODO()\n      }\n  }\n\n  @Test\n  fun connectionPool() {\n    var connectionPool = ConnectionPool()\n    connectionPool = ConnectionPool(0, 0L, TimeUnit.SECONDS)\n    val idleConnectionCount: Int = connectionPool.idleConnectionCount()\n    val connectionCount: Int = connectionPool.connectionCount()\n    connectionPool.evictAll()\n  }\n\n  @Test\n  fun connectionSpec() {\n    var connectionSpec: ConnectionSpec = ConnectionSpec.RESTRICTED_TLS\n    connectionSpec = ConnectionSpec.MODERN_TLS\n    connectionSpec = ConnectionSpec.COMPATIBLE_TLS\n    connectionSpec = ConnectionSpec.CLEARTEXT\n    val tlsVersions: List<TlsVersion>? = connectionSpec.tlsVersions\n    val cipherSuites: List<CipherSuite>? = connectionSpec.cipherSuites\n    val supportsTlsExtensions: Boolean = connectionSpec.supportsTlsExtensions\n    val compatible: Boolean =\n      connectionSpec.isCompatible(\n        localhost().sslSocketFactory().createSocket() as SSLSocket,\n      )\n  }\n\n  @Test\n  fun connectionSpecBuilder() {\n    var builder = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)\n    builder = builder.allEnabledCipherSuites()\n    builder = builder.cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)\n    builder = builder.cipherSuites(\"\", \"\")\n    builder = builder.allEnabledTlsVersions()\n    builder = builder.tlsVersions(TlsVersion.TLS_1_3)\n    builder = builder.tlsVersions(\"\", \"\")\n    val connectionSpec: ConnectionSpec = builder.build()\n  }\n\n  @Test\n  fun cookie() {\n    val cookie: Cookie = Cookie.Builder().build()\n    val name: String = cookie.name\n    val value: String = cookie.value\n    val persistent: Boolean = cookie.persistent\n    val expiresAt: Long = cookie.expiresAt\n    val hostOnly: Boolean = cookie.hostOnly\n    val domain: String = cookie.domain\n    val path: String = cookie.path\n    val httpOnly: Boolean = cookie.httpOnly\n    val secure: Boolean = cookie.secure\n    val matches: Boolean = cookie.matches(\"\".toHttpUrl())\n    val parsedCookie: Cookie? = Cookie.parse(\"\".toHttpUrl(), \"\")\n    val cookies: List<Cookie> = Cookie.parseAll(\"\".toHttpUrl(), headersOf())\n  }\n\n  @Test\n  fun cookieBuilder() {\n    var builder: Cookie.Builder = Cookie.Builder()\n    builder = builder.name(\"\")\n    builder = builder.value(\"\")\n    builder = builder.expiresAt(0L)\n    builder = builder.domain(\"\")\n    builder = builder.hostOnlyDomain(\"\")\n    builder = builder.path(\"\")\n    builder = builder.secure()\n    builder = builder.httpOnly()\n    builder = builder.sameSite(\"None\")\n    val cookie: Cookie = builder.build()\n  }\n\n  @Test\n  fun cookieJar() {\n    val cookieJar =\n      object : CookieJar {\n        override fun saveFromResponse(\n          url: HttpUrl,\n          cookies: List<Cookie>,\n        ) = TODO()\n\n        override fun loadForRequest(url: HttpUrl): List<Cookie> = TODO()\n      }\n  }\n\n  @Test\n  fun credentials() {\n    val basic: String = Credentials.basic(\"\", \"\")\n  }\n\n  @Test\n  fun dispatcher() {\n    var dispatcher = Dispatcher()\n    dispatcher = Dispatcher(Executors.newCachedThreadPool())\n    val maxRequests: Int = dispatcher.maxRequests\n    dispatcher.maxRequests = 0\n    val maxRequestsPerHost: Int = dispatcher.maxRequestsPerHost\n    dispatcher.maxRequestsPerHost = 0\n    val executorService: ExecutorService = dispatcher.executorService\n    dispatcher.idleCallback = Runnable { TODO() }\n    val queuedCalls: List<Call> = dispatcher.queuedCalls()\n    val runningCalls: List<Call> = dispatcher.runningCalls()\n    val queuedCallsCount: Int = dispatcher.queuedCallsCount()\n    val runningCallsCount: Int = dispatcher.runningCallsCount()\n    dispatcher.cancelAll()\n  }\n\n  @Test\n  fun dispatcherFromMockWebServer() {\n    val dispatcher =\n      object : okhttp3.mockwebserver.Dispatcher() {\n        override fun dispatch(request: RecordedRequest): MockResponse = TODO()\n\n        override fun peek(): MockResponse = TODO()\n\n        override fun shutdown() = TODO()\n      }\n  }\n\n  @Test\n  fun dns() {\n    var dns: Dns = Dns { TODO() }\n\n    val system: Dns = Dns.SYSTEM\n  }\n\n  @Test\n  fun eventListener() {\n    val eventListener =\n      object : EventListener() {\n        override fun callStart(call: Call) = TODO()\n\n        override fun dnsStart(\n          call: Call,\n          domainName: String,\n        ) = TODO()\n\n        override fun dnsEnd(\n          call: Call,\n          domainName: String,\n          inetAddressList: List<InetAddress>,\n        ) = TODO()\n\n        override fun connectStart(\n          call: Call,\n          inetSocketAddress: InetSocketAddress,\n          proxy: Proxy,\n        ) = TODO()\n\n        override fun secureConnectStart(call: Call) = TODO()\n\n        override fun secureConnectEnd(\n          call: Call,\n          handshake: Handshake?,\n        ) = TODO()\n\n        override fun connectEnd(\n          call: Call,\n          inetSocketAddress: InetSocketAddress,\n          proxy: Proxy,\n          protocol: Protocol?,\n        ) = TODO()\n\n        override fun connectFailed(\n          call: Call,\n          inetSocketAddress: InetSocketAddress,\n          proxy: Proxy,\n          protocol: Protocol?,\n          ioe: IOException,\n        ) = TODO()\n\n        override fun connectionAcquired(\n          call: Call,\n          connection: Connection,\n        ) = TODO()\n\n        override fun connectionReleased(\n          call: Call,\n          connection: Connection,\n        ) = TODO()\n\n        override fun requestHeadersStart(call: Call) = TODO()\n\n        override fun requestHeadersEnd(\n          call: Call,\n          request: Request,\n        ) = TODO()\n\n        override fun requestBodyStart(call: Call) = TODO()\n\n        override fun requestBodyEnd(\n          call: Call,\n          byteCount: Long,\n        ) = TODO()\n\n        override fun requestFailed(\n          call: Call,\n          ioe: IOException,\n        ) = TODO()\n\n        override fun responseHeadersStart(call: Call) = TODO()\n\n        override fun responseHeadersEnd(\n          call: Call,\n          response: Response,\n        ) = TODO()\n\n        override fun responseBodyStart(call: Call) = TODO()\n\n        override fun responseBodyEnd(\n          call: Call,\n          byteCount: Long,\n        ) = TODO()\n\n        override fun responseFailed(\n          call: Call,\n          ioe: IOException,\n        ) = TODO()\n\n        override fun callEnd(call: Call) = TODO()\n\n        override fun callFailed(\n          call: Call,\n          ioe: IOException,\n        ) = TODO()\n\n        override fun canceled(call: Call) = TODO()\n      }\n    val none: EventListener = EventListener.NONE\n  }\n\n  @Test\n  fun eventListenerBuilder() {\n    var builder: EventListener.Factory = EventListener.Factory { TODO() }\n  }\n\n  @Test\n  fun formBody() {\n    val formBody: FormBody = FormBody.Builder().build()\n    val size: Int = formBody.size\n    val encodedName: String = formBody.encodedName(0)\n    val name: String = formBody.name(0)\n    val encodedValue: String = formBody.encodedValue(0)\n    val value: String = formBody.value(0)\n    val contentType: MediaType = formBody.contentType()\n    val contentLength: Long = formBody.contentLength()\n    formBody.writeTo(Buffer())\n    val requestBody: RequestBody = formBody\n  }\n\n  @Test\n  fun formBodyBuilder() {\n    var builder: FormBody.Builder = FormBody.Builder()\n    builder = FormBody.Builder(Charsets.UTF_8)\n    builder = builder.add(\"\", \"\")\n    builder = builder.addEncoded(\"\", \"\")\n    val formBody: FormBody = builder.build()\n  }\n\n  @Test\n  fun handshake() {\n    var handshake: Handshake =\n      (localhost().sslSocketFactory().createSocket() as SSLSocket).session.handshake()\n    val listOfCertificates: List<Certificate> = listOf()\n    handshake =\n      Handshake.get(\n        TlsVersion.TLS_1_3,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n        listOfCertificates,\n        listOfCertificates,\n      )\n    val tlsVersion: TlsVersion = handshake.tlsVersion\n    val cipherSuite: CipherSuite = handshake.cipherSuite\n    val peerCertificates: List<Certificate> = handshake.peerCertificates\n    val peerPrincipal: Principal? = handshake.peerPrincipal\n    val localCertificates: List<Certificate> = handshake.localCertificates\n    val localPrincipal: Principal? = handshake.localPrincipal\n  }\n\n  @Test\n  fun headers() {\n    var headers: Headers = headersOf(\"\", \"\")\n    headers = mapOf(\"\" to \"\").toHeaders()\n    val get: String? = headers[\"\"]\n    val date: Date? = headers.getDate(\"\")\n    val instant: Instant? = headers.getInstant(\"\")\n    val size: Int = headers.size\n    val name: String = headers.name(0)\n    val value: String = headers.value(0)\n    val names: Set<String> = headers.names()\n    val values: List<String> = headers.values(\"\")\n    val byteCount: Long = headers.byteCount()\n    val builder: Headers.Builder = headers.newBuilder()\n    val multimap: Map<String, List<String>> = headers.toMultimap()\n  }\n\n  @Test\n  fun headersBuilder() {\n    var builder: Headers.Builder = Headers.Builder()\n    builder = builder.add(\"\")\n    builder = builder.add(\"\", \"\")\n    builder = builder.addUnsafeNonAscii(\"\", \"\")\n    builder = builder.addAll(headersOf())\n    builder = builder.add(\"\", Date(0L))\n    builder = builder.add(\"\", Instant.EPOCH)\n    builder = builder.set(\"\", \"\")\n    builder = builder.set(\"\", Date(0L))\n    builder = builder.set(\"\", Instant.EPOCH)\n    builder = builder.removeAll(\"\")\n    val get: String? = builder[\"\"]\n    val headers: Headers = builder.build()\n  }\n\n  @Test\n  fun httpLoggingInterceptor() {\n    var interceptor: HttpLoggingInterceptor = HttpLoggingInterceptor()\n    interceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT)\n    interceptor.redactHeader(\"\")\n    interceptor.level = HttpLoggingInterceptor.Level.BASIC\n    var level: HttpLoggingInterceptor.Level = interceptor.level\n    interceptor.intercept(newInterceptorChain())\n  }\n\n  @Test\n  fun httpLoggingInterceptorLevel() {\n    val none: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.NONE\n    val basic: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.BASIC\n    val headers: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.HEADERS\n    val body: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.BODY\n  }\n\n  @Test\n  fun httpLoggingInterceptorLogger() {\n    var logger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger { TODO() }\n    val default: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger.DEFAULT\n  }\n\n  @Test\n  fun httpUrl() {\n    val httpUrl: HttpUrl = \"\".toHttpUrl()\n    val isHttps: Boolean = httpUrl.isHttps\n    val url: URL = httpUrl.toUrl()\n    val uri: URI = httpUrl.toUri()\n    val scheme: String = httpUrl.scheme\n    val encodedUsername: String = httpUrl.encodedUsername\n    val username: String = httpUrl.username\n    val encodedPassword: String = httpUrl.encodedPassword\n    val password: String = httpUrl.password\n    val host: String = httpUrl.host\n    val port: Int = httpUrl.port\n    val pathSize: Int = httpUrl.pathSize\n    val encodedPath: String = httpUrl.encodedPath\n    val encodedPathSegments: List<String> = httpUrl.encodedPathSegments\n    val pathSegments: List<String> = httpUrl.pathSegments\n    val encodedQuery: String? = httpUrl.encodedQuery\n    val query: String? = httpUrl.query\n    val querySize: Int = httpUrl.querySize\n    val queryParameter: String? = httpUrl.queryParameter(\"\")\n    val queryParameterNames: Set<String> = httpUrl.queryParameterNames\n    val queryParameterValues: List<String?> = httpUrl.queryParameterValues(\"\")\n    val queryParameterName: String = httpUrl.queryParameterName(0)\n    val queryParameterValue: String? = httpUrl.queryParameterValue(0)\n    val encodedFragment: String? = httpUrl.encodedFragment\n    val fragment: String? = httpUrl.fragment\n    val redact: String = httpUrl.redact()\n    var builder: HttpUrl.Builder = httpUrl.newBuilder()\n    var resolveBuilder: HttpUrl.Builder? = httpUrl.newBuilder(\"\")\n    val topPrivateDomain: String? = httpUrl.topPrivateDomain()\n    val resolve: HttpUrl? = httpUrl.resolve(\"\")\n    val getFromUrl: HttpUrl? = URL(\"\").toHttpUrlOrNull()\n    val getFromUri: HttpUrl? = URI(\"\").toHttpUrlOrNull()\n    val parse: HttpUrl? = \"\".toHttpUrlOrNull()\n    val defaultPort: Int = HttpUrl.defaultPort(\"\")\n  }\n\n  @Test\n  fun httpUrlBuilder() {\n    var builder: HttpUrl.Builder = HttpUrl.Builder()\n    builder = builder.scheme(\"\")\n    builder = builder.username(\"\")\n    builder = builder.encodedUsername(\"\")\n    builder = builder.password(\"\")\n    builder = builder.encodedPassword(\"\")\n    builder = builder.host(\"\")\n    builder = builder.port(0)\n    builder = builder.addPathSegment(\"\")\n    builder = builder.addPathSegments(\"\")\n    builder = builder.addEncodedPathSegment(\"\")\n    builder = builder.addEncodedPathSegments(\"\")\n    builder = builder.setPathSegment(0, \"\")\n    builder = builder.setEncodedPathSegment(0, \"\")\n    builder = builder.removePathSegment(0)\n    builder = builder.encodedPath(\"\")\n    builder = builder.query(\"\")\n    builder = builder.encodedQuery(\"\")\n    builder = builder.addQueryParameter(\"\", \"\")\n    builder = builder.addEncodedQueryParameter(\"\", \"\")\n    builder = builder.setQueryParameter(\"\", \"\")\n    builder = builder.setEncodedQueryParameter(\"\", \"\")\n    builder = builder.removeAllQueryParameters(\"\")\n    builder = builder.removeAllEncodedQueryParameters(\"\")\n    builder = builder.fragment(\"\")\n    builder = builder.encodedFragment(\"\")\n    val httpUrl: HttpUrl = builder.build()\n  }\n\n  @Test\n  fun interceptor() {\n    var interceptor: Interceptor = Interceptor { TODO() }\n    interceptor = Interceptor { it: Interceptor.Chain -> TODO() }\n  }\n\n  @Test\n  fun interceptorChain() {\n    val chain: Interceptor.Chain = newInterceptorChain()\n  }\n\n  @Test\n  fun handshakeCertificates() {\n    val handshakeCertificates = HandshakeCertificates.Builder().build()\n    val keyManager: X509KeyManager = handshakeCertificates.keyManager\n    val trustManager: X509TrustManager = handshakeCertificates.trustManager\n    val sslSocketFactory: SSLSocketFactory = handshakeCertificates.sslSocketFactory()\n    val sslContext: SSLContext = handshakeCertificates.sslContext()\n  }\n\n  @Test\n  fun handshakeCertificatesBuilder() {\n    var builder: HandshakeCertificates.Builder = HandshakeCertificates.Builder()\n    val heldCertificate = HeldCertificate.Builder().build()\n    builder = builder.heldCertificate(heldCertificate, heldCertificate.certificate)\n    builder = builder.addTrustedCertificate(heldCertificate.certificate)\n    builder = builder.addPlatformTrustedCertificates()\n    val handshakeCertificates: HandshakeCertificates = builder.build()\n  }\n\n  @Test\n  fun heldCertificate() {\n    val heldCertificate: HeldCertificate = HeldCertificate.Builder().build()\n    val certificate: X509Certificate = heldCertificate.certificate\n    val keyPair: KeyPair = heldCertificate.keyPair\n    val certificatePem: String = heldCertificate.certificatePem()\n    val privateKeyPkcs8Pem: String = heldCertificate.privateKeyPkcs8Pem()\n    val privateKeyPkcs1Pem: String = heldCertificate.privateKeyPkcs1Pem()\n  }\n\n  @Test\n  fun heldCertificateBuilder() {\n    val keyPair: KeyPair = KeyPairGenerator.getInstance(\"\").genKeyPair()\n    var builder: HeldCertificate.Builder = HeldCertificate.Builder()\n    builder = builder.validityInterval(0L, 0L)\n    builder = builder.duration(0L, TimeUnit.SECONDS)\n    builder = builder.addSubjectAlternativeName(\"\")\n    builder = builder.commonName(\"\")\n    builder = builder.organizationalUnit(\"\")\n    builder = builder.serialNumber(BigInteger.ZERO)\n    builder = builder.serialNumber(0L)\n    builder = builder.keyPair(keyPair)\n    builder = builder.keyPair(keyPair.public, keyPair.private)\n    builder = builder.signedBy(HeldCertificate.Builder().build())\n    builder = builder.certificateAuthority(0)\n    builder = builder.ecdsa256()\n    builder = builder.rsa2048()\n    val heldCertificate: HeldCertificate = builder.build()\n  }\n\n  @Test\n  fun javaNetAuthenticator() {\n    val authenticator = JavaNetAuthenticator()\n    val response = Response.Builder().build()\n    var request: Request? = authenticator.authenticate(factory.newRoute(), response)\n    request = authenticator.authenticate(null, response)\n  }\n\n  @Test\n  fun javaNetCookieJar() {\n    val cookieJar: JavaNetCookieJar = JavaNetCookieJar(newCookieHandler())\n    val httpUrl = \"\".toHttpUrl()\n    val loadForRequest: List<Cookie> = cookieJar.loadForRequest(httpUrl)\n    cookieJar.saveFromResponse(httpUrl, listOf(Cookie.Builder().build()))\n  }\n\n  @Test\n  fun loggingEventListener() {\n    var loggingEventListener: EventListener = LoggingEventListener.Factory().create(FailingCall())\n  }\n\n  @Test\n  fun loggingEventListenerFactory() {\n    var factory: LoggingEventListener.Factory = LoggingEventListener.Factory()\n    factory = LoggingEventListener.Factory(HttpLoggingInterceptor.Logger.DEFAULT)\n    factory =\n      object : LoggingEventListener.Factory() {\n        override fun create(call: Call): EventListener = TODO()\n      }\n    val eventListener: EventListener = factory.create(FailingCall())\n  }\n\n  @Test\n  fun mediaType() {\n    val mediaType: MediaType = \"\".toMediaType()\n    val defaultCharset: Charset? = mediaType.charset()\n    val charset: Charset? = mediaType.charset(Charsets.UTF_8)\n    val type: String = mediaType.type\n    val subtype: String = mediaType.subtype\n    val parse: MediaType? = \"\".toMediaTypeOrNull()\n  }\n\n  @Test\n  fun mockResponse() {\n    var mockResponse: MockResponse = MockResponse()\n    var status: String = mockResponse.status\n    status = mockResponse.status\n    mockResponse.status = \"\"\n    mockResponse = mockResponse.setResponseCode(0)\n    var headers: Headers = mockResponse.headers\n    var trailers: Headers = mockResponse.trailers\n    mockResponse = mockResponse.clearHeaders()\n    mockResponse = mockResponse.addHeader(\"\")\n    mockResponse = mockResponse.addHeader(\"\", \"\")\n    mockResponse = mockResponse.addHeaderLenient(\"\", Any())\n    mockResponse = mockResponse.setHeader(\"\", Any())\n    mockResponse.headers = headersOf()\n    mockResponse.trailers = headersOf()\n    mockResponse = mockResponse.removeHeader(\"\")\n    var body: Buffer? = mockResponse.getBody()\n    mockResponse = mockResponse.setBody(Buffer())\n    mockResponse = mockResponse.setChunkedBody(Buffer(), 0)\n    mockResponse = mockResponse.setChunkedBody(\"\", 0)\n    var socketPolicy: SocketPolicy = mockResponse.socketPolicy\n    mockResponse.socketPolicy = SocketPolicy.KEEP_OPEN\n    var http2ErrorCode: Int = mockResponse.http2ErrorCode\n    mockResponse.http2ErrorCode = 0\n    mockResponse = mockResponse.throttleBody(0L, 0L, TimeUnit.SECONDS)\n    var throttleBytesPerPeriod: Long = mockResponse.throttleBytesPerPeriod\n    throttleBytesPerPeriod = mockResponse.throttleBytesPerPeriod\n    var throttlePeriod: Long = mockResponse.getThrottlePeriod(TimeUnit.SECONDS)\n    mockResponse = mockResponse.setBodyDelay(0L, TimeUnit.SECONDS)\n    val bodyDelay: Long = mockResponse.getBodyDelay(TimeUnit.SECONDS)\n    mockResponse = mockResponse.setHeadersDelay(0L, TimeUnit.SECONDS)\n    val headersDelay: Long = mockResponse.getHeadersDelay(TimeUnit.SECONDS)\n    mockResponse = mockResponse.withPush(PushPromise(\"\", \"\", headersOf(), MockResponse()))\n    var pushPromises: List<PushPromise> = mockResponse.pushPromises\n    pushPromises = mockResponse.pushPromises\n    mockResponse = mockResponse.withSettings(Settings())\n    var settings: Settings = mockResponse.settings\n    settings = mockResponse.settings\n    mockResponse =\n      mockResponse.withWebSocketUpgrade(\n        object : WebSocketListener() {\n        },\n      )\n    var webSocketListener: WebSocketListener? = mockResponse.webSocketListener\n    webSocketListener = mockResponse.webSocketListener\n  }\n\n  @Test\n  fun mockWebServer() {\n    val mockWebServer: MockWebServer = MockWebServer()\n    var port: Int = mockWebServer.port\n    var hostName: String = mockWebServer.hostName\n    hostName = mockWebServer.hostName\n    val toProxyAddress: Proxy = mockWebServer.toProxyAddress()\n    mockWebServer.serverSocketFactory = ServerSocketFactory.getDefault()\n    val url: HttpUrl = mockWebServer.url(\"\")\n    mockWebServer.bodyLimit = 0L\n    mockWebServer.protocolNegotiationEnabled = false\n    mockWebServer.protocols = listOf()\n    val protocols: List<Protocol> = mockWebServer.protocols\n    mockWebServer.useHttps(SSLSocketFactory.getDefault() as SSLSocketFactory, false)\n    mockWebServer.noClientAuth()\n    mockWebServer.requestClientAuth()\n    mockWebServer.requireClientAuth()\n    val request: RecordedRequest = mockWebServer.takeRequest()\n    val nullableRequest: RecordedRequest? = mockWebServer.takeRequest(0L, TimeUnit.SECONDS)\n    var requestCount: Int = mockWebServer.requestCount\n    mockWebServer.enqueue(MockResponse())\n    mockWebServer.start()\n    mockWebServer.start(0)\n    mockWebServer.start(InetAddress.getLocalHost(), 0)\n    mockWebServer.shutdown()\n    var dispatcher: okhttp3.mockwebserver.Dispatcher = mockWebServer.dispatcher\n    dispatcher = mockWebServer.dispatcher\n    mockWebServer.dispatcher = QueueDispatcher()\n    mockWebServer.dispatcher = QueueDispatcher()\n    mockWebServer.close()\n  }\n\n  @Test\n  fun multipartBody() {\n    val multipartBody: MultipartBody = MultipartBody.Builder().build()\n    val type: MediaType = multipartBody.type\n    val boundary: String = multipartBody.boundary\n    val size: Int = multipartBody.size\n    val parts: List<MultipartBody.Part> = multipartBody.parts\n    val part: MultipartBody.Part = multipartBody.part(0)\n    val contentType: MediaType = multipartBody.contentType()\n    val contentLength: Long = multipartBody.contentLength()\n    multipartBody.writeTo(Buffer())\n    val mixed: MediaType = MultipartBody.MIXED\n    val alternative: MediaType = MultipartBody.ALTERNATIVE\n    val digest: MediaType = MultipartBody.DIGEST\n    val parallel: MediaType = MultipartBody.PARALLEL\n    val form: MediaType = MultipartBody.FORM\n  }\n\n  @Test\n  fun multipartBodyPart() {\n    val requestBody: RequestBody = \"\".toRequestBody(null)\n    var part: MultipartBody.Part = MultipartBody.Part.create(null, requestBody)\n    part = MultipartBody.Part.create(headersOf(), requestBody)\n    part = MultipartBody.Part.create(requestBody)\n    part = MultipartBody.Part.createFormData(\"\", \"\")\n    part = MultipartBody.Part.createFormData(\"\", \"\", requestBody)\n    part = MultipartBody.Part.createFormData(\"\", null, requestBody)\n    val headers: Headers? = part.headers\n    val body: RequestBody = part.body\n  }\n\n  @Test\n  fun multipartBodyBuilder() {\n    val requestBody = \"\".toRequestBody(null)\n    var builder: MultipartBody.Builder = MultipartBody.Builder()\n    builder = MultipartBody.Builder(\"\")\n    builder = builder.setType(\"\".toMediaType())\n    builder = builder.addPart(requestBody)\n    builder = builder.addPart(headersOf(), requestBody)\n    builder = builder.addPart(null, requestBody)\n    builder = builder.addFormDataPart(\"\", \"\")\n    builder = builder.addFormDataPart(\"\", \"\", requestBody)\n    builder = builder.addFormDataPart(\"\", null, requestBody)\n    builder = builder.addPart(MultipartBody.Part.create(requestBody))\n    val multipartBody: MultipartBody = builder.build()\n  }\n\n  @Test\n  fun okHttpClient() {\n    val client: OkHttpClient = OkHttpClient()\n    val dispatcher: Dispatcher = client.dispatcher\n    val proxy: Proxy? = client.proxy\n    val protocols: List<Protocol> = client.protocols\n    val connectionSpecs: List<ConnectionSpec> = client.connectionSpecs\n    val interceptors: List<Interceptor> = client.interceptors\n    val networkInterceptors: List<Interceptor> = client.networkInterceptors\n    val eventListenerFactory: EventListener.Factory = client.eventListenerFactory\n    val proxySelector: ProxySelector = client.proxySelector\n    val cookieJar: CookieJar = client.cookieJar\n    val cache: Cache? = client.cache\n    val socketFactory: SocketFactory = client.socketFactory\n    val sslSocketFactory: SSLSocketFactory = client.sslSocketFactory\n    val hostnameVerifier: HostnameVerifier = client.hostnameVerifier\n    val certificatePinner: CertificatePinner = client.certificatePinner\n    val proxyAuthenticator: Authenticator = client.proxyAuthenticator\n    val authenticator: Authenticator = client.authenticator\n    val connectionPool: ConnectionPool = client.connectionPool\n    val dns: Dns = client.dns\n    val followSslRedirects: Boolean = client.followSslRedirects\n    val followRedirects: Boolean = client.followRedirects\n    val retryOnConnectionFailure: Boolean = client.retryOnConnectionFailure\n    val callTimeoutMillis: Int = client.callTimeoutMillis\n    val connectTimeoutMillis: Int = client.connectTimeoutMillis\n    val readTimeoutMillis: Int = client.readTimeoutMillis\n    val writeTimeoutMillis: Int = client.writeTimeoutMillis\n    val pingIntervalMillis: Int = client.pingIntervalMillis\n    val call: Call = client.newCall(Request.Builder().build())\n    val webSocket: WebSocket =\n      client.newWebSocket(\n        Request.Builder().build(),\n        object : WebSocketListener() {\n        },\n      )\n    val newBuilder: OkHttpClient.Builder = client.newBuilder()\n  }\n\n  @Test\n  fun okHttpClientBuilder() {\n    var builder: OkHttpClient.Builder = OkHttpClient.Builder()\n    builder = builder.callTimeout(0L, TimeUnit.SECONDS)\n    builder = builder.callTimeout(Duration.ofSeconds(0L))\n    builder = builder.connectTimeout(0L, TimeUnit.SECONDS)\n    builder = builder.connectTimeout(Duration.ofSeconds(0L))\n    builder = builder.readTimeout(0L, TimeUnit.SECONDS)\n    builder = builder.readTimeout(Duration.ofSeconds(0L))\n    builder = builder.writeTimeout(0L, TimeUnit.SECONDS)\n    builder = builder.writeTimeout(Duration.ofSeconds(0L))\n    builder = builder.pingInterval(0L, TimeUnit.SECONDS)\n    builder = builder.pingInterval(Duration.ofSeconds(0L))\n    builder = builder.proxy(Proxy.NO_PROXY)\n    builder = builder.proxySelector(NullProxySelector)\n    builder = builder.cookieJar(CookieJar.NO_COOKIES)\n    builder = builder.cache(Cache(File(\"/cache/\"), Integer.MAX_VALUE.toLong()))\n    builder = builder.dns(Dns.SYSTEM)\n    builder = builder.socketFactory(SocketFactory.getDefault())\n    builder = builder.sslSocketFactory(localhost().sslSocketFactory(), localhost().trustManager)\n    builder = builder.hostnameVerifier(OkHostnameVerifier)\n    builder = builder.certificatePinner(CertificatePinner.DEFAULT)\n    builder = builder.authenticator(Authenticator.NONE)\n    builder = builder.proxyAuthenticator(Authenticator.NONE)\n    builder = builder.connectionPool(ConnectionPool(0, 0, TimeUnit.SECONDS))\n    builder = builder.followSslRedirects(false)\n    builder = builder.followRedirects(false)\n    builder = builder.retryOnConnectionFailure(false)\n    builder = builder.dispatcher(Dispatcher())\n    builder = builder.protocols(listOf(Protocol.HTTP_1_1))\n    builder = builder.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS))\n    val interceptors: List<Interceptor> = builder.interceptors()\n    builder = builder.addInterceptor(Interceptor { TODO() })\n    builder = builder.addInterceptor { it: Interceptor.Chain -> TODO() }\n    val networkInterceptors: List<Interceptor> = builder.networkInterceptors()\n    builder = builder.addNetworkInterceptor(Interceptor { TODO() })\n    builder = builder.addNetworkInterceptor { it: Interceptor.Chain -> TODO() }\n    builder = builder.eventListener(EventListener.NONE)\n    builder = builder.eventListenerFactory { TODO() }\n    val client: OkHttpClient = builder.build()\n  }\n\n  @Test\n  fun testAddInterceptor() {\n    val builder = OkHttpClient.Builder()\n\n    val i = HttpLoggingInterceptor()\n\n    builder.interceptors().add(i)\n    builder.networkInterceptors().add(i)\n  }\n\n  @Test\n  fun protocol() {\n    var protocol: Protocol = Protocol.HTTP_2\n    protocol = Protocol.get(\"\")\n  }\n\n  @Test\n  fun pushPromise() {\n    val pushPromise: PushPromise = PushPromise(\"\", \"\", headersOf(), MockResponse())\n    val method: String = pushPromise.method\n    val path: String = pushPromise.path\n    val headers: Headers = pushPromise.headers\n    val response: MockResponse = pushPromise.response\n  }\n\n  @Test\n  fun queueDispatcher() {\n    val queueDispatcher = QueueDispatcher()\n    var mockResponse: MockResponse =\n      queueDispatcher.dispatch(\n        RecordedRequest(\"\", headersOf(), listOf(), 0L, Buffer(), 0, Socket()),\n      )\n    mockResponse = queueDispatcher.peek()\n    queueDispatcher.enqueueResponse(MockResponse())\n    queueDispatcher.shutdown()\n    queueDispatcher.setFailFast(false)\n    queueDispatcher.setFailFast(MockResponse())\n  }\n\n  @Test\n  fun recordedRequest() {\n    var recordedRequest: RecordedRequest =\n      RecordedRequest(\n        \"\",\n        headersOf(),\n        listOf(),\n        0L,\n        Buffer(),\n        0,\n        Socket(),\n      )\n    recordedRequest = RecordedRequest(\"\", headersOf(), listOf(), 0L, Buffer(), 0, Socket())\n    var requestUrl: HttpUrl? = recordedRequest.requestUrl\n    var requestLine: String = recordedRequest.requestLine\n    var method: String? = recordedRequest.method\n    var path: String? = recordedRequest.path\n    var headers: Headers = recordedRequest.headers\n    val header: String? = recordedRequest.getHeader(\"\")\n    var chunkSizes: List<Int>? = recordedRequest.chunkSizes\n    var bodySize: Long = recordedRequest.bodySize\n    var body: Buffer = recordedRequest.body\n    var utf8Body: String = recordedRequest.body.readUtf8()\n    var sequenceNumber: Int = recordedRequest.sequenceNumber\n    var tlsVersion: TlsVersion? = recordedRequest.tlsVersion\n    var handshake: Handshake? = recordedRequest.handshake\n  }\n\n  @Test\n  fun request() {\n    val request: Request = Request.Builder().build()\n    val isHttps: Boolean = request.isHttps\n    val url: HttpUrl = request.url\n    val method: String = request.method\n    val headers: Headers = request.headers\n    val header: String? = request.header(\"\")\n    val headersForName: List<String> = request.headers(\"\")\n    val body: RequestBody? = request.body\n    var stringTag: String? = request.tag(String::class)\n    stringTag = request.tag<String>()\n    var tag: Any? = request.tag()\n    tag = request.tag(Any::class.java)\n    val builder: Request.Builder = request.newBuilder()\n    val cacheControl: CacheControl = request.cacheControl\n  }\n\n  @Test\n  fun requestConstructor() {\n    Request(url = \"\".toHttpUrl())\n    Request(\n      url = \"\".toHttpUrl(),\n      headers = headersOf(),\n      method = \"\",\n      body = \"\".toRequestBody(null),\n    )\n  }\n\n  @Test\n  fun requestBuilder() {\n    val requestBody = \"\".toRequestBody(null)\n    var builder = Request.Builder()\n    builder = builder.url(\"\".toHttpUrl())\n    builder = builder.url(\"\")\n    builder = builder.url(URL(\"\"))\n    builder = builder.header(\"\", \"\")\n    builder = builder.addHeader(\"\", \"\")\n    builder = builder.removeHeader(\"\")\n    builder = builder.headers(headersOf())\n    builder = builder.cacheControl(CacheControl.FORCE_CACHE)\n    builder = builder.get()\n    builder = builder.head()\n    builder = builder.post(requestBody)\n    builder = builder.delete(requestBody)\n    builder = builder.delete(null)\n    builder = builder.put(requestBody)\n    builder = builder.patch(requestBody)\n    builder = builder.method(\"\", requestBody)\n    builder = builder.method(\"\", null)\n    builder = builder.tag(\"\")\n    builder = builder.tag(null)\n    builder = builder.tag(String::class.java, \"\")\n    builder = builder.tag(String::class.java, null)\n    val request: Request = builder.build()\n  }\n\n  @Test\n  fun requestBody() {\n    var requestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType? = TODO()\n\n        override fun contentLength(): Long = TODO()\n\n        override fun isDuplex(): Boolean = TODO()\n\n        override fun isOneShot(): Boolean = TODO()\n\n        override fun writeTo(sink: BufferedSink) = TODO()\n      }\n    requestBody = \"\".toRequestBody(null)\n    requestBody = \"\".toRequestBody(\"\".toMediaTypeOrNull())\n    requestBody = ByteString.EMPTY.toRequestBody(null)\n    requestBody = ByteString.EMPTY.toRequestBody(\"\".toMediaTypeOrNull())\n    requestBody = byteArrayOf(0, 1).toRequestBody(null, 0, 2)\n    requestBody = byteArrayOf(0, 1).toRequestBody(\"\".toMediaTypeOrNull(), 0, 2)\n    requestBody = byteArrayOf(0, 1).toRequestBody(null, 0, 2)\n    requestBody = byteArrayOf(0, 1).toRequestBody(\"\".toMediaTypeOrNull(), 0, 2)\n    requestBody = File(\"\").asRequestBody(null)\n    requestBody = File(\"\").asRequestBody(\"\".toMediaTypeOrNull())\n  }\n\n  @Test\n  fun response() {\n    val response: Response = Response.Builder().build()\n    val request: Request = response.request\n    val protocol: Protocol = response.protocol\n    val code: Int = response.code\n    val successful: Boolean = response.isSuccessful\n    val message: String = response.message\n    val handshake: Handshake? = response.handshake\n    val headersForName: List<String> = response.headers(\"\")\n    val header: String? = response.header(\"\")\n    val headers: Headers = response.headers\n    val trailers: Headers = response.trailers()\n    val peekBody: ResponseBody = response.peekBody(0L)\n    val body: ResponseBody = response.body\n    val builder: Response.Builder = response.newBuilder()\n    val redirect: Boolean = response.isRedirect\n    val networkResponse: Response? = response.networkResponse\n    val cacheResponse: Response? = response.cacheResponse\n    val priorResponse: Response? = response.priorResponse\n    val challenges: List<Challenge> = response.challenges()\n    val cacheControl: CacheControl = response.cacheControl\n    val sentRequestAtMillis: Long = response.sentRequestAtMillis\n    val receivedResponseAtMillis: Long = response.receivedResponseAtMillis\n  }\n\n  @Test\n  fun responseBuilder() {\n    var builder: Response.Builder = Response.Builder()\n    builder = builder.request(Request.Builder().build())\n    builder = builder.protocol(Protocol.HTTP_2)\n    builder = builder.code(0)\n    builder = builder.message(\"\")\n    builder =\n      builder.handshake(\n        Handshake.get(\n          TlsVersion.TLS_1_3,\n          CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n          listOf(),\n          listOf(),\n        ),\n      )\n    builder = builder.handshake(null)\n    builder = builder.header(\"\", \"\")\n    builder = builder.addHeader(\"\", \"\")\n    builder = builder.removeHeader(\"\")\n    builder = builder.headers(headersOf())\n    builder = builder.body(\"\".toResponseBody(null))\n    builder = builder.networkResponse(Response.Builder().build())\n    builder = builder.networkResponse(null)\n    builder = builder.cacheResponse(Response.Builder().build())\n    builder = builder.cacheResponse(null)\n    builder = builder.priorResponse(Response.Builder().build())\n    builder = builder.priorResponse(null)\n    builder = builder.sentRequestAtMillis(0L)\n    builder = builder.receivedResponseAtMillis(0L)\n    val response: Response = builder.build()\n  }\n\n  @Test\n  fun responseBody() {\n    var responseBody: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = TODO()\n\n        override fun contentLength(): Long = TODO()\n\n        override fun source(): BufferedSource = TODO()\n\n        override fun close() = TODO()\n      }\n    val byteStream = responseBody.byteStream()\n    val source = responseBody.source()\n    val bytes = responseBody.bytes()\n    val charStream = responseBody.charStream()\n    val string = responseBody.string()\n    responseBody.close()\n    responseBody = \"\".toResponseBody(\"\".toMediaType())\n    responseBody = \"\".toResponseBody(null)\n    responseBody = ByteString.EMPTY.toResponseBody(\"\".toMediaType())\n    responseBody = ByteString.EMPTY.toResponseBody(null)\n    responseBody = byteArrayOf(0, 1).toResponseBody(\"\".toMediaType())\n    responseBody = byteArrayOf(0, 1).toResponseBody(null)\n    responseBody = Buffer().asResponseBody(\"\".toMediaType(), 0L)\n    responseBody = Buffer().asResponseBody(null, 0L)\n  }\n\n  @Test\n  fun route() {\n    val route: Route = factory.newRoute()\n    val address: Address = route.address\n    val proxy: Proxy = route.proxy\n    val inetSocketAddress: InetSocketAddress = route.socketAddress\n    val requiresTunnel: Boolean = route.requiresTunnel()\n  }\n\n  @Test\n  fun socketPolicy() {\n    val socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN\n  }\n\n  @Test\n  fun tlsVersion() {\n    var tlsVersion: TlsVersion = TlsVersion.TLS_1_3\n    val javaName: String = tlsVersion.javaName\n    tlsVersion = TlsVersion.forJavaName(\"\")\n  }\n\n  @Test\n  fun webSocket() {\n    val webSocket =\n      object : WebSocket {\n        override fun request(): Request = TODO()\n\n        override fun queueSize(): Long = TODO()\n\n        override fun send(text: String): Boolean = TODO()\n\n        override fun send(bytes: ByteString): Boolean = TODO()\n\n        override fun close(\n          code: Int,\n          reason: String?,\n        ): Boolean = TODO()\n\n        override fun cancel() = TODO()\n      }\n  }\n\n  @Test\n  fun webSocketListener() {\n    val webSocketListener =\n      object : WebSocketListener() {\n        override fun onOpen(\n          webSocket: WebSocket,\n          response: Response,\n        ) = TODO()\n\n        override fun onMessage(\n          webSocket: WebSocket,\n          text: String,\n        ) = TODO()\n\n        override fun onMessage(\n          webSocket: WebSocket,\n          bytes: ByteString,\n        ) = TODO()\n\n        override fun onClosing(\n          webSocket: WebSocket,\n          code: Int,\n          reason: String,\n        ) = TODO()\n\n        override fun onClosed(\n          webSocket: WebSocket,\n          code: Int,\n          reason: String,\n        ) = TODO()\n\n        override fun onFailure(\n          webSocket: WebSocket,\n          t: Throwable,\n          response: Response?,\n        ) = TODO()\n      }\n  }\n\n  private fun newCookieHandler(): CookieHandler =\n    object : CookieHandler() {\n      override fun put(\n        uri: URI?,\n        responseHeaders: MutableMap<String, MutableList<String>>?,\n      ) = TODO()\n\n      override fun get(\n        uri: URI?,\n        requestHeaders: MutableMap<String, MutableList<String>>?,\n      ): MutableMap<String, MutableList<String>> = TODO()\n    }\n\n  private fun newInterceptorChain(): Interceptor.Chain =\n    object : Interceptor.Chain {\n      override fun request(): Request = TODO()\n\n      override fun proceed(request: Request): Response = TODO()\n\n      override fun connection(): Connection? = TODO()\n\n      override fun call(): Call = TODO()\n\n      override fun connectTimeoutMillis(): Int = TODO()\n\n      override fun withConnectTimeout(\n        timeout: Int,\n        unit: TimeUnit,\n      ): Interceptor.Chain = TODO()\n\n      override fun readTimeoutMillis(): Int = TODO()\n\n      override fun withReadTimeout(\n        timeout: Int,\n        unit: TimeUnit,\n      ): Interceptor.Chain = TODO()\n\n      override fun writeTimeoutMillis(): Int = TODO()\n\n      override fun withWriteTimeout(\n        timeout: Int,\n        unit: TimeUnit,\n      ): Interceptor.Chain = TODO()\n\n      override val dns: Dns\n        get() = TODO()\n\n      override val socketFactory: SocketFactory\n        get() = TODO()\n\n      override val retryOnConnectionFailure: Boolean\n        get() = TODO()\n      override val authenticator: Authenticator\n        get() = TODO()\n      override val cookieJar: CookieJar\n        get() = TODO()\n      override val cache: Cache?\n        get() = TODO()\n      override val proxy: Proxy?\n        get() = TODO()\n      override val proxySelector: ProxySelector\n        get() = TODO()\n      override val proxyAuthenticator: Authenticator\n        get() = TODO()\n      override val sslSocketFactoryOrNull: SSLSocketFactory\n        get() = TODO()\n      override val x509TrustManagerOrNull: X509TrustManager\n        get() = TODO()\n      override val hostnameVerifier: HostnameVerifier\n        get() = TODO()\n      override val certificatePinner: CertificatePinner\n        get() = TODO()\n      override val connectionPool: ConnectionPool\n        get() = TODO()\n\n      override fun withDns(dns: Dns): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withSocketFactory(socketFactory: SocketFactory): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withRetryOnConnectionFailure(retryOnConnectionFailure: Boolean): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withAuthenticator(authenticator: Authenticator): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withCookieJar(cookieJar: CookieJar): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withCache(cache: Cache?): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withProxy(proxy: Proxy?): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withProxySelector(proxySelector: ProxySelector): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withProxyAuthenticator(proxyAuthenticator: Authenticator): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withSslSocketFactory(\n        sslSocketFactory: SSLSocketFactory?,\n        x509TrustManager: X509TrustManager?,\n      ): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withHostnameVerifier(hostnameVerifier: HostnameVerifier): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withCertificatePinner(certificatePinner: CertificatePinner): Interceptor.Chain {\n        TODO()\n      }\n\n      override fun withConnectionPool(connectionPool: ConnectionPool): Interceptor.Chain {\n        TODO()\n      }\n\n      override val followSslRedirects: Boolean\n        get() = TODO()\n      override val followRedirects: Boolean\n        get() = TODO()\n      override val eventListener: EventListener\n        get() = TODO()\n    }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/LoomTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isTrue\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass LoomTest {\n  @JvmField\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @JvmField\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private lateinit var client: OkHttpClient\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeLoom()\n\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .dispatcher(Dispatcher(newVirtualThreadPerTaskExecutor()))\n        .build()\n  }\n\n  private fun newVirtualThreadPerTaskExecutor(): ExecutorService =\n    Executors::class.java.getMethod(\"newVirtualThreadPerTaskExecutor\").invoke(null) as ExecutorService\n\n  @Test\n  fun testRequest() {\n    server.enqueue(MockResponse())\n\n    val request = Request(server.url(\"/\"))\n\n    client.newCall(request).execute().use {\n      assertThat(it.code).isEqualTo(200)\n    }\n  }\n\n  @Test\n  fun testIfSupported() {\n    assertThat(platform.isLoom()).isTrue()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/MediaTypeGetTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\nimport okhttp3.MediaType.Companion.toMediaType\n\nopen class MediaTypeGetTest : MediaTypeTest() {\n  override fun parse(string: String): MediaType = string.toMediaType()\n\n  override fun assertInvalid(\n    string: String,\n    exceptionMessage: String?,\n  ) {\n    val e =\n      assertFailsWith<IllegalArgumentException> {\n        parse(string)\n      }\n    assertEquals(exceptionMessage, e.message)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/MediaTypeJvmTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.util.Locale\nimport kotlin.test.assertEquals\nimport kotlin.test.assertNull\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.internal.platform.Platform.Companion.isAndroid\nimport org.junit.jupiter.api.Test\n\nclass MediaTypeJvmTest : MediaTypeGetTest() {\n  override fun MediaType.charsetName(): String? = this.charset()?.name()\n\n  @Test fun testCharsetNameIsDoubleQuotedAndSingleQuotedAndroid() {\n    val mediaType = \"text/plain;charset=\\\"'utf-8'\\\"\".toMediaType()\n    if (isAndroid) {\n      // Charset.forName(\"'utf-8'\") == UTF-8\n      assertEquals(\"UTF-8\", mediaType.charsetName())\n    } else {\n      assertNull(mediaType.charset())\n    }\n  }\n\n  @Test fun testDefaultCharset() {\n    val noCharset = parse(\"text/plain\")\n    assertEquals(\n      \"UTF-8\",\n      noCharset.charset(Charsets.UTF_8)!!.name(),\n    )\n    assertEquals(\n      \"US-ASCII\",\n      noCharset.charset(Charsets.US_ASCII)!!.name(),\n    )\n    val charset = parse(\"text/plain; charset=iso-8859-1\")\n    assertEquals(\n      \"ISO-8859-1\",\n      charset.charset(Charsets.UTF_8)!!.name(),\n    )\n    assertEquals(\n      \"ISO-8859-1\",\n      charset.charset(Charsets.US_ASCII)!!.name(),\n    )\n  }\n\n  @Test fun testTurkishDotlessIWithEnUs() {\n    withLocale(Locale(\"en\", \"US\")) {\n      val mediaType = parse(\"IMAGE/JPEG\")\n      assertEquals(\"image\", mediaType.type)\n      assertEquals(\"jpeg\", mediaType.subtype)\n    }\n  }\n\n  @Test fun testTurkishDotlessIWithTrTr() {\n    withLocale(Locale(\"tr\", \"TR\")) {\n      val mediaType = parse(\"IMAGE/JPEG\")\n      assertEquals(\"image\", mediaType.type)\n      assertEquals(\"jpeg\", mediaType.subtype)\n    }\n  }\n\n  private fun <T> withLocale(\n    locale: Locale,\n    block: () -> T,\n  ): T {\n    val previous = Locale.getDefault()\n    try {\n      Locale.setDefault(locale)\n      return block()\n    } finally {\n      Locale.setDefault(previous)\n    }\n  }\n\n  @Test fun testIllegalCharsetName() {\n    val mediaType = parse(\"text/plain; charset=\\\"!@#$%^&*()\\\"\")\n    assertNull(mediaType.charsetName())\n  }\n\n  @Test fun testUnsupportedCharset() {\n    val mediaType = parse(\"text/plain; charset=utf-wtf\")\n    assertNull(mediaType.charsetName())\n  }\n\n  @Test fun testCharsetNameIsDoubleQuotedAndSingleQuoted() {\n    val mediaType = parse(\"text/plain;charset=\\\"'utf-8'\\\"\")\n    assertNull(mediaType.charsetName())\n  }\n\n  @Test fun testCharsetNameIsDoubleQuotedSingleQuote() {\n    val mediaType = parse(\"text/plain;charset=\\\"'\\\"\")\n    assertNull(mediaType.charsetName())\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/MediaTypeTest.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n * Copyright (C) 2011 The Guava Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertNull\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\n\n/**\n * Test MediaType API and parsing.\n *\n * This test includes tests from [Guava's](https://code.google.com/p/guava-libraries/)\n * MediaTypeTest.\n */\nopen class MediaTypeTest {\n  open fun MediaType.charsetName(): String? = parameter(\"charset\")?.uppercase()\n\n  protected open fun parse(string: String): MediaType = string.toMediaTypeOrNull()!!\n\n  protected open fun assertInvalid(\n    string: String,\n    exceptionMessage: String?,\n  ) {\n    assertNull(string.toMediaTypeOrNull(), exceptionMessage)\n  }\n\n  @Test fun testParse() {\n    val mediaType = parse(\"text/plain;boundary=foo;charset=utf-8\")\n    assertEquals(\"text\", mediaType.type)\n    assertEquals(\"plain\", mediaType.subtype)\n    assertEquals(\"UTF-8\", mediaType.charsetName())\n    assertEquals(\"text/plain;boundary=foo;charset=utf-8\", mediaType.toString())\n    assertEquals(mediaType, parse(\"text/plain;boundary=foo;charset=utf-8\"))\n    assertEquals(\n      mediaType.hashCode(),\n      parse(\"text/plain;boundary=foo;charset=utf-8\").hashCode(),\n    )\n  }\n\n  @Test fun testValidParse() {\n    assertMediaType(\"text/plain\")\n    assertMediaType(\"application/atom+xml; charset=utf-8\")\n    assertMediaType(\"application/atom+xml; a=1; a=2; b=3\")\n    assertMediaType(\"image/gif; foo=bar\")\n    assertMediaType(\"text/plain; a=1\")\n    assertMediaType(\"text/plain; a=1; a=2; b=3\")\n    assertMediaType(\"text/plain; charset=utf-16\")\n    assertMediaType(\"text/plain; \\t \\n \\r a=b\")\n    assertMediaType(\"text/plain;\")\n    assertMediaType(\"text/plain; \")\n    assertMediaType(\"text/plain; a=1;\")\n    assertMediaType(\"text/plain; a=1; \")\n    assertMediaType(\"text/plain; a=1;; b=2\")\n    assertMediaType(\"text/plain;;\")\n    assertMediaType(\"text/plain; ;\")\n  }\n\n  @Test fun testInvalidParse() {\n    assertInvalid(\"\", \"No subtype found for: \\\"\\\"\")\n    assertInvalid(\"/\", \"No subtype found for: \\\"/\\\"\")\n    assertInvalid(\"text\", \"No subtype found for: \\\"text\\\"\")\n    assertInvalid(\"text/\", \"No subtype found for: \\\"text/\\\"\")\n    assertInvalid(\"te<t/plain\", \"No subtype found for: \\\"te<t/plain\\\"\")\n    assertInvalid(\" text/plain\", \"No subtype found for: \\\" text/plain\\\"\")\n    assertInvalid(\"te xt/plain\", \"No subtype found for: \\\"te xt/plain\\\"\")\n    assertInvalid(\"text /plain\", \"No subtype found for: \\\"text /plain\\\"\")\n    assertInvalid(\"text/ plain\", \"No subtype found for: \\\"text/ plain\\\"\")\n    assertInvalid(\n      \"text/pl@in\",\n      \"Parameter is not formatted correctly: \\\"@in\\\" for: \\\"text/pl@in\\\"\",\n    )\n    assertInvalid(\n      \"text/plain; a\",\n      \"Parameter is not formatted correctly: \\\"a\\\" for: \\\"text/plain; a\\\"\",\n    )\n    assertInvalid(\n      \"text/plain; a=\",\n      \"Parameter is not formatted correctly: \\\"a=\\\" for: \\\"text/plain; a=\\\"\",\n    )\n    assertInvalid(\n      \"text/plain; a=@\",\n      \"Parameter is not formatted correctly: \\\"a=@\\\" for: \\\"text/plain; a=@\\\"\",\n    )\n    assertInvalid(\n      \"text/plain; a=\\\"@\",\n      \"Parameter is not formatted correctly: \\\"a=\\\"@\\\" for: \\\"text/plain; a=\\\"@\\\"\",\n    )\n    assertInvalid(\n      \"text/plain; a=1; b\",\n      \"Parameter is not formatted correctly: \\\"b\\\" for: \\\"text/plain; a=1; b\\\"\",\n    )\n    assertInvalid(\n      \"text/plain; a=1; b=\",\n      \"Parameter is not formatted correctly: \\\"b=\\\" for: \\\"text/plain; a=1; b=\\\"\",\n    )\n    assertInvalid(\n      \"text/plain; a=\\u2025\",\n      \"Parameter is not formatted correctly: \\\"a=‥\\\" for: \\\"text/plain; a=‥\\\"\",\n    )\n    assertInvalid(\n      \"text/pl ain\",\n      \"Parameter is not formatted correctly: \\\" ain\\\" for: \\\"text/pl ain\\\"\",\n    )\n    assertInvalid(\n      \"text/plain \",\n      \"Parameter is not formatted correctly: \\\" \\\" for: \\\"text/plain \\\"\",\n    )\n    assertInvalid(\n      \"text/plain ; a=1\",\n      \"Parameter is not formatted correctly: \\\" ; a=1\\\" for: \\\"text/plain ; a=1\\\"\",\n    )\n  }\n\n  @Test fun testDoubleQuotesAreSpecial() {\n    val mediaType = parse(\"text/plain;a=\\\";charset=utf-8;b=\\\"\")\n    assertNull(mediaType.charsetName())\n  }\n\n  @Test fun testSingleQuotesAreNotSpecial() {\n    val mediaType = parse(\"text/plain;a=';charset=utf-8;b='\")\n    assertEquals(\"UTF-8\", mediaType.charsetName())\n  }\n\n  @Test fun testParseWithSpecialCharacters() {\n    val mediaType = parse(\"!#$%&'*+-.{|}~/!#$%&'*+-.{|}~; !#$%&'*+-.{|}~=!#$%&'*+-.{|}~\")\n    assertEquals(\"!#$%&'*+-.{|}~\", mediaType.type)\n    assertEquals(\"!#$%&'*+-.{|}~\", mediaType.subtype)\n  }\n\n  @Test fun testCharsetIsOneOfManyParameters() {\n    val mediaType = parse(\"text/plain;a=1;b=2;charset=utf-8;c=3\")\n    assertEquals(\"text\", mediaType.type)\n    assertEquals(\"plain\", mediaType.subtype)\n    assertEquals(\"UTF-8\", mediaType.charsetName())\n    assertEquals(\"utf-8\", mediaType.parameter(\"charset\"))\n    assertEquals(\"1\", mediaType.parameter(\"a\"))\n    assertEquals(\"2\", mediaType.parameter(\"b\"))\n    assertEquals(\"3\", mediaType.parameter(\"c\"))\n    assertEquals(\"utf-8\", mediaType.parameter(\"CHARSET\"))\n    assertEquals(\"1\", mediaType.parameter(\"A\"))\n    assertEquals(\"2\", mediaType.parameter(\"B\"))\n    assertEquals(\"3\", mediaType.parameter(\"C\"))\n  }\n\n  @Test fun testCharsetAndQuoting() {\n    val mediaType = parse(\"text/plain;a=\\\";charset=us-ascii\\\";charset=\\\"utf-8\\\";b=\\\"iso-8859-1\\\"\")\n    assertEquals(\"UTF-8\", mediaType.charsetName())\n  }\n\n  @Test fun testDuplicatedCharsets() {\n    val mediaType = parse(\"text/plain; charset=utf-8; charset=UTF-8\")\n    assertEquals(\"UTF-8\", mediaType.charsetName())\n  }\n\n  @Test fun testMultipleCharsetsReturnsFirstMatch() {\n    val mediaType = parse(\"text/plain; charset=utf-8; charset=utf-16\")\n    assertEquals(\"UTF-8\", mediaType.charsetName())\n  }\n\n  /**\n   * This is invalid according to RFC 822. But it's what Chrome does and it avoids a potentially\n   * unpleasant IllegalCharsetNameException.\n   */\n  @Test fun testCharsetNameIsSingleQuoted() {\n    val mediaType = parse(\"text/plain;charset='utf-8'\")\n    assertEquals(\"UTF-8\", mediaType.charsetName())\n  }\n\n  @Test fun testParseDanglingSemicolon() {\n    val mediaType = parse(\"text/plain;\")\n    assertEquals(\"text\", mediaType.type)\n    assertEquals(\"plain\", mediaType.subtype)\n    assertNull(mediaType.charsetName())\n    assertEquals(\"text/plain;\", mediaType.toString())\n  }\n\n  @Test fun testParameter() {\n    val mediaType = parse(\"multipart/mixed; boundary=\\\"abcd\\\"\")\n    assertEquals(\"abcd\", mediaType.parameter(\"boundary\"))\n    assertEquals(\"abcd\", mediaType.parameter(\"BOUNDARY\"))\n  }\n\n  @Test fun testMultipleParameters() {\n    val mediaType = parse(\"Message/Partial; number=2; total=3; id=\\\"oc=abc@example.com\\\"\")\n    assertEquals(\"2\", mediaType.parameter(\"number\"))\n    assertEquals(\"3\", mediaType.parameter(\"total\"))\n    assertEquals(\"oc=abc@example.com\", mediaType.parameter(\"id\"))\n    assertNull(mediaType.parameter(\"foo\"))\n  }\n\n  @Test fun testRepeatedParameter() {\n    val mediaType = parse(\"multipart/mixed; number=2; number=3\")\n    assertEquals(\"2\", mediaType.parameter(\"number\"))\n  }\n\n  private fun assertMediaType(string: String) {\n    assertEquals(string, parse(string).toString())\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/MultipartBodyTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport java.nio.charset.StandardCharsets\nimport kotlin.test.assertFailsWith\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.utf8Size\nimport org.junit.jupiter.api.Test\n\nclass MultipartBodyTest {\n  @Test\n  fun onePartRequired() {\n    assertFailsWith<IllegalStateException> {\n      MultipartBody.Builder().build()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Multipart body must have at least one part.\")\n    }\n  }\n\n  @Test\n  fun singlePart() {\n    val expected =\n      \"\"\"\n      |--123\n      |\n      |Hello, World!\n      |--123--\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\")\n    val body =\n      MultipartBody\n        .Builder(\"123\")\n        .addPart(\"Hello, World!\".toRequestBody(null))\n        .build()\n    assertThat(body.boundary).isEqualTo(\"123\")\n    assertThat(body.type).isEqualTo(MultipartBody.MIXED)\n    assertThat(body.contentType().toString())\n      .isEqualTo(\"multipart/mixed; boundary=123\")\n    assertThat(body.parts.size).isEqualTo(1)\n    assertThat(body.contentLength()).isEqualTo(33L)\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(body.contentLength()).isEqualTo(buffer.size)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun threeParts() {\n    val expected =\n      \"\"\"\n      |--123\n      |\n      |Quick\n      |--123\n      |\n      |Brown\n      |--123\n      |\n      |Fox\n      |--123--\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\")\n    val body =\n      MultipartBody\n        .Builder(\"123\")\n        .addPart(\"Quick\".toRequestBody(null))\n        .addPart(\"Brown\".toRequestBody(null))\n        .addPart(\"Fox\".toRequestBody(null))\n        .build()\n    assertThat(body.boundary).isEqualTo(\"123\")\n    assertThat(body.type).isEqualTo(MultipartBody.MIXED)\n    assertThat(body.contentType().toString())\n      .isEqualTo(\"multipart/mixed; boundary=123\")\n    assertThat(body.parts.size).isEqualTo(3)\n    assertThat(body.contentLength()).isEqualTo(55L)\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(body.contentLength()).isEqualTo(buffer.size)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun fieldAndTwoFiles() {\n    val expected =\n      \"\"\"\n      |--AaB03x\n      |Content-Disposition: form-data; name=\"submit-name\"\n      |\n      |Larry\n      |--AaB03x\n      |Content-Disposition: form-data; name=\"files\"\n      |Content-Type: multipart/mixed; boundary=BbC04y\n      |\n      |--BbC04y\n      |Content-Disposition: file; filename=\"file1.txt\"\n      |Content-Type: text/plain; charset=utf-8\n      |\n      |... contents of file1.txt ...\n      |--BbC04y\n      |Content-Disposition: file; filename=\"file2.gif\"\n      |Content-Transfer-Encoding: binary\n      |Content-Type: image/gif\n      |\n      |... contents of file2.gif ...\n      |--BbC04y--\n      |\n      |--AaB03x--\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\")\n    val body =\n      MultipartBody\n        .Builder(\"AaB03x\")\n        .setType(MultipartBody.FORM)\n        .addFormDataPart(\"submit-name\", \"Larry\")\n        .addFormDataPart(\n          \"files\",\n          null,\n          MultipartBody\n            .Builder(\"BbC04y\")\n            .addPart(\n              headersOf(\"Content-Disposition\", \"file; filename=\\\"file1.txt\\\"\"),\n              \"... contents of file1.txt ...\".toRequestBody(\"text/plain\".toMediaType()),\n            ).addPart(\n              headersOf(\n                \"Content-Disposition\",\n                \"file; filename=\\\"file2.gif\\\"\",\n                \"Content-Transfer-Encoding\",\n                \"binary\",\n              ),\n              \"... contents of file2.gif ...\"\n                .toByteArray(StandardCharsets.UTF_8)\n                .toRequestBody(\"image/gif\".toMediaType()),\n            ).build(),\n        ).build()\n    assertThat(body.boundary).isEqualTo(\"AaB03x\")\n    assertThat(body.type).isEqualTo(MultipartBody.FORM)\n    assertThat(body.contentType().toString()).isEqualTo(\n      \"multipart/form-data; boundary=AaB03x\",\n    )\n    assertThat(body.parts.size).isEqualTo(2)\n    assertThat(body.contentLength()).isEqualTo(488L)\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(body.contentLength()).isEqualTo(buffer.size)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun stringEscapingIsWeird() {\n    val expected =\n      \"\"\"\n      |--AaB03x\n      |Content-Disposition: form-data; name=\"field with spaces\"; filename=\"filename with spaces.txt\"\n      |Content-Type: text/plain; charset=utf-8\n      |\n      |okay\n      |--AaB03x\n      |Content-Disposition: form-data; name=\"field with %22\"\n      |\n      |\"\n      |--AaB03x\n      |Content-Disposition: form-data; name=\"field with %22\"\n      |\n      |%22\n      |--AaB03x\n      |Content-Disposition: form-data; name=\"field with ~\"\n      |\n      |Alpha\n      |--AaB03x--\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\")\n    val body =\n      MultipartBody\n        .Builder(\"AaB03x\")\n        .setType(MultipartBody.FORM)\n        .addFormDataPart(\n          \"field with spaces\",\n          \"filename with spaces.txt\",\n          \"okay\".toRequestBody(\"text/plain; charset=utf-8\".toMediaType()),\n        ).addFormDataPart(\"field with \\\"\", \"\\\"\")\n        .addFormDataPart(\"field with %22\", \"%22\")\n        .addFormDataPart(\"field with \\u007e\", \"Alpha\")\n        .build()\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun streamingPartHasNoLength() {\n    class StreamingBody(\n      private val body: String,\n    ) : RequestBody() {\n      override fun contentType(): MediaType? = null\n\n      @Throws(IOException::class)\n      override fun writeTo(sink: BufferedSink) {\n        sink.writeUtf8(body)\n      }\n    }\n\n    val expected =\n      \"\"\"\n      |--123\n      |\n      |Quick\n      |--123\n      |\n      |Brown\n      |--123\n      |\n      |Fox\n      |--123--\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\")\n    val body =\n      MultipartBody\n        .Builder(\"123\")\n        .addPart(\"Quick\".toRequestBody(null))\n        .addPart(StreamingBody(\"Brown\"))\n        .addPart(\"Fox\".toRequestBody(null))\n        .build()\n    assertThat(body.boundary).isEqualTo(\"123\")\n    assertThat(body.type).isEqualTo(MultipartBody.MIXED)\n    assertThat(body.contentType().toString())\n      .isEqualTo(\"multipart/mixed; boundary=123\")\n    assertThat(body.parts.size).isEqualTo(3)\n    assertThat(body.contentLength()).isEqualTo(-1)\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun contentTypeHeaderIsForbidden() {\n    val multipart = MultipartBody.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      multipart.addPart(\n        headersOf(\"Content-Type\", \"text/plain\"),\n        \"Hello, World!\".toRequestBody(null),\n      )\n    }\n  }\n\n  @Test\n  fun contentLengthHeaderIsForbidden() {\n    val multipart = MultipartBody.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      multipart.addPart(\n        headersOf(\"Content-Length\", \"13\"),\n        \"Hello, World!\".toRequestBody(null),\n      )\n    }\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun partAccessors() {\n    val body =\n      MultipartBody\n        .Builder()\n        .addPart(headersOf(\"Foo\", \"Bar\"), \"Baz\".toRequestBody(null))\n        .build()\n    assertThat(body.parts.size).isEqualTo(1)\n    val part1Buffer = Buffer()\n    val part1 = body.part(0)\n    part1.body.writeTo(part1Buffer)\n    assertThat(part1.headers).isEqualTo(headersOf(\"Foo\", \"Bar\"))\n    assertThat(part1Buffer.readUtf8()).isEqualTo(\"Baz\")\n  }\n\n  @Test\n  fun nonAsciiFilename() {\n    val expected =\n      \"\"\"\n      |--AaB03x\n      |Content-Disposition: form-data; name=\"attachment\"; filename=\"resumé.pdf\"\n      |Content-Type: application/pdf; charset=utf-8\n      |\n      |Jesse’s Resumé\n      |--AaB03x--\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\")\n    val body =\n      MultipartBody\n        .Builder(\"AaB03x\")\n        .setType(MultipartBody.FORM)\n        .addFormDataPart(\n          \"attachment\",\n          \"resumé.pdf\",\n          \"Jesse’s Resumé\".toRequestBody(\"application/pdf\".toMediaTypeOrNull()),\n        ).build()\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun writeTwice() {\n    val expected =\n      \"\"\"\n      |--123\n      |\n      |Hello, World!\n      |--123--\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\")\n    val body =\n      MultipartBody\n        .Builder(\"123\")\n        .addPart(\"Hello, World!\".toRequestBody(null))\n        .build()\n\n    assertThat(body.isOneShot()).isEqualTo(false)\n\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(body.contentLength()).isEqualTo(buffer.size)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n\n    val buffer2 = Buffer()\n    body.writeTo(buffer2)\n    assertThat(body.contentLength()).isEqualTo(buffer2.size)\n    assertThat(buffer2.readUtf8()).isEqualTo(expected)\n  }\n\n  @Test\n  fun writeTwiceWithOneShot() {\n    val expected =\n      \"\"\"\n      |--123\n      |\n      |Hello, World!\n      |--123--\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\")\n    val body =\n      MultipartBody\n        .Builder(\"123\")\n        .addPart(\"Hello, World!\".toOneShotRequestBody())\n        .build()\n\n    assertThat(body.isOneShot()).isEqualTo(true)\n\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    assertThat(body.contentLength()).isEqualTo(buffer.size)\n    assertThat(buffer.readUtf8()).isEqualTo(expected)\n  }\n\n  fun String.toOneShotRequestBody(): RequestBody =\n    object : RequestBody() {\n      override fun contentType() = null\n\n      override fun isOneShot(): Boolean = true\n\n      override fun contentLength() = this@toOneShotRequestBody.utf8Size()\n\n      override fun writeTo(sink: BufferedSink) {\n        sink.writeUtf8(this@toOneShotRequestBody)\n      }\n    }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/MultipartReaderTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport java.io.EOFException\nimport java.net.ProtocolException\nimport kotlin.test.assertFailsWith\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okio.Buffer\nimport okio.BufferedSink\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\n\n@Tag(\"Slowish\")\nclass MultipartReaderTest {\n  @Test fun `parse multipart`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |Content-Type: text/plain; charset=utf-8\n      |Content-ID: abc\n      |\n      |abcd\n      |efgh\n      |--simple boundary\n      |Content-Type: text/plain; charset=utf-8\n      |Content-ID: ijk\n      |\n      |ijkl\n      |mnop\n      |\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n    assertThat(parts.boundary).isEqualTo(\"simple boundary\")\n\n    val partAbc = parts.nextPart()!!\n    assertThat(partAbc.headers).isEqualTo(\n      headersOf(\n        \"Content-Type\",\n        \"text/plain; charset=utf-8\",\n        \"Content-ID\",\n        \"abc\",\n      ),\n    )\n    assertThat(partAbc.body.readUtf8()).isEqualTo(\"abcd\\r\\nefgh\")\n\n    val partIjk = parts.nextPart()!!\n    assertThat(partIjk.headers).isEqualTo(\n      headersOf(\n        \"Content-Type\",\n        \"text/plain; charset=utf-8\",\n        \"Content-ID\",\n        \"ijk\",\n      ),\n    )\n    assertThat(partIjk.body.readUtf8()).isEqualTo(\"ijkl\\r\\nmnop\\r\\n\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `parse from response body`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val responseBody =\n      multipart.toResponseBody(\n        \"application/multipart; boundary=\\\"simple boundary\\\"\".toMediaType(),\n      )\n\n    val parts = MultipartReader(responseBody)\n    assertThat(parts.boundary).isEqualTo(\"simple boundary\")\n\n    val part = parts.nextPart()!!\n    assertThat(part.body.readUtf8()).isEqualTo(\"abcd\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `truncated multipart`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |efgh\n      |\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val part = parts.nextPart()!!\n    assertFailsWith<EOFException> {\n      part.body.readUtf8()\n    }\n\n    assertFailsWith<EOFException> {\n      assertThat(parts.nextPart()).isNull()\n    }\n  }\n\n  @Test fun `malformed headers`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |abcd\n      |\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    assertFailsWith<EOFException> {\n      parts.nextPart()\n    }\n  }\n\n  @Test fun `lf instead of crlf boundary is not honored`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary\n      |\n      |efgh\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n        .replace(Regex(\"(?m)abcd\\r\\n\"), \"abcd\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val part = parts.nextPart()!!\n    assertThat(part.body.readUtf8()).isEqualTo(\"abcd\\n--simple boundary\\r\\n\\r\\nefgh\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `partial boundary is not honored`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundar\n      |\n      |efgh\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val part = parts.nextPart()!!\n    assertThat(part.body.readUtf8()).isEqualTo(\"abcd\\r\\n--simple boundar\\r\\n\\r\\nefgh\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `do not need to read entire part`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |efgh\n      |ijkl\n      |--simple boundary\n      |\n      |mnop\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    parts.nextPart()!!\n    val partMno = parts.nextPart()!!\n    assertThat(partMno.body.readUtf8()).isEqualTo(\"mnop\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `cannot read part after calling nextPart`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |efgh\n      |ijkl\n      |--simple boundary\n      |\n      |mnop\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val partAbc = parts.nextPart()!!\n    val partMno = parts.nextPart()!!\n\n    assertFailsWith<IllegalStateException> {\n      partAbc.body.request(20)\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"closed\")\n    }\n\n    assertThat(partMno.body.readUtf8()).isEqualTo(\"mnop\")\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `cannot read part after calling close`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val part = parts.nextPart()!!\n    parts.close()\n\n    assertFailsWith<IllegalStateException> {\n      part.body.request(10)\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"closed\")\n    }\n  }\n\n  @Test fun `cannot call nextPart after calling close`() {\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer(),\n      )\n\n    parts.close()\n\n    assertFailsWith<IllegalStateException> {\n      parts.nextPart()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"closed\")\n    }\n  }\n\n  @Test fun `zero parts`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    assertFailsWith<ProtocolException> {\n      parts.nextPart()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"expected at least 1 part\")\n    }\n  }\n\n  @Test fun `skip preamble`() {\n    val multipart =\n      \"\"\"\n      |this is the preamble! it is invisible to application code\n      |\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val part = parts.nextPart()!!\n    assertThat(part.headers).isEqualTo(headersOf())\n    assertThat(part.body.readUtf8()).isEqualTo(\"abcd\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `skip epilogue`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary--\n      |this is the epilogue! it is also invisible to application code\n      |\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val part = parts.nextPart()!!\n    assertThat(part.headers).isEqualTo(headersOf())\n    assertThat(part.body.readUtf8()).isEqualTo(\"abcd\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `skip whitespace after boundary`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(Regex(\"(?m)simple boundary$\"), \"simple boundary \\t \\t\")\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val part = parts.nextPart()!!\n    assertThat(part.headers).isEqualTo(headersOf())\n    assertThat(part.body.readUtf8()).isEqualTo(\"abcd\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `skip whitespace after close delimiter`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(Regex(\"(?m)simple boundary--$\"), \"simple boundary-- \\t \\t\")\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val part = parts.nextPart()!!\n    assertThat(part.headers).isEqualTo(headersOf())\n    assertThat(part.body.readUtf8()).isEqualTo(\"abcd\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `other characters after boundary`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary hi\n      \"\"\".trimMargin()\n        .replace(Regex(\"(?m)simple boundary$\"), \"simple boundary \")\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    assertFailsWith<ProtocolException> {\n      parts.nextPart()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"unexpected characters after boundary\")\n    }\n  }\n\n  @Test fun `whitespace before close delimiter`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary  --\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    parts.nextPart()\n    assertFailsWith<ProtocolException> {\n      parts.nextPart()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"unexpected characters after boundary\")\n    }\n  }\n\n  /** The documentation advises that '-' is the simplest boundary possible. */\n  @Test fun `dash boundary`() {\n    val multipart =\n      \"\"\"\n      |---\n      |Content-ID: abc\n      |\n      |abcd\n      |---\n      |Content-ID: efg\n      |\n      |efgh\n      |-----\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"-\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    val partAbc = parts.nextPart()!!\n    assertThat(partAbc.headers).isEqualTo(headersOf(\"Content-ID\", \"abc\"))\n    assertThat(partAbc.body.readUtf8()).isEqualTo(\"abcd\")\n\n    val partEfg = parts.nextPart()!!\n    assertThat(partEfg.headers).isEqualTo(headersOf(\"Content-ID\", \"efg\"))\n    assertThat(partEfg.body.readUtf8()).isEqualTo(\"efgh\")\n\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `no more parts is idempotent`() {\n    val multipart =\n      \"\"\"\n      |--simple boundary\n      |\n      |abcd\n      |--simple boundary--\n      |\n      |efgh\n      |--simple boundary--\n      \"\"\".trimMargin()\n        .replace(\"\\n\", \"\\r\\n\")\n\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer().writeUtf8(multipart),\n      )\n\n    assertThat(parts.nextPart()).isNotNull()\n    assertThat(parts.nextPart()).isNull()\n    assertThat(parts.nextPart()).isNull()\n  }\n\n  @Test fun `empty source`() {\n    val parts =\n      MultipartReader(\n        boundary = \"simple boundary\",\n        source = Buffer(),\n      )\n\n    assertFailsWith<EOFException> {\n      parts.nextPart()\n    }\n  }\n\n  /** Confirm that [MultipartBody] and [MultipartReader] can work together. */\n  @Test fun `multipart round trip`() {\n    val body =\n      MultipartBody\n        .Builder(\"boundary\")\n        .setType(MultipartBody.PARALLEL)\n        .addPart(\"Quick\".toRequestBody(\"text/plain\".toMediaType()))\n        .addFormDataPart(\"color\", \"Brown\")\n        .addFormDataPart(\"animal\", \"fox.txt\", \"Fox\".toRequestBody())\n        .build()\n\n    val bodyContent = Buffer()\n    body.writeTo(bodyContent)\n\n    val reader = MultipartReader(bodyContent, \"boundary\")\n\n    val quickPart = reader.nextPart()!!\n    assertThat(quickPart.headers).isEqualTo(\n      headersOf(\n        \"Content-Type\",\n        \"text/plain; charset=utf-8\",\n      ),\n    )\n    assertThat(quickPart.body.readUtf8()).isEqualTo(\"Quick\")\n\n    val brownPart = reader.nextPart()!!\n    assertThat(brownPart.headers).isEqualTo(\n      headersOf(\n        \"Content-Disposition\",\n        \"form-data; name=\\\"color\\\"\",\n      ),\n    )\n    assertThat(brownPart.body.readUtf8()).isEqualTo(\"Brown\")\n\n    val foxPart = reader.nextPart()!!\n    assertThat(foxPart.headers).isEqualTo(\n      headersOf(\n        \"Content-Disposition\",\n        \"form-data; name=\\\"animal\\\"; filename=\\\"fox.txt\\\"\",\n      ),\n    )\n    assertThat(foxPart.body.readUtf8()).isEqualTo(\"Fox\")\n\n    assertThat(reader.nextPart()).isNull()\n  }\n\n  /**\n   * Read 100 MiB of 'a' chars. This was really slow due to a performance bug in [MultipartReader],\n   * and will be really slow if we regress the fix for that.\n   */\n  @Test\n  fun `reading a large part with small byteCount`() {\n    val multipartBody =\n      MultipartBody\n        .Builder(\"foo\")\n        .addPart(\n          headersOf(\"header-name\", \"header-value\"),\n          object : RequestBody() {\n            override fun contentType() = \"application/octet-stream\".toMediaTypeOrNull()\n\n            override fun contentLength() = 1024L * 1024L * 100L\n\n            override fun writeTo(sink: BufferedSink) {\n              val a1024x1024 = \"a\".repeat(1024 * 1024)\n              repeat(100) {\n                sink.writeUtf8(a1024x1024)\n              }\n            }\n          },\n        ).build()\n    val buffer = Buffer()\n    multipartBody.writeTo(buffer)\n\n    val multipartReader = MultipartReader(buffer, \"foo\")\n    val onlyPart = multipartReader.nextPart()!!\n    assertThat(onlyPart.headers).isEqualTo(\n      headersOf(\n        \"header-name\",\n        \"header-value\",\n        \"Content-Type\",\n        \"application/octet-stream\",\n      ),\n    )\n    val readBuff = Buffer()\n    var byteCount = 0L\n    while (true) {\n      val readByteCount = onlyPart.body.read(readBuff, 1024L)\n      if (readByteCount == -1L) break\n      byteCount += readByteCount\n      assertThat(readBuff.readUtf8()).isEqualTo(\"a\".repeat(readByteCount.toInt()))\n    }\n    assertThat(byteCount).isEqualTo(1024L * 1024L * 100L)\n    assertThat(multipartReader.nextPart()).isNull()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/OkHttpClientTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isNotInstanceOf\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isSameInstanceAs\nimport java.io.IOException\nimport java.net.CookieManager\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.ResponseCache\nimport java.net.SocketAddress\nimport java.net.URI\nimport java.time.Duration\nimport java.util.AbstractList\nimport javax.net.ssl.SSLSession\nimport javax.net.ssl.SSLSocketFactory\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.internal.platform.Platform.Companion.get\nimport okhttp3.internal.proxy.NullProxySelector\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertNotSame\nimport org.junit.jupiter.api.Assertions.assertSame\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass OkHttpClientTest {\n  @RegisterExtension\n  var platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @AfterEach fun tearDown() {\n    ProxySelector.setDefault(DEFAULT_PROXY_SELECTOR)\n    CookieManager.setDefault(DEFAULT_COOKIE_HANDLER)\n    ResponseCache.setDefault(DEFAULT_RESPONSE_CACHE)\n  }\n\n  @Test fun durationDefaults() {\n    val client = clientTestRule.newClient()\n    assertThat(client.callTimeoutMillis).isEqualTo(0)\n    assertThat(client.connectTimeoutMillis).isEqualTo(10000)\n    assertThat(client.readTimeoutMillis).isEqualTo(10000)\n    assertThat(client.writeTimeoutMillis).isEqualTo(10000)\n    assertThat(client.pingIntervalMillis).isEqualTo(0)\n    assertThat(client.webSocketCloseTimeout).isEqualTo(60_000)\n  }\n\n  @Test fun webSocketDefaults() {\n    val client = clientTestRule.newClient()\n    assertThat(client.minWebSocketMessageToCompress).isEqualTo(1024)\n  }\n\n  @Test fun timeoutValidRange() {\n    val builder = OkHttpClient.Builder()\n    try {\n      builder.callTimeout(Duration.ofNanos(1))\n    } catch (ignored: IllegalArgumentException) {\n    }\n    try {\n      builder.connectTimeout(Duration.ofNanos(1))\n    } catch (ignored: IllegalArgumentException) {\n    }\n    try {\n      builder.writeTimeout(Duration.ofNanos(1))\n    } catch (ignored: IllegalArgumentException) {\n    }\n    try {\n      builder.readTimeout(Duration.ofNanos(1))\n    } catch (ignored: IllegalArgumentException) {\n    }\n    try {\n      builder.callTimeout(Duration.ofDays(365))\n    } catch (ignored: IllegalArgumentException) {\n    }\n    try {\n      builder.connectTimeout(Duration.ofDays(365))\n    } catch (ignored: IllegalArgumentException) {\n    }\n    try {\n      builder.writeTimeout(Duration.ofDays(365))\n    } catch (ignored: IllegalArgumentException) {\n    }\n    try {\n      builder.readTimeout(Duration.ofDays(365))\n    } catch (ignored: IllegalArgumentException) {\n    }\n  }\n\n  @Test fun clonedInterceptorsListsAreIndependent() {\n    val interceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        chain.proceed(chain.request())\n      }\n    val original = clientTestRule.newClient()\n    original\n      .newBuilder()\n      .addInterceptor(interceptor)\n      .addNetworkInterceptor(interceptor)\n      .build()\n    assertThat(original.interceptors.size).isEqualTo(0)\n    assertThat(original.networkInterceptors.size).isEqualTo(0)\n  }\n\n  /**\n   * When copying the client, stateful things like the connection pool are shared across all\n   * clients.\n   */\n  @Test fun cloneSharesStatefulInstances() {\n    val client = clientTestRule.newClient()\n\n    // Values should be non-null.\n    val a = client.newBuilder().build()\n    assertThat(a.dispatcher).isNotNull()\n    assertThat(a.connectionPool).isNotNull()\n    assertThat(a.sslSocketFactory).isNotNull()\n    assertThat(a.x509TrustManager).isNotNull()\n\n    // Multiple clients share the instances.\n    val b = client.newBuilder().build()\n    assertThat(b.dispatcher).isSameInstanceAs(a.dispatcher)\n    assertThat(b.connectionPool).isSameInstanceAs(a.connectionPool)\n    assertThat(b.sslSocketFactory).isSameInstanceAs(a.sslSocketFactory)\n    assertThat(b.x509TrustManager).isSameInstanceAs(a.x509TrustManager)\n  }\n\n  @Test fun setProtocolsRejectsHttp10() {\n    val builder = OkHttpClient.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.protocols(listOf(Protocol.HTTP_1_0, Protocol.HTTP_1_1))\n    }\n  }\n\n  @Test fun certificatePinnerEquality() {\n    val clientA = clientTestRule.newClient()\n    val clientB = clientTestRule.newClient()\n    assertThat(clientB.certificatePinner).isEqualTo(clientA.certificatePinner)\n  }\n\n  @Test fun nullInterceptorInList() {\n    val builder = OkHttpClient.Builder()\n    builder.interceptors().addAll(listOf(null) as List<Interceptor>)\n    assertFailsWith<IllegalStateException> {\n      builder.build()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Null interceptor: [null]\")\n    }\n  }\n\n  @Test fun nullNetworkInterceptorInList() {\n    val builder = OkHttpClient.Builder()\n    builder.networkInterceptors().addAll(listOf(null) as List<Interceptor>)\n    assertFailsWith<IllegalStateException> {\n      builder.build()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Null network interceptor: [null]\")\n    }\n  }\n\n  @Test fun testH2PriorKnowledgeOkHttpClientConstructionFallback() {\n    assertFailsWith<IllegalArgumentException> {\n      OkHttpClient\n        .Builder()\n        .protocols(listOf(Protocol.H2_PRIOR_KNOWLEDGE, Protocol.HTTP_1_1))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"protocols containing h2_prior_knowledge cannot use other protocols: \" +\n          \"[h2_prior_knowledge, http/1.1]\",\n      )\n    }\n  }\n\n  @Test fun testH2PriorKnowledgeOkHttpClientConstructionDuplicates() {\n    assertFailsWith<IllegalArgumentException> {\n      OkHttpClient\n        .Builder()\n        .protocols(listOf(Protocol.H2_PRIOR_KNOWLEDGE, Protocol.H2_PRIOR_KNOWLEDGE))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"protocols containing h2_prior_knowledge cannot use other protocols: \" +\n          \"[h2_prior_knowledge, h2_prior_knowledge]\",\n      )\n    }\n  }\n\n  @Test fun testH2PriorKnowledgeOkHttpClientConstructionSuccess() {\n    val okHttpClient =\n      OkHttpClient\n        .Builder()\n        .protocols(listOf(Protocol.H2_PRIOR_KNOWLEDGE))\n        .build()\n    assertThat(okHttpClient.protocols.size).isEqualTo(1)\n    assertThat(okHttpClient.protocols[0]).isEqualTo(Protocol.H2_PRIOR_KNOWLEDGE)\n  }\n\n  @Test fun nullDefaultProxySelector() {\n    server!!.enqueue(MockResponse(body = \"abc\"))\n    ProxySelector.setDefault(null)\n    val client = clientTestRule.newClient()\n    val request = Request(server!!.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n  }\n\n  @Test fun sslSocketFactorySetAsSocketFactory() {\n    val builder = OkHttpClient.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.socketFactory(SSLSocketFactory.getDefault())\n    }\n  }\n\n  @Test fun noSslSocketFactoryConfigured() {\n    val client =\n      OkHttpClient\n        .Builder()\n        .connectionSpecs(listOf(ConnectionSpec.CLEARTEXT))\n        .build()\n    assertFailsWith<IllegalStateException> {\n      client.sslSocketFactory\n    }\n  }\n\n  @Test fun nullHostileProtocolList() {\n    val nullHostileProtocols =\n      object : AbstractList<Protocol?>() {\n        override val size: Int = 1\n\n        override fun get(index: Int) = Protocol.HTTP_1_1\n\n        override fun contains(element: Protocol?): Boolean {\n          if (element == null) throw NullPointerException()\n          return super.contains(element)\n        }\n\n        override fun indexOf(element: Protocol?): Int {\n          if (element == null) throw NullPointerException()\n          return super.indexOf(element)\n        }\n      } as List<Protocol>\n    val client =\n      OkHttpClient\n        .Builder()\n        .protocols(nullHostileProtocols)\n        .build()\n    assertEquals(\n      listOf(Protocol.HTTP_1_1),\n      client.protocols,\n    )\n  }\n\n  @Test fun nullProtocolInList() {\n    val protocols =\n      mutableListOf(\n        Protocol.HTTP_1_1,\n        null,\n      )\n    assertFailsWith<IllegalArgumentException> {\n      OkHttpClient\n        .Builder()\n        .protocols(protocols as List<Protocol>)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"protocols must not contain null\")\n    }\n  }\n\n  @Test fun spdy3IsRemovedFromProtocols() {\n    val protocols =\n      mutableListOf(\n        Protocol.HTTP_1_1,\n        Protocol.SPDY_3,\n      )\n    val client =\n      OkHttpClient\n        .Builder()\n        .protocols(protocols)\n        .build()\n    assertThat(client.protocols).containsExactly(Protocol.HTTP_1_1)\n  }\n\n  @Test fun testProxyDefaults() {\n    var client = OkHttpClient.Builder().build()\n    assertThat(client.proxy).isNull()\n    assertThat(client.proxySelector)\n      .isNotInstanceOf(NullProxySelector::class.java)\n    client =\n      OkHttpClient\n        .Builder()\n        .proxy(Proxy.NO_PROXY)\n        .build()\n    assertThat(client.proxy).isSameInstanceAs(Proxy.NO_PROXY)\n    assertThat(client.proxySelector)\n      .isInstanceOf(NullProxySelector::class.java)\n    client =\n      OkHttpClient\n        .Builder()\n        .proxySelector(FakeProxySelector())\n        .build()\n    assertThat(client.proxy).isNull()\n    assertThat(client.proxySelector)\n      .isInstanceOf(FakeProxySelector::class.java)\n  }\n\n  @Test fun sharesRouteDatabase() {\n    val client =\n      OkHttpClient\n        .Builder()\n        .build()\n    val proxySelector: ProxySelector =\n      object : ProxySelector() {\n        override fun select(uri: URI): List<Proxy> = listOf()\n\n        override fun connectFailed(\n          uri: URI,\n          socketAddress: SocketAddress,\n          e: IOException,\n        ) {}\n      }\n\n    val trustManager = get().platformTrustManager()\n    val sslContext = get().newSSLContext()\n    sslContext.init(null, null, null)\n\n    // new client, may share all same fields but likely different connection pool\n    assertNotSame(\n      client.routeDatabase,\n      OkHttpClient\n        .Builder()\n        .build()\n        .routeDatabase,\n    )\n\n    // same client with no change affecting route db\n    assertSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .build()\n        .routeDatabase,\n    )\n    assertSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .callTimeout(Duration.ofSeconds(5))\n        .build()\n        .routeDatabase,\n    )\n\n    // logically different scope of client for route db\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .dns { listOf() }\n        .build()\n        .routeDatabase,\n    )\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .proxyAuthenticator { _: Route?, _: Response? -> null }\n        .build()\n        .routeDatabase,\n    )\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .protocols(listOf(Protocol.HTTP_1_1))\n        .build()\n        .routeDatabase,\n    )\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .connectionSpecs(listOf(ConnectionSpec.COMPATIBLE_TLS))\n        .build()\n        .routeDatabase,\n    )\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .proxySelector(proxySelector)\n        .build()\n        .routeDatabase,\n    )\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .proxy(Proxy.NO_PROXY)\n        .build()\n        .routeDatabase,\n    )\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .sslSocketFactory(sslContext.socketFactory, trustManager)\n        .build()\n        .routeDatabase,\n    )\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .hostnameVerifier { _: String?, _: SSLSession? -> false }\n        .build()\n        .routeDatabase,\n    )\n    assertNotSame(\n      client.routeDatabase,\n      client\n        .newBuilder()\n        .certificatePinner(CertificatePinner.Builder().build())\n        .build()\n        .routeDatabase,\n    )\n  }\n\n  @Test fun minWebSocketMessageToCompressNegative() {\n    val builder = OkHttpClient.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.minWebSocketMessageToCompress(-1024)\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"minWebSocketMessageToCompress must be positive: -1024\")\n    }\n  }\n\n  companion object {\n    private val DEFAULT_PROXY_SELECTOR = ProxySelector.getDefault()\n    private val DEFAULT_COOKIE_HANDLER = CookieManager.getDefault()\n    private val DEFAULT_RESPONSE_CACHE = ResponseCache.getDefault()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/OpenJSSETest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isNotNull\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.TestUtil.assumeNetwork\nimport okhttp3.internal.connectionAccessor\nimport okhttp3.internal.exchangeAccessor\nimport okhttp3.internal.platform.OpenJSSEPlatform\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.openjsse.sun.security.ssl.SSLSocketFactoryImpl\nimport org.openjsse.sun.security.ssl.SSLSocketImpl\n\nclass OpenJSSETest {\n  @JvmField @RegisterExtension\n  var platform = PlatformRule()\n\n  @JvmField @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  var client = clientTestRule.newClient()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeOpenJSSE()\n  }\n\n  @Test\n  fun testTlsv13Works() {\n    enableTls()\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val request = Request(server.url(\"/\"))\n\n    val response = client.newCall(request).execute()\n\n    response.use {\n      assertEquals(200, response.code)\n      assertEquals(TlsVersion.TLS_1_3, response.handshake?.tlsVersion)\n      assertEquals(Protocol.HTTP_2, response.protocol)\n\n      assertThat(response.exchangeAccessor!!.connectionAccessor.socket())\n        .isInstanceOf<SSLSocketImpl>()\n    }\n  }\n\n  @Test\n  fun testSupportedProtocols() {\n    val factory = SSLSocketFactoryImpl()\n    val s = factory.createSocket() as SSLSocketImpl\n\n    assertEquals(listOf(\"TLSv1.3\", \"TLSv1.2\"), s.enabledProtocols.toList())\n  }\n\n  @Test\n  @Disabled\n  fun testMozilla() {\n    assumeNetwork()\n\n    val request = Request.Builder().url(\"https://mozilla.org/robots.txt\").build()\n\n    client.newCall(request).execute().use {\n      assertThat(it.protocol).isEqualTo(Protocol.HTTP_2)\n      assertThat(it.handshake!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3)\n    }\n  }\n\n  @Test\n  fun testBuildIfSupported() {\n    val actual = OpenJSSEPlatform.buildIfSupported()\n    assertThat(actual).isNotNull()\n  }\n\n  private fun enableTls() {\n    // Generate a self-signed cert for the server to serve and the client to trust.\n    // can't use TlsUtil.localhost with a non OpenJSSE trust manager\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"localhost\")\n        .addSubjectAlternativeName(\"localhost\")\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(heldCertificate)\n        .addTrustedCertificate(heldCertificate.certificate)\n        .build()\n\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ProtocolTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport okhttp3.Protocol.Companion.get\nimport org.junit.jupiter.api.Assertions.assertThrows\nimport org.junit.jupiter.api.Test\n\nclass ProtocolTest {\n  @Test\n  fun testGetKnown() {\n    assertThat(get(\"http/1.0\")).isEqualTo(Protocol.HTTP_1_0)\n    assertThat(get(\"http/1.1\")).isEqualTo(Protocol.HTTP_1_1)\n    assertThat(get(\"spdy/3.1\")).isEqualTo(Protocol.SPDY_3)\n    assertThat(get(\"h2\")).isEqualTo(Protocol.HTTP_2)\n    assertThat(get(\"h2_prior_knowledge\")).isEqualTo(Protocol.H2_PRIOR_KNOWLEDGE)\n    assertThat(get(\"quic\")).isEqualTo(Protocol.QUIC)\n    assertThat(get(\"h3\")).isEqualTo(Protocol.HTTP_3)\n    assertThat(get(\"h3-29\")).isEqualTo(Protocol.HTTP_3)\n  }\n\n  @Test\n  fun testGetUnknown() {\n    assertThrows(IOException::class.java) { get(\"tcp\") }\n  }\n\n  @Test\n  fun testToString() {\n    assertThat(Protocol.HTTP_1_0.toString()).isEqualTo(\"http/1.0\")\n    assertThat(Protocol.HTTP_1_1.toString()).isEqualTo(\"http/1.1\")\n    assertThat(Protocol.SPDY_3.toString()).isEqualTo(\"spdy/3.1\")\n    assertThat(Protocol.HTTP_2.toString()).isEqualTo(\"h2\")\n    assertThat(Protocol.H2_PRIOR_KNOWLEDGE.toString())\n      .isEqualTo(\"h2_prior_knowledge\")\n    assertThat(Protocol.QUIC.toString()).isEqualTo(\"quic\")\n    assertThat(Protocol.HTTP_3.toString()).isEqualTo(\"h3\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/PublicInternalApiTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okhttp3.internal.http.HttpMethod.permitsRequestBody\nimport okhttp3.internal.http.HttpMethod.requiresRequestBody\nimport okhttp3.internal.http.hasBody\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\n\n@Suppress(\"DEPRECATION_ERROR\")\nclass PublicInternalApiTest {\n  @Test\n  fun permitsRequestBody() {\n    assertTrue(permitsRequestBody(\"POST\"))\n    assertFalse(permitsRequestBody(\"GET\"))\n  }\n\n  @Test\n  fun requiresRequestBody() {\n    assertTrue(requiresRequestBody(\"PUT\"))\n    assertFalse(requiresRequestBody(\"GET\"))\n  }\n\n  @Test\n  fun hasBody() {\n    val request = Request.Builder().url(\"http://example.com\").build()\n    val response =\n      Response\n        .Builder()\n        .code(200)\n        .message(\"OK\")\n        .request(request)\n        .protocol(Protocol.HTTP_2)\n        .build()\n    assertTrue(hasBody(response))\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/RecordedResponse.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isBetween\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.text.SimpleDateFormat\nimport java.util.Date\n\n/**\n * A received response or failure recorded by the response recorder.\n */\nclass RecordedResponse(\n  @JvmField val request: Request,\n  val response: Response?,\n  val webSocket: WebSocket?,\n  val body: String?,\n  val failure: IOException?,\n) {\n  fun assertRequestUrl(url: HttpUrl) =\n    apply {\n      assertThat(request.url).isEqualTo(url)\n    }\n\n  fun assertRequestMethod(method: String) =\n    apply {\n      assertThat(request.method).isEqualTo(method)\n    }\n\n  fun assertRequestHeader(\n    name: String,\n    vararg values: String,\n  ) = apply {\n    assertThat(request.headers(name)).containsExactly(*values)\n  }\n\n  fun assertCode(expectedCode: Int) =\n    apply {\n      assertThat(response!!.code).isEqualTo(expectedCode)\n    }\n\n  fun assertSuccessful() =\n    apply {\n      assertThat(failure).isNull()\n      assertThat(response!!.isSuccessful).isTrue()\n    }\n\n  fun assertNotSuccessful() =\n    apply {\n      assertThat(response!!.isSuccessful).isFalse()\n    }\n\n  fun assertHeader(\n    name: String,\n    vararg values: String?,\n  ) = apply {\n    assertThat(response!!.headers(name)).containsExactly(*values)\n  }\n\n  fun assertHeaders(headers: Headers) =\n    apply {\n      assertThat(response!!.headers).isEqualTo(headers)\n    }\n\n  fun assertBody(expectedBody: String) =\n    apply {\n      assertThat(body).isEqualTo(expectedBody)\n    }\n\n  fun assertHandshake() =\n    apply {\n      val handshake = response!!.handshake!!\n      assertThat(handshake.tlsVersion).isNotNull()\n      assertThat(handshake.cipherSuite).isNotNull()\n      assertThat(handshake.peerPrincipal).isNotNull()\n      assertThat(handshake.peerCertificates.size).isEqualTo(1)\n      assertThat(handshake.localPrincipal).isNull()\n      assertThat(handshake.localCertificates.size).isEqualTo(0)\n    }\n\n  /**\n   * Asserts that the current response was redirected and returns the prior response.\n   */\n  fun priorResponse(): RecordedResponse {\n    val priorResponse = response!!.priorResponse!!\n    return RecordedResponse(priorResponse.request, priorResponse, null, null, null)\n  }\n\n  /**\n   * Asserts that the current response used the network and returns the network response.\n   */\n  fun networkResponse(): RecordedResponse {\n    val networkResponse = response!!.networkResponse!!\n    return RecordedResponse(networkResponse.request, networkResponse, null, null, null)\n  }\n\n  /** Asserts that the current response didn't use the network.  */\n  fun assertNoNetworkResponse() =\n    apply {\n      assertThat(response!!.networkResponse).isNull()\n    }\n\n  /** Asserts that the current response didn't use the cache.  */\n  fun assertNoCacheResponse() =\n    apply {\n      assertThat(response!!.cacheResponse).isNull()\n    }\n\n  /**\n   * Asserts that the current response used the cache and returns the cache response.\n   */\n  fun cacheResponse(): RecordedResponse {\n    val cacheResponse = response!!.cacheResponse!!\n    return RecordedResponse(cacheResponse.request, cacheResponse, null, null, null)\n  }\n\n  fun assertFailure(vararg allowedExceptionTypes: Class<*>) =\n    apply {\n      var found = false\n      for (expectedClass in allowedExceptionTypes) {\n        if (expectedClass.isInstance(failure)) {\n          found = true\n          break\n        }\n      }\n      assertThat(\n        found,\n        \"Expected exception type among ${allowedExceptionTypes.contentToString()}, got $failure\",\n      ).isTrue()\n    }\n\n  fun assertFailure(vararg messages: String) =\n    apply {\n      assertThat(failure, \"No failure found\").isNotNull()\n      assertThat(messages).contains(failure!!.message)\n    }\n\n  fun assertFailureMatches(vararg patterns: String) =\n    apply {\n      val message = failure!!.message!!\n      assertThat(\n        patterns.firstOrNull { pattern ->\n          message.matches(pattern.toRegex())\n        },\n      ).isNotNull()\n    }\n\n  fun assertSentRequestAtMillis(\n    minimum: Long,\n    maximum: Long,\n  ) = apply {\n    assertDateInRange(minimum, response!!.sentRequestAtMillis, maximum)\n  }\n\n  fun assertReceivedResponseAtMillis(\n    minimum: Long,\n    maximum: Long,\n  ) = apply {\n    assertDateInRange(minimum, response!!.receivedResponseAtMillis, maximum)\n  }\n\n  private fun assertDateInRange(\n    minimum: Long,\n    actual: Long,\n    maximum: Long,\n  ) {\n    assertThat(actual, \"${format(minimum)} <= ${format(actual)} <= ${format(maximum)}\")\n      .isBetween(minimum, maximum)\n  }\n\n  private fun format(time: Long) = SimpleDateFormat(\"HH:mm:ss.SSS\").format(Date(time))\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/RecordingCallback.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.util.concurrent.TimeUnit\n\n/**\n * Records received HTTP responses so they can be later retrieved by tests.\n */\nclass RecordingCallback : Callback {\n  private val responses = mutableListOf<RecordedResponse>()\n\n  @Synchronized\n  override fun onFailure(\n    call: Call,\n    e: IOException,\n  ) {\n    responses.add(RecordedResponse(call.request(), null, null, null, e))\n    (this as Object).notifyAll()\n  }\n\n  @Synchronized\n  override fun onResponse(\n    call: Call,\n    response: Response,\n  ) {\n    val body = response.body.string()\n    responses.add(RecordedResponse(call.request(), response, null, body, null))\n    (this as Object).notifyAll()\n  }\n\n  /**\n   * Returns the recorded response triggered by `request`. Throws if the response isn't\n   * enqueued before the timeout.\n   */\n  @Synchronized\n  fun await(url: HttpUrl): RecordedResponse {\n    val timeoutMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) + TIMEOUT_MILLIS\n    while (true) {\n      val i = responses.iterator()\n      while (i.hasNext()) {\n        val recordedResponse = i.next()\n        if (recordedResponse.request.url.equals(url)) {\n          i.remove()\n          return recordedResponse\n        }\n      }\n      val nowMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime())\n      if (nowMillis >= timeoutMillis) break\n      (this as Object).wait(timeoutMillis - nowMillis)\n    }\n\n    throw AssertionError(\"Timed out waiting for response to $url\")\n  }\n\n  companion object {\n    val TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/RecordingExecutor.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport java.util.concurrent.AbstractExecutorService\nimport java.util.concurrent.RejectedExecutionException\nimport java.util.concurrent.TimeUnit\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.finishedAccessor\n\n/**\n * A fake executor for testing that never executes anything! Instead, it just keeps track of what's\n * been enqueued.\n */\ninternal class RecordingExecutor(\n  private val dispatcherTest: DispatcherTest,\n) : AbstractExecutorService() {\n  private var shutdown: Boolean = false\n  private val calls = mutableListOf<RealCall.AsyncCall>()\n\n  override fun execute(command: Runnable) {\n    if (shutdown) throw RejectedExecutionException()\n    calls.add(command as RealCall.AsyncCall)\n  }\n\n  fun assertJobs(vararg expectedUrls: String) {\n    val actualUrls = calls.map { it.request.url.toString() }\n    assertThat(actualUrls).containsExactly(*expectedUrls)\n  }\n\n  fun finishJob(url: String) {\n    val i = calls.iterator()\n    while (i.hasNext()) {\n      val call = i.next()\n      if (call.request.url.toString() == url) {\n        i.remove()\n        dispatcherTest.dispatcher.finishedAccessor(call)\n        return\n      }\n    }\n    throw AssertionError(\"No such job: $url\")\n  }\n\n  override fun shutdown() {\n    shutdown = true\n  }\n\n  override fun shutdownNow(): List<Runnable> = throw UnsupportedOperationException()\n\n  override fun isShutdown(): Boolean = shutdown\n\n  override fun isTerminated(): Boolean = throw UnsupportedOperationException()\n\n  override fun awaitTermination(\n    timeout: Long,\n    unit: TimeUnit,\n  ): Boolean = throw UnsupportedOperationException()\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/RequestBodyTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport java.io.FileDescriptor\nimport java.io.FileInputStream\nimport java.io.IOException\nimport java.nio.file.Path\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.RequestBody.Companion.asRequestBody\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okio.Buffer\nimport okio.FileSystem\nimport okio.Path.Companion.toOkioPath\nimport org.junit.jupiter.api.Assertions.assertThrows\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.io.TempDir\n\nclass RequestBodyTest {\n  private lateinit var filePath: okio.Path\n\n  @BeforeEach\n  fun setup(\n    @TempDir tempDir: Path,\n  ) {\n    filePath = tempDir.toOkioPath() / \"file.txt\"\n  }\n\n  @Test\n  fun testFileDescriptor() {\n    assertOnFileDescriptor { fd ->\n      val requestBody = fd.toRequestBody()\n\n      assertThat(requestBody.contentLength()).isEqualTo(-1L)\n      assertThat(requestBody.isOneShot()).isEqualTo(true)\n    }\n  }\n\n  @Test\n  fun testFileDescriptorRead() {\n    assertOnFileDescriptor(content = \"Hello\") { fd ->\n      val requestBody = fd.toRequestBody()\n\n      val buffer = Buffer()\n      requestBody.writeTo(buffer)\n      assertThat(buffer.readUtf8()).isEqualTo(\"Hello\")\n    }\n  }\n\n  @Test\n  fun testFileDescriptorDefaultMediaType() {\n    assertOnFileDescriptor { fd ->\n      val requestBody = fd.toRequestBody()\n\n      assertThat(requestBody.contentType()).isNull()\n    }\n  }\n\n  @Test\n  fun testFileDescriptorMediaType() {\n    assertOnFileDescriptor { fd ->\n      val contentType = \"text/plain\".toMediaType()\n\n      val requestBody = fd.toRequestBody(contentType)\n\n      assertThat(requestBody.contentType()).isEqualTo(contentType)\n    }\n  }\n\n  @Test\n  fun testFileDescriptorReadTwice() {\n    assertOnFileDescriptor(content = \"Hello\") { fd ->\n      val requestBody = fd.toRequestBody()\n\n      val buffer = Buffer()\n      requestBody.writeTo(buffer)\n      assertThat(buffer.readUtf8()).isEqualTo(\"Hello\")\n\n      assertThrows(IOException::class.java) {\n        requestBody.writeTo(Buffer())\n      }\n    }\n  }\n\n  @Test\n  fun testFileDescriptorAfterClose() {\n    val closedRequestBody = assertOnFileDescriptor { it.toRequestBody() }\n\n    assertThrows(IOException::class.java) {\n      closedRequestBody.writeTo(Buffer())\n    }\n  }\n\n  @Test\n  fun testPathRead() {\n    assertOnPath(content = \"Hello\") { path ->\n      val requestBody = path.asRequestBody(FileSystem.SYSTEM)\n\n      val buffer = Buffer()\n      requestBody.writeTo(buffer)\n      assertThat(buffer.readUtf8()).isEqualTo(\"Hello\")\n    }\n  }\n\n  @Test\n  fun testSha256() {\n    val hash = \"Hello\".toRequestBody().sha256().hex()\n    assertThat(hash).isEqualTo(\"185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969\")\n  }\n\n  private inline fun <T> assertOnFileDescriptor(\n    content: String? = null,\n    fn: (FileDescriptor) -> T,\n  ): T =\n    assertOnPath(content) {\n      FileInputStream(filePath.toFile()).use { fis ->\n        fn(fis.fd)\n      }\n    }\n\n  private inline fun <T> assertOnPath(\n    content: String? = null,\n    fn: (okio.Path) -> T,\n  ): T {\n    FileSystem.SYSTEM.write(filePath) {\n      if (content != null) {\n        writeUtf8(content)\n      }\n    }\n\n    return fn(filePath)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/RequestCommonTest.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.isSameInstanceAs\nimport assertk.assertions.isTrue\nimport kotlin.test.Test\nimport kotlin.test.assertFailsWith\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.internal.EmptyTags\n\nclass RequestCommonTest {\n  @Test\n  fun constructorNormal() {\n    val url = \"https://example.com/\".toHttpUrl()\n    val body = \"hello\".toRequestBody()\n    val headers = headersOf(\"User-Agent\", \"RequestTest\")\n    val method = \"PUT\"\n    val request =\n      Request(\n        url = url,\n        headers = headers,\n        method = method,\n        body = body,\n      )\n    assertThat(request.url).isEqualTo(url)\n    assertThat(request.headers).isEqualTo(headers)\n    assertThat(request.method).isEqualTo(method)\n    assertThat(request.body).isEqualTo(body)\n    assertThat(request.tags).isEqualTo(EmptyTags)\n  }\n\n  @Test\n  fun constructorNoBodyNoMethod() {\n    val url = \"https://example.com/\".toHttpUrl()\n    val headers = headersOf(\"User-Agent\", \"RequestTest\")\n    val request =\n      Request(\n        url = url,\n        headers = headers,\n      )\n    assertThat(request.url).isEqualTo(url)\n    assertThat(request.headers).isEqualTo(headers)\n    assertThat(request.method).isEqualTo(\"GET\")\n    assertThat(request.body).isNull()\n    assertThat(request.tags).isEqualTo(EmptyTags)\n  }\n\n  @Test\n  fun constructorNoMethod() {\n    val url = \"https://example.com/\".toHttpUrl()\n    val body = \"hello\".toRequestBody()\n    val headers = headersOf(\"User-Agent\", \"RequestTest\")\n    val request =\n      Request(\n        url = url,\n        headers = headers,\n        body = body,\n      )\n    assertThat(request.url).isEqualTo(url)\n    assertThat(request.headers).isEqualTo(headers)\n    assertThat(request.method).isEqualTo(\"POST\")\n    assertThat(request.body).isEqualTo(body)\n    assertThat(request.tags).isEqualTo(EmptyTags)\n  }\n\n  @Test\n  fun constructorNoBody() {\n    val url = \"https://example.com/\".toHttpUrl()\n    val headers = headersOf(\"User-Agent\", \"RequestTest\")\n    val method = \"DELETE\"\n    val request =\n      Request(\n        url = url,\n        headers = headers,\n        method = method,\n      )\n    assertThat(request.url).isEqualTo(url)\n    assertThat(request.headers).isEqualTo(headers)\n    assertThat(request.method).isEqualTo(method)\n    assertThat(request.body).isNull()\n    assertThat(request.tags).isEqualTo(EmptyTags)\n  }\n\n  @Test\n  fun newBuilderUrlResetsUrl() {\n    val requestWithoutCache = Request.Builder().url(\"http://localhost/api\").build()\n    val builtRequestWithoutCache = requestWithoutCache.newBuilder().url(\"http://localhost/api/foo\").build()\n    assertThat(builtRequestWithoutCache.url).isEqualTo(\n      \"http://localhost/api/foo\".toHttpUrl(),\n    )\n    val requestWithCache =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .build()\n    // cache url object\n    requestWithCache.url\n    val builtRequestWithCache =\n      requestWithCache\n        .newBuilder()\n        .url(\"http://localhost/api/foo\")\n        .build()\n    assertThat(builtRequestWithCache.url)\n      .isEqualTo(\"http://localhost/api/foo\".toHttpUrl())\n  }\n\n  @Test\n  fun cacheControl() {\n    val request =\n      Request\n        .Builder()\n        .cacheControl(CacheControl.Builder().noCache().build())\n        .url(\"https://square.com\")\n        .build()\n    assertThat(request.headers(\"Cache-Control\")).containsExactly(\"no-cache\")\n    assertThat(request.cacheControl.noCache).isTrue()\n  }\n\n  @Test\n  fun emptyCacheControlClearsAllCacheControlHeaders() {\n    val request =\n      Request\n        .Builder()\n        .header(\"Cache-Control\", \"foo\")\n        .cacheControl(CacheControl.Builder().build())\n        .url(\"https://square.com\")\n        .build()\n    assertThat(request.headers(\"Cache-Control\")).isEmpty()\n  }\n\n  @Test\n  fun headerAcceptsPermittedCharacters() {\n    val builder = Request.Builder()\n    builder.header(\"AZab09~\", \"AZab09 ~\")\n    builder.addHeader(\"AZab09~\", \"AZab09 ~\")\n  }\n\n  @Test\n  fun emptyNameForbidden() {\n    val builder = Request.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.header(\"\", \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      builder.addHeader(\"\", \"Value\")\n    }\n  }\n\n  @Test\n  fun headerAllowsTabOnlyInValues() {\n    val builder = Request.Builder()\n    builder.header(\"key\", \"sample\\tvalue\")\n    assertFailsWith<IllegalArgumentException> {\n      builder.header(\"sample\\tkey\", \"value\")\n    }\n  }\n\n  @Test\n  fun headerForbidsControlCharacters() {\n    assertForbiddenHeader(\"\\u0000\")\n    assertForbiddenHeader(\"\\r\")\n    assertForbiddenHeader(\"\\n\")\n    assertForbiddenHeader(\"\\u001f\")\n    assertForbiddenHeader(\"\\u007f\")\n    assertForbiddenHeader(\"\\u0080\")\n    assertForbiddenHeader(\"\\ud83c\\udf69\")\n  }\n\n  private fun assertForbiddenHeader(s: String) {\n    val builder = Request.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.header(s, \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      builder.addHeader(s, \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      builder.header(\"Name\", s)\n    }\n    assertFailsWith<IllegalArgumentException> {\n      builder.addHeader(\"Name\", s)\n    }\n  }\n\n  @Test\n  fun noTag() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .build()\n    assertThat(request.tag<Any>()).isNull()\n    assertThat(request.tag(Any::class)).isNull()\n    assertThat(request.tag(String::class)).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag<String>()).isNull()\n    assertThat(request.tag(String::class)).isNull()\n  }\n\n  @Test\n  fun defaultTag() {\n    val tag = \"1234\"\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(tag as Any)\n        .build()\n    assertThat(request.tag<Any>()).isSameInstanceAs(tag)\n    assertThat(request.tag(Any::class)).isSameInstanceAs(tag)\n    assertThat(request.tag(String::class)).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag<Any>()).isSameInstanceAs(tag)\n    assertThat(request.tag(Any::class)).isSameInstanceAs(tag)\n  }\n\n  @Test\n  fun nullRemovesTag() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(\"a\" as Any)\n        .tag(null)\n        .build()\n    assertThat(request.tag<Any>()).isNull()\n  }\n\n  @Test\n  fun removeAbsentTag() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(null)\n        .build()\n    assertThat(request.tag<String>()).isNull()\n  }\n\n  @Test\n  fun objectTag() {\n    val tag = \"1234\"\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(Any::class, tag)\n        .build()\n    assertThat(request.tag<Any>()).isSameInstanceAs(tag)\n    assertThat(request.tag(Any::class)).isSameInstanceAs(tag)\n    assertThat(request.tag(String::class)).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag(Any::class)).isSameInstanceAs(tag)\n    assertThat(request.tag<Any>()).isSameInstanceAs(tag)\n  }\n\n  @Test\n  fun kotlinReifiedTag() {\n    val uuidTag = \"1234\"\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag<String>(uuidTag) // Use the type parameter.\n        .build()\n    assertThat(request.tag<String>()).isSameInstanceAs(\"1234\")\n    assertThat(request.tag<Any>()).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag(String::class)).isSameInstanceAs(uuidTag)\n  }\n\n  @Test\n  fun kotlinClassTag() {\n    val uuidTag = \"1234\"\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(String::class, uuidTag) // Use the KClass<*> parameter.\n        .build()\n    assertThat(request.tag(Any::class)).isNull()\n    assertThat(request.tag(String::class)).isSameInstanceAs(\"1234\")\n\n    // Alternate access APIs also work.\n    assertThat(request.tag(String::class)).isSameInstanceAs(uuidTag)\n    assertThat(request.tag<String>()).isSameInstanceAs(uuidTag)\n  }\n\n  @Test\n  fun replaceOnlyTag() {\n    val uuidTag1 = \"1234\"\n    val uuidTag2 = \"4321\"\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(String::class, uuidTag1)\n        .tag(String::class, uuidTag2)\n        .build()\n    assertThat(request.tag(String::class)).isSameInstanceAs(uuidTag2)\n  }\n\n  @Test\n  fun multipleTags() {\n    val stringTag = \"dilophosaurus\"\n    val longTag = 20170815L as Long?\n    val objectTag = Any()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(Any::class, objectTag)\n        .tag(String::class, stringTag)\n        .tag(Long::class, longTag)\n        .build()\n    assertThat(request.tag<Any>()).isSameInstanceAs(objectTag)\n    assertThat(request.tag(Any::class)).isSameInstanceAs(objectTag)\n    assertThat(request.tag(String::class)).isSameInstanceAs(stringTag)\n\n    // TODO check for Jvm or handle Long/long correctly\n//    assertThat(request.tag(Long::class)).isSameAs(longTag)\n  }\n\n  /** Confirm that we don't accidentally share the backing map between objects. */\n  @Test\n  fun tagsAreImmutable() {\n    val builder =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n    val requestA = builder.tag(String::class, \"a\").build()\n    val requestB = builder.tag(String::class, \"b\").build()\n    val requestC = requestA.newBuilder().tag(String::class, \"c\").build()\n    assertThat(requestA.tag(String::class)).isSameInstanceAs(\"a\")\n    assertThat(requestB.tag(String::class)).isSameInstanceAs(\"b\")\n    assertThat(requestC.tag(String::class)).isSameInstanceAs(\"c\")\n  }\n\n  @Test\n  fun requestToStringRedactsSensitiveHeaders() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"content-length\", \"99\")\n        .add(\"authorization\", \"peanutbutter\")\n        .add(\"proxy-authorization\", \"chocolate\")\n        .add(\"cookie\", \"drink=coffee\")\n        .add(\"set-cookie\", \"accessory=sugar\")\n        .add(\"user-agent\", \"OkHttp\")\n        .build()\n    val request =\n      Request(\n        \"https://square.com\".toHttpUrl(),\n        headers,\n      )\n    assertThat(request.toString()).isEqualTo(\n      \"Request{method=GET, url=https://square.com/, headers=[\" +\n        \"content-length:99,\" +\n        \" authorization:██,\" +\n        \" proxy-authorization:██,\" +\n        \" cookie:██,\" +\n        \" set-cookie:██,\" +\n        \" user-agent:OkHttp\" +\n        \"]}\",\n    )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/RequestTest.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.isSameInstanceAs\nimport assertk.assertions.isTrue\nimport java.io.File\nimport java.io.FileWriter\nimport java.net.URI\nimport java.util.UUID\nimport kotlin.test.assertFailsWith\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.RequestBody.Companion.asRequestBody\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.internal.EmptyTags\nimport okio.Buffer\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.GzipSource\nimport okio.buffer\nimport okio.use\nimport org.junit.jupiter.api.Test\n\nclass RequestTest {\n  @Test\n  fun constructor() {\n    val url = \"https://example.com/\".toHttpUrl()\n    val body = \"hello\".toRequestBody()\n    val headers = headersOf(\"User-Agent\", \"RequestTest\")\n    val method = \"PUT\"\n    val request =\n      Request(\n        url = url,\n        headers = headers,\n        method = method,\n        body = body,\n      )\n    assertThat(request.url).isEqualTo(url)\n    assertThat(request.headers).isEqualTo(headers)\n    assertThat(request.method).isEqualTo(method)\n    assertThat(request.body).isEqualTo(body)\n    assertThat(request.tags).isEqualTo(EmptyTags)\n  }\n\n  @Test\n  fun constructorNoBodyNoMethod() {\n    val url = \"https://example.com/\".toHttpUrl()\n    val headers = headersOf(\"User-Agent\", \"RequestTest\")\n    val request =\n      Request(\n        url = url,\n        headers = headers,\n      )\n    assertThat(request.url).isEqualTo(url)\n    assertThat(request.headers).isEqualTo(headers)\n    assertThat(request.method).isEqualTo(\"GET\")\n    assertThat(request.body).isNull()\n    assertThat(request.tags).isEqualTo(EmptyTags)\n  }\n\n  @Test\n  fun constructorNoMethod() {\n    val url = \"https://example.com/\".toHttpUrl()\n    val body = \"hello\".toRequestBody()\n    val headers = headersOf(\"User-Agent\", \"RequestTest\")\n    val request =\n      Request(\n        url = url,\n        headers = headers,\n        body = body,\n      )\n    assertThat(request.url).isEqualTo(url)\n    assertThat(request.headers).isEqualTo(headers)\n    assertThat(request.method).isEqualTo(\"POST\")\n    assertThat(request.body).isEqualTo(body)\n    assertThat(request.tags).isEqualTo(EmptyTags)\n  }\n\n  @Test\n  fun constructorNoBody() {\n    val url = \"https://example.com/\".toHttpUrl()\n    val headers = headersOf(\"User-Agent\", \"RequestTest\")\n    val method = \"DELETE\"\n    val request =\n      Request(\n        url = url,\n        headers = headers,\n        method = method,\n      )\n    assertThat(request.url).isEqualTo(url)\n    assertThat(request.headers).isEqualTo(headers)\n    assertThat(request.method).isEqualTo(method)\n    assertThat(request.body).isNull()\n    assertThat(request.tags).isEqualTo(EmptyTags)\n  }\n\n  @Test\n  fun string() {\n    val contentType = \"text/plain; charset=utf-8\".toMediaType()\n    val body = \"abc\".toByteArray().toRequestBody(contentType)\n    assertThat(body.contentType()).isEqualTo(contentType)\n    assertThat(body.contentLength()).isEqualTo(3)\n    assertThat(bodyToHex(body)).isEqualTo(\"616263\")\n    assertThat(bodyToHex(body), \"Retransmit body\").isEqualTo(\"616263\")\n  }\n\n  @Test\n  fun stringWithDefaultCharsetAdded() {\n    val contentType = \"text/plain\".toMediaType()\n    val body = \"\\u0800\".toRequestBody(contentType)\n    assertThat(body.contentType()).isEqualTo(\"text/plain; charset=utf-8\".toMediaType())\n    assertThat(body.contentLength()).isEqualTo(3)\n    assertThat(bodyToHex(body)).isEqualTo(\"e0a080\")\n  }\n\n  @Test\n  fun stringWithNonDefaultCharsetSpecified() {\n    val contentType = \"text/plain; charset=utf-16be\".toMediaType()\n    val body = \"\\u0800\".toRequestBody(contentType)\n    assertThat(body.contentType()).isEqualTo(contentType)\n    assertThat(body.contentLength()).isEqualTo(2)\n    assertThat(bodyToHex(body)).isEqualTo(\"0800\")\n  }\n\n  @Test\n  fun byteArray() {\n    val contentType = \"text/plain\".toMediaType()\n    val body: RequestBody = \"abc\".toByteArray().toRequestBody(contentType)\n    assertThat(body.contentType()).isEqualTo(contentType)\n    assertThat(body.contentLength()).isEqualTo(3)\n    assertThat(bodyToHex(body)).isEqualTo(\"616263\")\n    assertThat(bodyToHex(body), \"Retransmit body\").isEqualTo(\"616263\")\n  }\n\n  @Test\n  fun byteArrayRange() {\n    val contentType = \"text/plain\".toMediaType()\n    val body: RequestBody = \".abcd\".toByteArray().toRequestBody(contentType, 1, 3)\n    assertThat(body.contentType()).isEqualTo(contentType)\n    assertThat(body.contentLength()).isEqualTo(3)\n    assertThat(bodyToHex(body)).isEqualTo(\"616263\")\n    assertThat(bodyToHex(body), \"Retransmit body\").isEqualTo(\"616263\")\n  }\n\n  @Test\n  fun byteString() {\n    val contentType = \"text/plain\".toMediaType()\n    val body: RequestBody = \"Hello\".encodeUtf8().toRequestBody(contentType)\n    assertThat(body.contentType()).isEqualTo(contentType)\n    assertThat(body.contentLength()).isEqualTo(5)\n    assertThat(bodyToHex(body)).isEqualTo(\"48656c6c6f\")\n    assertThat(bodyToHex(body), \"Retransmit body\").isEqualTo(\"48656c6c6f\")\n  }\n\n  @Test\n  fun file() {\n    val file = File.createTempFile(\"RequestTest\", \"tmp\")\n    val writer = FileWriter(file)\n    writer.write(\"abc\")\n    writer.close()\n    val contentType = \"text/plain\".toMediaType()\n    val body: RequestBody = file.asRequestBody(contentType)\n    assertThat(body.contentType()).isEqualTo(contentType)\n    assertThat(body.contentLength()).isEqualTo(3)\n    assertThat(bodyToHex(body)).isEqualTo(\"616263\")\n    assertThat(bodyToHex(body), \"Retransmit body\").isEqualTo(\"616263\")\n  }\n\n  /** Common verbs used for apis such as GitHub, AWS, and Google Cloud.  */\n  @Test\n  fun crudVerbs() {\n    val contentType = \"application/json\".toMediaType()\n    val body = \"{}\".toRequestBody(contentType)\n\n    val get =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .get()\n        .build()\n    assertThat(get.method).isEqualTo(\"GET\")\n    assertThat(get.body).isNull()\n\n    val head =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .head()\n        .build()\n    assertThat(head.method).isEqualTo(\"HEAD\")\n    assertThat(head.body).isNull()\n\n    val delete =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .delete()\n        .build()\n    assertThat(delete.method).isEqualTo(\"DELETE\")\n    assertThat(delete.body!!.contentLength()).isEqualTo(0L)\n\n    val post =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .post(body)\n        .build()\n    assertThat(post.method).isEqualTo(\"POST\")\n    assertThat(post.body).isEqualTo(body)\n\n    val put =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .put(body)\n        .build()\n    assertThat(put.method).isEqualTo(\"PUT\")\n    assertThat(put.body).isEqualTo(body)\n\n    val patch =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .patch(body)\n        .build()\n    assertThat(patch.method).isEqualTo(\"PATCH\")\n    assertThat(patch.body).isEqualTo(body)\n\n    val query =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .query(body)\n        .build()\n    assertThat(query.method).isEqualTo(\"QUERY\")\n    assertThat(query.body).isEqualTo(body)\n  }\n\n  @Test\n  fun uninitializedURI() {\n    val request = Request.Builder().url(\"http://localhost/api\").build()\n    assertThat(request.url.toUri()).isEqualTo(URI(\"http://localhost/api\"))\n    assertThat(request.url).isEqualTo(\"http://localhost/api\".toHttpUrl())\n  }\n\n  @Test\n  fun newBuilderUrlResetsUrl() {\n    val requestWithoutCache = Request.Builder().url(\"http://localhost/api\").build()\n    val builtRequestWithoutCache = requestWithoutCache.newBuilder().url(\"http://localhost/api/foo\").build()\n    assertThat(builtRequestWithoutCache.url).isEqualTo(\n      \"http://localhost/api/foo\".toHttpUrl(),\n    )\n    val requestWithCache =\n      Request\n        .Builder()\n        .url(\"http://localhost/api\")\n        .build()\n    // cache url object\n    requestWithCache.url\n    val builtRequestWithCache =\n      requestWithCache\n        .newBuilder()\n        .url(\"http://localhost/api/foo\")\n        .build()\n    assertThat(builtRequestWithCache.url)\n      .isEqualTo(\"http://localhost/api/foo\".toHttpUrl())\n  }\n\n  @Test\n  fun cacheControl() {\n    val request =\n      Request\n        .Builder()\n        .cacheControl(CacheControl.Builder().noCache().build())\n        .url(\"https://square.com\")\n        .build()\n    assertThat(request.headers(\"Cache-Control\")).containsExactly(\"no-cache\")\n    assertThat(request.cacheControl.noCache).isTrue()\n  }\n\n  @Test\n  fun emptyCacheControlClearsAllCacheControlHeaders() {\n    val request =\n      Request\n        .Builder()\n        .header(\"Cache-Control\", \"foo\")\n        .cacheControl(CacheControl.Builder().build())\n        .url(\"https://square.com\")\n        .build()\n    assertThat(request.headers(\"Cache-Control\")).isEmpty()\n  }\n\n  @Test\n  fun headerAcceptsPermittedCharacters() {\n    val builder = Request.Builder()\n    builder.header(\"AZab09~\", \"AZab09 ~\")\n    builder.addHeader(\"AZab09~\", \"AZab09 ~\")\n  }\n\n  @Test\n  fun emptyNameForbidden() {\n    val builder = Request.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.header(\"\", \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      builder.addHeader(\"\", \"Value\")\n    }\n  }\n\n  @Test\n  fun headerAllowsTabOnlyInValues() {\n    val builder = Request.Builder()\n    builder.header(\"key\", \"sample\\tvalue\")\n    assertFailsWith<IllegalArgumentException> {\n      builder.header(\"sample\\tkey\", \"value\")\n    }\n  }\n\n  @Test\n  fun headerForbidsControlCharacters() {\n    assertForbiddenHeader(\"\\u0000\")\n    assertForbiddenHeader(\"\\r\")\n    assertForbiddenHeader(\"\\n\")\n    assertForbiddenHeader(\"\\u001f\")\n    assertForbiddenHeader(\"\\u007f\")\n    assertForbiddenHeader(\"\\u0080\")\n    assertForbiddenHeader(\"\\ud83c\\udf69\")\n  }\n\n  private fun assertForbiddenHeader(s: String) {\n    val builder = Request.Builder()\n    assertFailsWith<IllegalArgumentException> {\n      builder.header(s, \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      builder.addHeader(s, \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      builder.header(\"Name\", s)\n    }\n    assertFailsWith<IllegalArgumentException> {\n      builder.addHeader(\"Name\", s)\n    }\n  }\n\n  @Test\n  fun noTag() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .build()\n    assertThat(request.tag()).isNull()\n    assertThat(request.tag(Any::class.java)).isNull()\n    assertThat(request.tag(UUID::class.java)).isNull()\n    assertThat(request.tag(String::class.java)).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag<String>()).isNull()\n    assertThat(request.tag(String::class)).isNull()\n  }\n\n  @Test\n  fun defaultTag() {\n    val tag = UUID.randomUUID()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(tag)\n        .build()\n    assertThat(request.tag()).isSameInstanceAs(tag)\n    assertThat(request.tag(Any::class.java)).isSameInstanceAs(tag)\n    assertThat(request.tag(UUID::class.java)).isNull()\n    assertThat(request.tag(String::class.java)).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag<Any>()).isSameInstanceAs(tag)\n    assertThat(request.tag(Any::class)).isSameInstanceAs(tag)\n  }\n\n  @Test\n  fun nullRemovesTag() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(\"a\")\n        .tag(null)\n        .build()\n    assertThat(request.tag()).isNull()\n  }\n\n  @Test\n  fun removeAbsentTag() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(null)\n        .build()\n    assertThat(request.tag()).isNull()\n  }\n\n  @Test\n  fun objectTag() {\n    val tag = UUID.randomUUID()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(Any::class.java, tag)\n        .build()\n    assertThat(request.tag()).isSameInstanceAs(tag)\n    assertThat(request.tag(Any::class.java)).isSameInstanceAs(tag)\n    assertThat(request.tag(UUID::class.java)).isNull()\n    assertThat(request.tag(String::class.java)).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag(Any::class)).isSameInstanceAs(tag)\n    assertThat(request.tag<Any>()).isSameInstanceAs(tag)\n  }\n\n  @Test\n  fun javaClassTag() {\n    val uuidTag = UUID.randomUUID()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(UUID::class.java, uuidTag) // Use the Class<*> parameter.\n        .build()\n    assertThat(request.tag()).isNull()\n    assertThat(request.tag(Any::class.java)).isNull()\n    assertThat(request.tag(UUID::class.java)).isSameInstanceAs(uuidTag)\n    assertThat(request.tag(String::class.java)).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag(UUID::class)).isSameInstanceAs(uuidTag)\n    assertThat(request.tag<UUID>()).isSameInstanceAs(uuidTag)\n  }\n\n  @Test\n  fun kotlinReifiedTag() {\n    val uuidTag = UUID.randomUUID()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag<UUID>(uuidTag) // Use the type parameter.\n        .build()\n    assertThat(request.tag()).isNull()\n    assertThat(request.tag<Any>()).isNull()\n    assertThat(request.tag<UUID>()).isSameInstanceAs(uuidTag)\n    assertThat(request.tag<String>()).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag(UUID::class.java)).isSameInstanceAs(uuidTag)\n    assertThat(request.tag(UUID::class)).isSameInstanceAs(uuidTag)\n  }\n\n  @Test\n  fun kotlinClassTag() {\n    val uuidTag = UUID.randomUUID()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(UUID::class, uuidTag) // Use the KClass<*> parameter.\n        .build()\n    assertThat(request.tag()).isNull()\n    assertThat(request.tag(Any::class)).isNull()\n    assertThat(request.tag(UUID::class)).isSameInstanceAs(uuidTag)\n    assertThat(request.tag(String::class)).isNull()\n\n    // Alternate access APIs also work.\n    assertThat(request.tag(UUID::class.java)).isSameInstanceAs(uuidTag)\n    assertThat(request.tag<UUID>()).isSameInstanceAs(uuidTag)\n  }\n\n  @Test\n  fun replaceOnlyTag() {\n    val uuidTag1 = UUID.randomUUID()\n    val uuidTag2 = UUID.randomUUID()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(UUID::class.java, uuidTag1)\n        .tag(UUID::class.java, uuidTag2)\n        .build()\n    assertThat(request.tag(UUID::class.java)).isSameInstanceAs(uuidTag2)\n  }\n\n  @Test\n  fun multipleTags() {\n    val uuidTag = UUID.randomUUID()\n    val stringTag = \"dilophosaurus\"\n    val longTag = 20170815L as Long?\n    val objectTag = Any()\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n        .tag(Any::class.java, objectTag)\n        .tag(UUID::class.java, uuidTag)\n        .tag(String::class.java, stringTag)\n        .tag(Long::class.javaObjectType, longTag)\n        .build()\n    assertThat(request.tag()).isSameInstanceAs(objectTag)\n    assertThat(request.tag(Any::class.java)).isSameInstanceAs(objectTag)\n    assertThat(request.tag(UUID::class.java)).isSameInstanceAs(uuidTag)\n    assertThat(request.tag(String::class.java)).isSameInstanceAs(stringTag)\n    assertThat(request.tag(Long::class.javaObjectType)).isSameInstanceAs(longTag)\n  }\n\n  /** Confirm that we don't accidentally share the backing map between objects. */\n  @Test\n  fun tagsAreImmutable() {\n    val builder =\n      Request\n        .Builder()\n        .url(\"https://square.com\")\n    val requestA = builder.tag(String::class.java, \"a\").build()\n    val requestB = builder.tag(String::class.java, \"b\").build()\n    val requestC = requestA.newBuilder().tag(String::class.java, \"c\").build()\n    assertThat(requestA.tag(String::class.java)).isSameInstanceAs(\"a\")\n    assertThat(requestB.tag(String::class.java)).isSameInstanceAs(\"b\")\n    assertThat(requestC.tag(String::class.java)).isSameInstanceAs(\"c\")\n  }\n\n  @Test\n  fun requestToStringRedactsSensitiveHeaders() {\n    val headers =\n      Headers\n        .Builder()\n        .add(\"content-length\", \"99\")\n        .add(\"authorization\", \"peanutbutter\")\n        .add(\"proxy-authorization\", \"chocolate\")\n        .add(\"cookie\", \"drink=coffee\")\n        .add(\"set-cookie\", \"accessory=sugar\")\n        .add(\"user-agent\", \"OkHttp\")\n        .build()\n    val request =\n      Request(\n        \"https://square.com\".toHttpUrl(),\n        headers,\n      )\n    assertThat(request.toString()).isEqualTo(\n      \"Request{method=GET, url=https://square.com/, headers=[\" +\n        \"content-length:99,\" +\n        \" authorization:██,\" +\n        \" proxy-authorization:██,\" +\n        \" cookie:██,\" +\n        \" set-cookie:██,\" +\n        \" user-agent:OkHttp\" +\n        \"]}\",\n    )\n  }\n\n  @Test\n  fun requestToStringIncludesTags() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com/\".toHttpUrl())\n        .tag<String>(\"hello\")\n        .tag<Int>(5)\n        .build()\n    assertThat(request.toString()).isEqualTo(\n      \"Request{method=GET, url=https://square.com/, tags={\" +\n        \"class kotlin.String=hello, \" +\n        \"class kotlin.Int=5\" +\n        \"}}\",\n    )\n  }\n\n  @Test\n  fun gzip() {\n    val mediaType = \"text/plain; charset=utf-8\".toMediaType()\n    val originalBody = \"This is the original message\".toRequestBody(mediaType)\n    assertThat(originalBody.contentLength()).isEqualTo(28L)\n    assertThat(originalBody.contentType()).isEqualTo(mediaType)\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://square.com/\")\n        .post(originalBody)\n        .gzip()\n        .build()\n    assertThat(request.headers[\"Content-Encoding\"]).isEqualTo(\"gzip\")\n    assertThat(request.body?.contentLength()).isEqualTo(-1L)\n    assertThat(request.body?.contentType()).isEqualTo(mediaType)\n\n    val requestBodyBytes =\n      Buffer()\n        .apply {\n          request.body?.writeTo(this)\n        }\n\n    val decompressedRequestBody =\n      GzipSource(requestBodyBytes).use {\n        it.buffer().readUtf8()\n      }\n    assertThat(decompressedRequestBody).isEqualTo(\"This is the original message\")\n  }\n\n  @Test\n  fun cannotGzipWithoutABody() {\n    assertFailsWith<IllegalStateException> {\n      Request\n        .Builder()\n        .url(\"https://square.com/\")\n        .gzip()\n        .build()\n    }.also {\n      assertThat(it).hasMessage(\"cannot gzip a request that has no body\")\n    }\n  }\n\n  @Test\n  fun cannotGzipWithAnotherContentEncoding() {\n    assertFailsWith<IllegalStateException> {\n      Request\n        .Builder()\n        .url(\"https://square.com/\")\n        .post(\"This is the original message\".toRequestBody())\n        .addHeader(\"Content-Encoding\", \"deflate\")\n        .gzip()\n        .build()\n    }.also {\n      assertThat(it).hasMessage(\"Content-Encoding already set: deflate\")\n    }\n  }\n\n  @Test\n  fun cannotGzipTwice() {\n    assertFailsWith<IllegalStateException> {\n      Request\n        .Builder()\n        .url(\"https://square.com/\")\n        .post(\"This is the original message\".toRequestBody())\n        .gzip()\n        .gzip()\n        .build()\n    }.also {\n      assertThat(it).hasMessage(\"Content-Encoding already set: gzip\")\n    }\n  }\n\n  @Test\n  fun curlGet() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://example.com\")\n        .header(\"Authorization\", \"Bearer abc123\")\n        .build()\n\n    val curl = request.toCurl()\n    assertThat(curl)\n      .isEqualTo(\n        \"\"\"\n        |curl 'https://example.com/' \\\n        |  -H 'Authorization: Bearer abc123'\n        \"\"\".trimMargin(),\n      )\n  }\n\n  @Test\n  fun curlPostWithBody() {\n    val body = \"{\\\"key\\\":\\\"value\\\"}\".toRequestBody(\"application/json\".toMediaType())\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.example.com/data\")\n        .post(body)\n        .addHeader(\"Authorization\", \"Bearer abc123\")\n        .build()\n\n    assertThat(request.toCurl())\n      .isEqualTo(\n        \"\"\"\n        |curl 'https://api.example.com/data' \\\n        |  -H 'Authorization: Bearer abc123' \\\n        |  -H 'Content-Type: application/json; charset=utf-8' \\\n        |  --data '{\"key\":\"value\"}'\n        \"\"\".trimMargin(),\n      )\n  }\n\n  @Test\n  fun bodyContentTypeTakesPrecedence() {\n    val body = \"{\\\"key\\\":\\\"value\\\"}\".toRequestBody(\"application/json\".toMediaType())\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.example.com/data\")\n        .post(body)\n        .addHeader(\"Content-Type\", \"text/plain\")\n        .build()\n\n    assertThat(request.toCurl())\n      .isEqualTo(\n        \"\"\"\n        |curl 'https://api.example.com/data' \\\n        |  -H 'Content-Type: application/json; charset=utf-8' \\\n        |  --data '{\"key\":\"value\"}'\n        \"\"\".trimMargin(),\n      )\n  }\n\n  @Test\n  fun requestContentTypeIsFallback() {\n    val body = \"{\\\"key\\\":\\\"value\\\"}\".toRequestBody(contentType = null)\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.example.com/data\")\n        .post(body)\n        .addHeader(\"Content-Type\", \"text/plain\")\n        .build()\n\n    assertThat(request.toCurl())\n      .isEqualTo(\n        \"\"\"\n        |curl 'https://api.example.com/data' \\\n        |  -H 'Content-Type: text/plain' \\\n        |  --data '{\"key\":\"value\"}'\n        \"\"\".trimMargin(),\n      )\n  }\n\n  /** Put is not the default method so `-X 'PUT'` is included. */\n  @Test\n  fun curlPutWithBody() {\n    val body = \"{\\\"key\\\":\\\"value\\\"}\".toRequestBody(\"application/json\".toMediaType())\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.example.com/data\")\n        .put(body)\n        .addHeader(\"Authorization\", \"Bearer abc123\")\n        .build()\n\n    assertThat(request.toCurl())\n      .isEqualTo(\n        \"\"\"\n        |curl 'https://api.example.com/data' \\\n        |  -X 'PUT' \\\n        |  -H 'Authorization: Bearer abc123' \\\n        |  -H 'Content-Type: application/json; charset=utf-8' \\\n        |  --data '{\"key\":\"value\"}'\n        \"\"\".trimMargin(),\n      )\n  }\n\n  @Test\n  fun curlPostWithComplexBody() {\n    val jsonBody =\n      \"\"\"\n      |{\n      |  \"user\": {\n      |    \"id\": 123,\n      |    \"name\": \"Tim O'Reilly\"\n      |  },\n      |  \"roles\": [\"admin\", \"editor\"],\n      |  \"active\": true\n      |}\n      |\n      \"\"\".trimMargin()\n\n    val body = jsonBody.toRequestBody(\"application/json\".toMediaType())\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.example.com/users\")\n        .post(body)\n        .addHeader(\"Authorization\", \"Bearer xyz789\")\n        .build()\n\n    assertThat(request.toCurl())\n      .isEqualTo(\n        \"\"\"\n        |curl 'https://api.example.com/users' \\\n        |  -H 'Authorization: Bearer xyz789' \\\n        |  -H 'Content-Type: application/json; charset=utf-8' \\\n        |  --data '{\n        |  \"user\": {\n        |    \"id\": 123,\n        |    \"name\": \"Tim O'\\''Reilly\"\n        |  },\n        |  \"roles\": [\"admin\", \"editor\"],\n        |  \"active\": true\n        |}\n        |'\n        \"\"\".trimMargin(),\n      )\n  }\n\n  @Test\n  fun curlPostWithBinaryBody() {\n    val binaryData = \"00010203\".decodeHex()\n    val body = binaryData.toRequestBody(\"application/octet-stream\".toMediaType())\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.example.com/upload\")\n        .post(body)\n        .build()\n\n    val curl = request.toCurl()\n    assertThat(curl)\n      .isEqualTo(\n        \"\"\"\n        |curl 'https://api.example.com/upload' \\\n        |  -H 'Content-Type: application/octet-stream' \\\n        |  --data-binary '00010203'\n        \"\"\".trimMargin(),\n      )\n  }\n\n  @Test\n  fun curlPostWithBinaryBodyOmitted() {\n    val binaryData = \"1020\".decodeHex()\n    val body = binaryData.toRequestBody(\"application/octet-stream\".toMediaType())\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.example.com/upload\")\n        .post(body)\n        .build()\n\n    val curl = request.toCurl(includeBody = false)\n    assertThat(curl)\n      .isEqualTo(\n        \"\"\"\n        |curl 'https://api.example.com/upload' \\\n        |  -X 'POST' \\\n        |  -H 'Content-Type: application/octet-stream'\n        \"\"\".trimMargin(),\n      )\n  }\n\n  private fun bodyToHex(body: RequestBody): String {\n    val buffer = Buffer()\n    body.writeTo(buffer)\n    return buffer.readByteString().hex()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ResponseBodyJvmTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.io.InputStreamReader\nimport java.io.Reader\nimport java.nio.charset.StandardCharsets\nimport java.util.concurrent.atomic.AtomicBoolean\nimport kotlin.test.assertFailsWith\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okhttp3.internal.and\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeHex\nimport okio.ForwardingSource\nimport okio.buffer\nimport org.junit.jupiter.api.Test\n\nclass ResponseBodyJvmTest {\n  @Test\n  fun stringEmpty() {\n    val body = body(\"\")\n    assertThat(body.string()).isEqualTo(\"\")\n  }\n\n  @Test\n  fun stringLooksLikeBomButTooShort() {\n    val body = body(\"000048\")\n    assertThat(body.string()).isEqualTo(\"\\u0000\\u0000H\")\n  }\n\n  @Test\n  fun stringDefaultsToUtf8() {\n    val body = body(\"68656c6c6f\")\n    assertThat(body.string()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun stringExplicitCharset() {\n    val body = body(\"00000068000000650000006c0000006c0000006f\", \"utf-32be\")\n    assertThat(body.string()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun stringBomOverridesExplicitCharset() {\n    val body = body(\"0000feff00000068000000650000006c0000006c0000006f\", \"utf-8\")\n    assertThat(body.string()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun stringBomUtf8() {\n    val body = body(\"efbbbf68656c6c6f\")\n    assertThat(body.string()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun stringBomUtf16Be() {\n    val body = body(\"feff00680065006c006c006f\")\n    assertThat(body.string()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun stringBomUtf16Le() {\n    val body = body(\"fffe680065006c006c006f00\")\n    assertThat(body.string()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun stringBomUtf32Be() {\n    val body = body(\"0000feff00000068000000650000006c0000006c0000006f\")\n    assertThat(body.string()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun stringBomUtf32Le() {\n    val body = body(\"fffe000068000000650000006c0000006c0000006f000000\")\n    assertThat(body.string()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun stringClosesUnderlyingSource() {\n    val closed = AtomicBoolean()\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 5\n\n        override fun source(): BufferedSource {\n          val source = Buffer().writeUtf8(\"hello\")\n          return object : ForwardingSource(source) {\n            @Throws(IOException::class)\n            override fun close() {\n              closed.set(true)\n              super.close()\n            }\n          }.buffer()\n        }\n      }\n    assertThat(body.string()).isEqualTo(\"hello\")\n    assertThat(closed.get()).isTrue()\n  }\n\n  @Test\n  fun readerEmpty() {\n    val body = body(\"\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"\")\n  }\n\n  @Test\n  fun readerLooksLikeBomButTooShort() {\n    val body = body(\"000048\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"\\u0000\\u0000H\")\n  }\n\n  @Test\n  fun readerDefaultsToUtf8() {\n    val body = body(\"68656c6c6f\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun readerExplicitCharset() {\n    val body = body(\"00000068000000650000006c0000006c0000006f\", \"utf-32be\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun readerBomUtf8() {\n    val body = body(\"efbbbf68656c6c6f\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun readerBomUtf16Be() {\n    val body = body(\"feff00680065006c006c006f\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun readerBomUtf16Le() {\n    val body = body(\"fffe680065006c006c006f00\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun readerBomUtf32Be() {\n    val body = body(\"0000feff00000068000000650000006c0000006c0000006f\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun readerBomUtf32Le() {\n    val body = body(\"fffe000068000000650000006c0000006c0000006f000000\")\n    assertThat(exhaust(body.charStream())).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun readerClosedBeforeBomClosesUnderlyingSource() {\n    val closed = AtomicBoolean()\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 5\n\n        override fun source(): BufferedSource {\n          val body = body(\"fffe680065006c006c006f00\")\n          return object : ForwardingSource(body.source()) {\n            @Throws(IOException::class)\n            override fun close() {\n              closed.set(true)\n              super.close()\n            }\n          }.buffer()\n        }\n      }\n    body.charStream().close()\n    assertThat(closed.get()).isTrue()\n  }\n\n  @Test\n  fun readerClosedAfterBomClosesUnderlyingSource() {\n    val closed = AtomicBoolean()\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 5\n\n        override fun source(): BufferedSource {\n          val body = body(\"fffe680065006c006c006f00\")\n          return object : ForwardingSource(body.source()) {\n            @Throws(IOException::class)\n            override fun close() {\n              closed.set(true)\n              super.close()\n            }\n          }.buffer()\n        }\n      }\n    val reader = body.charStream()\n    assertThat(reader.read()).isEqualTo('h'.code)\n    reader.close()\n    assertThat(closed.get()).isTrue()\n  }\n\n  @Test\n  fun sourceSeesBom() {\n    val body = \"efbbbf68656c6c6f\".decodeHex().toResponseBody()\n    val source = body.source()\n    assertThat(source.readByte() and 0xff).isEqualTo(0xef)\n    assertThat(source.readByte() and 0xff).isEqualTo(0xbb)\n    assertThat(source.readByte() and 0xff).isEqualTo(0xbf)\n    assertThat(source.readUtf8()).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun bytesEmpty() {\n    val body = body(\"\")\n    assertThat(body.bytes().size).isEqualTo(0)\n  }\n\n  @Test\n  fun bytesSeesBom() {\n    val body = body(\"efbbbf68656c6c6f\")\n    val bytes = body.bytes()\n    assertThat(bytes[0] and 0xff).isEqualTo(0xef)\n    assertThat(bytes[1] and 0xff).isEqualTo(0xbb)\n    assertThat(bytes[2] and 0xff).isEqualTo(0xbf)\n    assertThat(String(bytes, 3, 5, StandardCharsets.UTF_8)).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun bytesClosesUnderlyingSource() {\n    val closed = AtomicBoolean()\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 5\n\n        override fun source(): BufferedSource {\n          val source = Buffer().writeUtf8(\"hello\")\n          return object : ForwardingSource(source) {\n            @Throws(IOException::class)\n            override fun close() {\n              closed.set(true)\n              super.close()\n            }\n          }.buffer()\n        }\n      }\n    assertThat(body.bytes().size).isEqualTo(5)\n    assertThat(closed.get()).isTrue()\n  }\n\n  @Test\n  fun bytesThrowsWhenLengthsDisagree() {\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 10\n\n        override fun source(): BufferedSource = Buffer().writeUtf8(\"hello\")\n      }\n    assertFailsWith<IOException> {\n      body.bytes()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Content-Length (10) and stream length (5) disagree\",\n      )\n    }\n  }\n\n  @Test\n  fun bytesThrowsMoreThanIntMaxValue() {\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = Int.MAX_VALUE + 1L\n\n        override fun source(): BufferedSource = throw AssertionError()\n      }\n    assertFailsWith<IOException> {\n      body.bytes()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Cannot buffer entire body for content length: 2147483648\",\n      )\n    }\n  }\n\n  @Test\n  fun byteStringEmpty() {\n    val body = body(\"\")\n    assertThat(body.byteString()).isEqualTo(ByteString.EMPTY)\n  }\n\n  @Test\n  fun byteStringSeesBom() {\n    val body = body(\"efbbbf68656c6c6f\")\n    val actual = body.byteString()\n    val expected: ByteString = \"efbbbf68656c6c6f\".decodeHex()\n    assertThat(actual).isEqualTo(expected)\n  }\n\n  @Test\n  fun byteStringClosesUnderlyingSource() {\n    val closed = AtomicBoolean()\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 5\n\n        override fun source(): BufferedSource {\n          val source = Buffer().writeUtf8(\"hello\")\n          return object : ForwardingSource(source) {\n            @Throws(IOException::class)\n            override fun close() {\n              closed.set(true)\n              super.close()\n            }\n          }.buffer()\n        }\n      }\n    assertThat(body.byteString().size).isEqualTo(5)\n    assertThat(closed.get()).isTrue()\n  }\n\n  @Test\n  fun byteStringThrowsWhenLengthsDisagree() {\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 10\n\n        override fun source(): BufferedSource = Buffer().writeUtf8(\"hello\")\n      }\n    assertFailsWith<IOException> {\n      body.byteString()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Content-Length (10) and stream length (5) disagree\",\n      )\n    }\n  }\n\n  @Test\n  fun byteStringThrowsMoreThanIntMaxValue() {\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = Int.MAX_VALUE + 1L\n\n        override fun source(): BufferedSource = throw AssertionError()\n      }\n    assertFailsWith<IOException> {\n      body.byteString()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Cannot buffer entire body for content length: 2147483648\",\n      )\n    }\n  }\n\n  @Test\n  fun byteStreamEmpty() {\n    val body = body(\"\")\n    val bytes = body.byteStream()\n    assertThat(bytes.read()).isEqualTo(-1)\n  }\n\n  @Test\n  fun byteStreamSeesBom() {\n    val body = body(\"efbbbf68656c6c6f\")\n    val bytes = body.byteStream()\n    assertThat(bytes.read()).isEqualTo(0xef)\n    assertThat(bytes.read()).isEqualTo(0xbb)\n    assertThat(bytes.read()).isEqualTo(0xbf)\n    assertThat(exhaust(InputStreamReader(bytes, StandardCharsets.UTF_8))).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun byteStreamClosesUnderlyingSource() {\n    val closed = AtomicBoolean()\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 5\n\n        override fun source(): BufferedSource {\n          val source = Buffer().writeUtf8(\"hello\")\n          return object : ForwardingSource(source) {\n            @Throws(IOException::class)\n            override fun close() {\n              closed.set(true)\n              super.close()\n            }\n          }.buffer()\n        }\n      }\n    body.byteStream().close()\n    assertThat(closed.get()).isTrue()\n  }\n\n  @Test\n  fun unicodeTextWithUnsupportedEncoding() {\n    val text = \"eile oli oliiviõli\"\n    val body = text.toResponseBody(\"text/plain; charset=unknown\".toMediaType())\n    assertThat(body.string()).isEqualTo(text)\n  }\n\n  companion object {\n    @JvmOverloads\n    fun body(\n      hex: String,\n      charset: String? = null,\n    ): ResponseBody {\n      val mediaType = if (charset == null) null else \"any/thing; charset=$charset\".toMediaType()\n      return hex.decodeHex().toResponseBody(mediaType)\n    }\n\n    fun exhaust(reader: Reader): String {\n      val builder = StringBuilder()\n      val buf = CharArray(10)\n      var read: Int\n      while (reader.read(buf).also { read = it } != -1) {\n        builder.appendRange(buf, 0, read)\n      }\n      return builder.toString()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ResponseBodyTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isTrue\nimport kotlin.test.Test\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.IOException\nimport okio.Source\nimport okio.buffer\n\nclass ResponseBodyTest {\n  @Test\n  fun sourceEmpty() {\n    val mediaType = if (null == null) null else \"any/thing; charset=${null}\".toMediaType()\n    val body = \"\".decodeHex().toResponseBody(mediaType)\n    val source = body.source()\n    assertThat(source.exhausted()).isTrue()\n    assertThat(source.readUtf8()).isEqualTo(\"\")\n  }\n\n  @Test\n  fun sourceClosesUnderlyingSource() {\n    var closed = false\n\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 5\n\n        override fun source(): BufferedSource {\n          val source = Buffer().writeUtf8(\"hello\")\n          return object : ForwardingSource(source) {\n            override fun close() {\n              closed = true\n              super.close()\n            }\n          }.buffer()\n        }\n      }\n    body.source().close()\n    assertThat(closed).isTrue()\n  }\n\n  @Test\n  fun throwingUnderlyingSourceClosesQuietly() {\n    val body: ResponseBody =\n      object : ResponseBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 5\n\n        override fun source(): BufferedSource {\n          val source = Buffer().writeUtf8(\"hello\")\n          return object : ForwardingSource(source) {\n            @Throws(IOException::class)\n            override fun close(): Unit = throw IOException(\"Broken!\")\n          }.buffer()\n        }\n      }\n    assertThat(body.source().readUtf8()).isEqualTo(\"hello\")\n    body.close()\n  }\n\n  @Test\n  fun unicodeText() {\n    val text = \"eile oli oliiviõli\"\n    val body = text.toResponseBody()\n    assertThat(body.string()).isEqualTo(text)\n  }\n\n  @Test\n  fun unicodeTextWithCharset() {\n    val text = \"eile oli oliiviõli\"\n    val body = text.toResponseBody(\"text/plain; charset=UTF-8\".toMediaType())\n    assertThat(body.string()).isEqualTo(text)\n  }\n\n  @Test\n  fun unicodeByteString() {\n    val text = \"eile oli oliiviõli\"\n    val body = text.toResponseBody()\n    assertThat(body.byteString()).isEqualTo(text.encodeUtf8())\n  }\n\n  @Test\n  fun unicodeByteStringWithCharset() {\n    val text = \"eile oli oliiviõli\".encodeUtf8()\n    val body = text.toResponseBody(\"text/plain; charset=EBCDIC\".toMediaType())\n    assertThat(body.byteString()).isEqualTo(text)\n  }\n\n  @Test\n  fun unicodeBytes() {\n    val text = \"eile oli oliiviõli\"\n    val body = text.toResponseBody()\n    assertThat(body.bytes()).isEqualTo(text.encodeToByteArray())\n  }\n\n  @Test\n  fun unicodeBytesWithCharset() {\n    val text = \"eile oli oliiviõli\".encodeToByteArray()\n    val body = text.toResponseBody(\"text/plain; charset=EBCDIC\".toMediaType())\n    assertThat(body.bytes()).isEqualTo(text)\n  }\n}\n\nabstract class ForwardingSource(\n  val delegate: Source,\n) : Source {\n  override fun read(\n    sink: Buffer,\n    byteCount: Long,\n  ): Long = delegate.read(sink, byteCount)\n\n  override fun timeout() = delegate.timeout()\n\n  override fun close() = delegate.close()\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ResponseCommonTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport kotlin.test.Test\nimport kotlin.test.assertFailsWith\nimport okhttp3.ResponseBody.Companion.asResponseBody\nimport okio.Buffer\nimport okio.ByteString.Companion.EMPTY\nimport okio.Source\nimport okio.Timeout\nimport okio.buffer\n\nclass ResponseCommonTest {\n  @Test fun peekShorterThanResponse() {\n    val response = newResponse(responseBody(\"abcdef\"))\n    val peekedBody = response.peekBody(3)\n    assertThat(peekedBody.string()).isEqualTo(\"abc\")\n    assertThat(response.body.string()).isEqualTo(\"abcdef\")\n  }\n\n  @Test fun peekLongerThanResponse() {\n    val response = newResponse(responseBody(\"abc\"))\n    val peekedBody = response.peekBody(6)\n    assertThat(peekedBody.string()).isEqualTo(\"abc\")\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n  }\n\n  @Test fun eachPeakIsIndependent() {\n    val response = newResponse(responseBody(\"abcdef\"))\n    val p1 = response.peekBody(4)\n    val p2 = response.peekBody(2)\n    assertThat(response.body.string()).isEqualTo(\"abcdef\")\n    assertThat(p1.string()).isEqualTo(\"abcd\")\n    assertThat(p2.string()).isEqualTo(\"ab\")\n  }\n\n  @Test fun negativeStatusCodeThrowsIllegalStateException() {\n    assertFailsWith<IllegalStateException> {\n      newResponse(responseBody(\"set status code -1\"), -1)\n    }\n  }\n\n  @Test fun zeroStatusCodeIsValid() {\n    val response = newResponse(responseBody(\"set status code 0\"), 0)\n    assertThat(response.code).isEqualTo(0)\n  }\n\n  @Test fun defaultResponseBodyIsEmpty() {\n    val response =\n      Response\n        .Builder()\n        .request(\n          Request\n            .Builder()\n            .url(\"https://example.com/\")\n            .build(),\n        ).protocol(Protocol.HTTP_1_1)\n        .code(200)\n        .message(\"OK\")\n        .build()\n    assertThat(response.body.contentType()).isNull()\n    assertThat(response.body.contentLength()).isEqualTo(0L)\n    assertThat(response.body.byteString()).isEqualTo(EMPTY)\n  }\n\n  /**\n   * Returns a new response body that refuses to be read once it has been closed. This is true of\n   * most [BufferedSource] instances, but not of [Buffer].\n   */\n  private fun responseBody(content: String): ResponseBody {\n    val data = Buffer().writeUtf8(content)\n    val source: Source =\n      object : Source {\n        var closed = false\n\n        override fun close() {\n          closed = true\n        }\n\n        override fun read(\n          sink: Buffer,\n          byteCount: Long,\n        ): Long {\n          check(!closed)\n          return data.read(sink, byteCount)\n        }\n\n        override fun timeout(): Timeout = Timeout.NONE\n      }\n    return source.buffer().asResponseBody(null, -1)\n  }\n\n  private fun newResponse(\n    responseBody: ResponseBody,\n    code: Int = 200,\n  ): Response =\n    Response\n      .Builder()\n      .request(\n        Request\n          .Builder()\n          .url(\"https://example.com/\")\n          .build(),\n      ).protocol(Protocol.HTTP_1_1)\n      .code(code)\n      .message(\"OK\")\n      .body(responseBody)\n      .build()\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ResponseJvmTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport kotlin.test.assertFailsWith\nimport okhttp3.ResponseBody.Companion.asResponseBody\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.Source\nimport okio.Timeout\nimport okio.buffer\nimport org.junit.jupiter.api.Test\n\nclass ResponseJvmTest {\n  @Test\n  fun testEmptyByDefaultIfTrailersNotSet() {\n    val response = newResponse(\"\".toResponseBody())\n\n    assertThat(response.trailers()).isEmpty()\n  }\n\n  @Test\n  fun worksIfTrailersSet() {\n    val response =\n      newResponse(\"\".toResponseBody()) {\n        trailers(\n          object : TrailersSource {\n            override fun get() = Headers.headersOf(\"a\", \"b\")\n          },\n        )\n      }\n\n    assertThat(response.trailers()[\"a\"]).isEqualTo(\"b\")\n  }\n\n  @Test fun peekAfterReadingResponse() {\n    val response = newResponse(responseBody(\"abc\"))\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n\n    assertFailsWith<IllegalStateException> {\n      response.peekBody(3)\n    }\n  }\n\n  /**\n   * Returns a new response body that refuses to be read once it has been closed. This is true of\n   * most [BufferedSource] instances, but not of [Buffer].\n   */\n  private fun responseBody(content: String): ResponseBody {\n    val data = Buffer().writeUtf8(content)\n    val source: Source =\n      object : Source {\n        var closed = false\n\n        override fun close() {\n          closed = true\n        }\n\n        override fun read(\n          sink: Buffer,\n          byteCount: Long,\n        ): Long {\n          check(!closed)\n          return data.read(sink, byteCount)\n        }\n\n        override fun timeout(): Timeout = Timeout.NONE\n      }\n    return source.buffer().asResponseBody(null, -1)\n  }\n\n  private fun newResponse(\n    responseBody: ResponseBody,\n    code: Int = 200,\n    fn: Response.Builder.() -> Unit = {},\n  ): Response =\n    Response\n      .Builder()\n      .request(\n        Request\n          .Builder()\n          .url(\"https://example.com/\")\n          .build(),\n      ).protocol(Protocol.HTTP_1_1)\n      .code(code)\n      .message(\"OK\")\n      .body(responseBody)\n      .apply { fn() }\n      .build()\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/RouteFailureTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport app.cash.burst.Burst\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.SocketTimeoutException\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.CloseStream\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.internal.http.RecordingProxySelector\nimport okhttp3.internal.http2.ErrorCode\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.RepeatedTest\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Burst\nclass RouteFailureTest {\n  private lateinit var socketFactory: SpecificHostSocketFactory\n  private lateinit var client: OkHttpClient\n\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  val server1 = MockWebServer()\n\n  @StartStop\n  val server2 = MockWebServer()\n\n  private var eventRecorder = EventRecorder()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n\n  val dns = FakeDns()\n\n  val ipv4 = InetAddress.getByName(\"203.0.113.1\")\n  val ipv6 = InetAddress.getByName(\"2001:db8:ffff:ffff:ffff:ffff:ffff:1\")\n\n  val refusedStream =\n    MockResponse\n      .Builder()\n      .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n      .build()\n  val bodyResponse = MockResponse(body = \"body\")\n\n  @BeforeEach\n  fun setUp() {\n    socketFactory = SpecificHostSocketFactory(InetSocketAddress(server1.hostName, server1.port))\n\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .dns(dns)\n        .socketFactory(socketFactory)\n        .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n        .build()\n  }\n\n  @RepeatedTest(100)\n  fun http2OneBadHostOneGoodNoRetryOnConnectionFailure() {\n    enableProtocol(Protocol.HTTP_2)\n\n    val request = Request(server1.url(\"/\"))\n\n    server1.enqueue(refusedStream)\n    server2.enqueue(bodyResponse)\n\n    dns[server1.hostName] = listOf(ipv6, ipv4)\n    socketFactory[ipv6] = server1.socketAddress\n    socketFactory[ipv4] = server2.socketAddress\n\n    client =\n      client\n        .newBuilder()\n        .fastFallback(false)\n        .apply {\n          retryOnConnectionFailure = false\n        }.build()\n\n    executeSynchronously(request)\n      .assertFailureMatches(\"stream was reset: REFUSED_STREAM\")\n\n    assertThat(client.routeDatabase.failedRoutes).isEmpty()\n    server1.takeRequest()\n    assertThat(server1.requestCount).isEqualTo(1)\n    assertThat(server2.requestCount).isEqualTo(0)\n\n    assertThat(clientTestRule.recordedConnectionEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @Test\n  fun http2OneBadHostOneGoodRetryOnConnectionFailure() {\n    enableProtocol(Protocol.HTTP_2)\n\n    val request = Request(server1.url(\"/\"))\n\n    server1.enqueue(refusedStream)\n    server1.enqueue(refusedStream)\n    server2.enqueue(bodyResponse)\n\n    dns[server1.hostName] = listOf(ipv6, ipv4)\n    socketFactory[ipv6] = server1.socketAddress\n    socketFactory[ipv4] = server2.socketAddress\n\n    client =\n      client\n        .newBuilder()\n        .fastFallback(false)\n        .apply {\n          retryOnConnectionFailure = true\n        }.build()\n\n    executeSynchronously(request)\n      .assertBody(\"body\")\n\n    assertThat(client.routeDatabase.failedRoutes).isEmpty()\n    // TODO check if we expect a second request to server1, before attempting server2\n    assertThat(server1.requestCount).isEqualTo(2)\n    assertThat(server2.requestCount).isEqualTo(1)\n\n    assertThat(clientTestRule.recordedConnectionEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"NoNewExchanges\",\n      \"ConnectionReleased\",\n      \"ConnectionClosed\",\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @RepeatedTest(100)\n  fun http2OneBadHostOneGoodNoRetryOnConnectionFailureFastFallback() {\n    enableProtocol(Protocol.HTTP_2)\n\n    val request = Request(server1.url(\"/\"))\n\n    server1.enqueue(refusedStream)\n    server2.enqueue(bodyResponse)\n\n    dns[server1.hostName] = listOf(ipv6, ipv4)\n    socketFactory[ipv6] = server1.socketAddress\n    socketFactory[ipv4] = server2.socketAddress\n\n    client =\n      client\n        .newBuilder()\n        .fastFallback(true)\n        .apply {\n          retryOnConnectionFailure = false\n        }.build()\n\n    executeSynchronously(request)\n      .assertFailureMatches(\"stream was reset: REFUSED_STREAM\")\n\n    assertThat(client.routeDatabase.failedRoutes).isEmpty()\n    server1.takeRequest()\n    assertThat(server1.requestCount).isEqualTo(1)\n    assertThat(server2.requestCount).isEqualTo(0)\n\n    assertThat(clientTestRule.recordedConnectionEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @Test\n  fun http2OneBadHostOneGoodRetryOnConnectionFailureFastFallback() {\n    enableProtocol(Protocol.HTTP_2)\n\n    val request = Request(server1.url(\"/\"))\n\n    server1.enqueue(refusedStream)\n    server1.enqueue(refusedStream)\n    server2.enqueue(bodyResponse)\n\n    dns[server1.hostName] = listOf(ipv6, ipv4)\n    socketFactory[ipv6] = server1.socketAddress\n    socketFactory[ipv4] = server2.socketAddress\n\n    client =\n      client\n        .newBuilder()\n        .fastFallback(true)\n        .apply {\n          retryOnConnectionFailure = true\n        }.build()\n\n    executeSynchronously(request)\n      .assertBody(\"body\")\n\n    assertThat(client.routeDatabase.failedRoutes).isEmpty()\n    // TODO check if we expect a second request to server1, before attempting server2\n    assertThat(server1.requestCount).isEqualTo(2)\n    assertThat(server2.requestCount).isEqualTo(1)\n\n    assertThat(clientTestRule.recordedConnectionEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"NoNewExchanges\",\n      \"ConnectionReleased\",\n      \"ConnectionClosed\",\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @RepeatedTest(100)\n  fun http2OneBadHostRetryOnConnectionFailure() {\n    enableProtocol(Protocol.HTTP_2)\n\n    val request = Request(server1.url(\"/\"))\n\n    server1.enqueue(refusedStream)\n    server1.enqueue(refusedStream)\n\n    dns[server1.hostName] = listOf(ipv6)\n    socketFactory[ipv6] = server1.socketAddress\n\n    client =\n      client\n        .newBuilder()\n        .fastFallback(false)\n        .apply {\n          retryOnConnectionFailure = true\n        }.build()\n\n    executeSynchronously(request)\n      .assertFailureMatches(\"stream was reset: REFUSED_STREAM\")\n\n    assertThat(client.routeDatabase.failedRoutes).isEmpty()\n    server1.takeRequest()\n    assertThat(server1.requestCount).isEqualTo(1)\n\n    assertThat(clientTestRule.recordedConnectionEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @RepeatedTest(100)\n  fun http2OneBadHostRetryOnConnectionFailureFastFallback() {\n    enableProtocol(Protocol.HTTP_2)\n\n    val request = Request(server1.url(\"/\"))\n\n    server1.enqueue(refusedStream)\n    server1.enqueue(refusedStream)\n\n    dns[server1.hostName] = listOf(ipv6)\n    socketFactory[ipv6] = server1.socketAddress\n\n    client =\n      client\n        .newBuilder()\n        .fastFallback(true)\n        .apply {\n          retryOnConnectionFailure = true\n        }.build()\n\n    executeSynchronously(request)\n      .assertFailureMatches(\"stream was reset: REFUSED_STREAM\")\n\n    assertThat(client.routeDatabase.failedRoutes).isEmpty()\n    server1.takeRequest()\n    assertThat(server1.requestCount).isEqualTo(1)\n\n    assertThat(clientTestRule.recordedConnectionEventTypes()).containsExactly(\n      \"ConnectStart\",\n      \"ConnectEnd\",\n      \"ConnectionAcquired\",\n      \"ConnectionReleased\",\n    )\n  }\n\n  @Test\n  fun proxyMoveTest(cleanClose: Boolean = true) {\n    // Define a single Proxy at myproxy:8008 that will artificially move during the test\n    val proxySelector = RecordingProxySelector()\n    val socketAddress = InetSocketAddress.createUnresolved(\"myproxy\", 8008)\n    proxySelector.proxies.add(Proxy(Proxy.Type.HTTP, socketAddress))\n\n    // Define two host names for the DNS routing of fake proxy servers\n    val proxyServer1 = InetAddress.getByAddress(\"proxyServer1\", byteArrayOf(127, 0, 0, 2))\n    val proxyServer2 = InetAddress.getByAddress(\"proxyServer2\", byteArrayOf(127, 0, 0, 3))\n\n    println(\"Proxy Server 1 is ${server1.socketAddress}\")\n    println(\"Proxy Server 2 is ${server2.socketAddress}\")\n\n    // Since myproxy:8008 won't resolve, redirect with DNS to proxyServer1\n    // Then redirect socket connection to server1\n    dns[\"myproxy\"] = listOf(proxyServer1)\n    socketFactory[proxyServer1] = server1.socketAddress\n\n    client = client.newBuilder().proxySelector(proxySelector).build()\n\n    val request = Request(server1.url(\"/\"))\n\n    server1.enqueue(MockResponse(200))\n    server2.enqueue(MockResponse(200))\n    server2.enqueue(MockResponse(200))\n\n    println(\"\\n\\nRequest to ${server1.socketAddress}\")\n    executeSynchronously(request)\n      .assertSuccessful()\n      .assertCode(200)\n\n    println(\"server1.requestCount ${server1.requestCount}\")\n    assertThat(server1.requestCount).isEqualTo(1)\n\n    // Close the proxy server\n    if (cleanClose) {\n      server1.close()\n    }\n\n    // Now redirect with DNS to proxyServer2\n    // Then redirect socket connection to server2\n    dns[\"myproxy\"] = listOf(proxyServer2)\n    socketFactory[proxyServer2] = server2.socketAddress\n\n    println(\"\\n\\nRequest to ${server2.socketAddress}\")\n    executeSynchronously(request)\n      .apply {\n        // We may have a single failed request if not clean shutdown\n        if (cleanClose) {\n          assertSuccessful()\n          assertCode(200)\n\n          assertThat(server2.requestCount).isEqualTo(1)\n        } else {\n          this.assertFailure(SocketTimeoutException::class.java)\n        }\n      }\n\n    println(\"\\n\\nRequest to ${server2.socketAddress}\")\n    executeSynchronously(request)\n      .assertSuccessful()\n      .assertCode(200)\n  }\n\n  private fun enableProtocol(protocol: Protocol) {\n    enableTls()\n    client =\n      client\n        .newBuilder()\n        .protocols(listOf(protocol, Protocol.HTTP_1_1))\n        .build()\n    server1.protocols = client.protocols\n    server2.protocols = client.protocols\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    server1.useHttps(handshakeCertificates.sslSocketFactory())\n    server2.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n\n  private fun executeSynchronously(request: Request): RecordedResponse {\n    val call = client.newCall(request)\n    return try {\n      val response = call.execute()\n      val bodyString = response.body.string()\n      RecordedResponse(request, response, null, bodyString, null)\n    } catch (e: IOException) {\n      RecordedResponse(request, null, null, null, e)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/ServerTruncatesRequestTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.fail\nimport javax.net.ssl.SSLSocket\nimport kotlin.reflect.KClass\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.FollowUpDecision\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.RequestBodyStart\nimport okhttp3.CallEvent.RequestFailed\nimport okhttp3.CallEvent.RequestHeadersEnd\nimport okhttp3.CallEvent.RequestHeadersStart\nimport okhttp3.CallEvent.ResponseBodyEnd\nimport okhttp3.CallEvent.ResponseBodyStart\nimport okhttp3.CallEvent.ResponseHeadersEnd\nimport okhttp3.CallEvent.ResponseHeadersStart\nimport okhttp3.CallEvent.SecureConnectEnd\nimport okhttp3.CallEvent.SecureConnectStart\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.TestUtil.assumeNotWindows\nimport okhttp3.internal.duplex.AsyncRequestBody\nimport okhttp3.testing.PlatformRule\nimport okio.BufferedSink\nimport okio.IOException\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Timeout(30)\n@Tag(\"Slowish\")\nclass ServerTruncatesRequestTest {\n  @RegisterExtension\n  @JvmField\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  @JvmField\n  var clientTestRule = OkHttpClientTestRule()\n\n  private val eventRecorder = EventRecorder()\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n      .build()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n    platform.assumeHttp2Support()\n  }\n\n  @Test\n  fun serverTruncatesRequestOnLongPostHttp1() {\n    // \"java.net.SocketException: Socket closed\" thrown reading response after\n    // \"java.net.SocketException: Connection reset by peer\" writing request\n    assumeNotWindows()\n\n    serverTruncatesRequestOnLongPost(https = false)\n  }\n\n  @Test\n  fun serverTruncatesRequestOnLongPostHttp2() {\n    enableProtocol(Protocol.HTTP_2)\n    serverTruncatesRequestOnLongPost(https = true)\n  }\n\n  private fun serverTruncatesRequestOnLongPost(https: Boolean) {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .doNotReadRequestBody()\n        .build(),\n    )\n\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/\"),\n          body = SlowRequestBody,\n        ),\n      )\n\n    call.execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"abc\")\n    }\n\n    val expectedEvents = mutableListOf<KClass<out CallEvent>>()\n    // Start out with standard events...\n    expectedEvents += CallStart::class\n    expectedEvents += ProxySelectStart::class\n    expectedEvents += ProxySelectEnd::class\n    expectedEvents += DnsStart::class\n    expectedEvents += DnsEnd::class\n    expectedEvents += ConnectStart::class\n    if (https) {\n      expectedEvents += SecureConnectStart::class\n      expectedEvents += SecureConnectEnd::class\n    }\n    expectedEvents += ConnectEnd::class\n    expectedEvents += ConnectionAcquired::class\n    expectedEvents += RequestHeadersStart::class\n    expectedEvents += RequestHeadersEnd::class\n    expectedEvents += RequestBodyStart::class\n    // ... but we can read the response even after writing the request fails.\n    expectedEvents += RequestFailed::class\n    expectedEvents += ResponseHeadersStart::class\n    expectedEvents += ResponseHeadersEnd::class\n    expectedEvents += FollowUpDecision::class\n    expectedEvents += ResponseBodyStart::class\n    expectedEvents += ResponseBodyEnd::class\n    expectedEvents += ConnectionReleased::class\n    expectedEvents += CallEnd::class\n    assertThat(eventRecorder.recordedEventTypes()).isEqualTo(expectedEvents)\n\n    // Confirm that the connection pool was not corrupted by making another call.\n    makeSimpleCall()\n  }\n\n  /**\n   * If the server returns a full response, it doesn't really matter if the HTTP/2 stream is reset.\n   * Attempts to write the request body fails fast.\n   */\n  @Test\n  fun serverTruncatesRequestHttp2OnDuplexRequest() {\n    enableProtocol(Protocol.HTTP_2)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .doNotReadRequestBody()\n        .build(),\n    )\n\n    val requestBody = AsyncRequestBody()\n\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/\"),\n          body = requestBody,\n        ),\n      )\n\n    call.execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"abc\")\n      val requestBodyOut = requestBody.takeSink()\n      assertFailsWith<IOException> {\n        SlowRequestBody.writeTo(requestBodyOut)\n      }\n      assertFailsWith<IOException> {\n        requestBodyOut.close()\n      }\n    }\n\n    // Confirm that the connection pool was not corrupted by making another call.\n    makeSimpleCall()\n  }\n\n  @Test\n  fun serverTruncatesRequestButTrailersCanStillBeReadHttp1() {\n    // \"java.net.SocketException: Socket closed\" thrown reading response after\n    // \"java.net.SocketException: Connection reset by peer\" writing request\n    assumeNotWindows()\n\n    serverTruncatesRequestButTrailersCanStillBeRead(http2 = false)\n  }\n\n  @Test\n  fun serverTruncatesRequestButTrailersCanStillBeReadHttp2() {\n    enableProtocol(Protocol.HTTP_2)\n    serverTruncatesRequestButTrailersCanStillBeRead(http2 = true)\n  }\n\n  private fun serverTruncatesRequestButTrailersCanStillBeRead(http2: Boolean) {\n    val mockResponse =\n      MockResponse\n        .Builder()\n        .doNotReadRequestBody()\n        .trailers(headersOf(\"caboose\", \"xyz\"))\n\n    // Trailers always work for HTTP/2, but only for chunked bodies in HTTP/1.\n    if (http2) {\n      mockResponse.body(\"abc\")\n    } else {\n      mockResponse.chunkedBody(\"abc\", 1)\n    }\n\n    server.enqueue(mockResponse.build())\n\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/\"),\n          body = SlowRequestBody,\n        ),\n      )\n\n    call.execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"abc\")\n      assertThat(response.trailers()).isEqualTo(headersOf(\"caboose\", \"xyz\"))\n    }\n  }\n\n  @Disabled(\"Follow up with fix in https://github.com/square/okhttp/issues/6853\")\n  @Test\n  fun serverDisconnectsBeforeSecondRequestHttp1() {\n    enableProtocol(Protocol.HTTP_1_1)\n\n    server.enqueue(MockResponse(code = 200, body = \"Req1\"))\n    server.enqueue(MockResponse(code = 200, body = \"Req2\"))\n\n    val eventListener =\n      object : EventListener() {\n        var socket: SSLSocket? = null\n        var closed = false\n\n        override fun connectionAcquired(\n          call: Call,\n          connection: Connection,\n        ) {\n          socket = connection.socket() as SSLSocket\n        }\n\n        override fun requestHeadersStart(call: Call) {\n          if (closed) {\n            throw IOException(\"fake socket failure\")\n          }\n        }\n      }\n    val localClient = client.newBuilder().eventListener(eventListener).build()\n\n    val call1 = localClient.newCall(Request(server.url(\"/\")))\n\n    call1.execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"Req1\")\n      assertThat(response.handshake).isNotNull()\n      assertThat(response.protocol == Protocol.HTTP_1_1)\n    }\n\n    eventListener.closed = true\n\n    val call2 = localClient.newCall(Request(server.url(\"/\")))\n\n    assertThrows<IOException>(\"fake socket failure\") {\n      call2.execute()\n    }\n  }\n\n  @Test\n  fun noAttemptToReadResponseIfLoadingRequestBodyIsSourceOfFailure() {\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val requestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun writeTo(sink: BufferedSink) {\n          throw IOException(\"boom\") // Despite this exception, 'sink' is healthy.\n        }\n      }\n\n    val callA =\n      client.newCall(\n        Request(\n          url = server.url(\"/\"),\n          body = requestBody,\n        ),\n      )\n\n    assertFailsWith<IOException> {\n      callA.execute()\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"boom\")\n    }\n\n    assertThat(server.requestCount).isEqualTo(0)\n\n    // Confirm that the connection pool was not corrupted by making another call. This doesn't use\n    // makeSimpleCall() because it uses the MockResponse enqueued above.\n    val callB = client.newCall(Request(server.url(\"/\")))\n    callB.execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"abc\")\n    }\n  }\n\n  private fun makeSimpleCall() {\n    server.enqueue(MockResponse(body = \"healthy\"))\n    val callB = client.newCall(Request(server.url(\"/\")))\n    callB.execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"healthy\")\n    }\n  }\n\n  private fun enableProtocol(protocol: Protocol) {\n    enableTls()\n    client =\n      client\n        .newBuilder()\n        .protocols(listOf(protocol, Protocol.HTTP_1_1))\n        .build()\n    server.protocols = client.protocols\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n\n  /** A request body that slowly trickles bytes, expecting to not complete. */\n  private object SlowRequestBody : RequestBody() {\n    override fun contentType(): MediaType? = null\n\n    override fun writeTo(sink: BufferedSink) {\n      for (i in 0 until 50) {\n        sink.writeUtf8(\"abc\")\n        sink.flush()\n        Thread.sleep(100)\n      }\n      fail(\"\")\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/SessionReuseTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport app.cash.burst.Burst\nimport app.cash.burst.burstValues\nimport assertk.assertThat\nimport assertk.assertions.containsExactlyInAnyOrder\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isNotEmpty\nimport javax.net.ssl.SSLSocket\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.testing.PlatformRule\nimport okhttp3.testing.PlatformVersion\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertNotEquals\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Burst\nclass SessionReuseTest {\n  @JvmField @RegisterExtension\n  var platform = PlatformRule()\n\n  @JvmField @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n\n  var client = clientTestRule.newClient()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    // Default after JDK 14, but we are avoiding tests that assume special setup.\n    // System.setProperty(\"jdk.tls.client.enableSessionTicketExtension\", \"true\")\n    // System.setProperty(\"jdk.tls.server.enableSessionTicketExtension\", \"true\")\n\n    // IllegalStateException: Cannot resume session and session creation is disabled\n    platform.assumeNotBouncyCastle()\n  }\n\n  @Test\n  fun testSessionReuse(tlsVersion: String = burstValues(\"TLSv1.2\", \"TLSv1.3\")) {\n    if (tlsVersion == TlsVersion.TLS_1_3.javaName) {\n      assumeTrue(PlatformVersion.majorVersion != 8)\n    }\n\n    val sessionIds = mutableListOf<String>()\n\n    enableTls()\n\n    val tlsVersion = TlsVersion.forJavaName(tlsVersion)\n    val spec =\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .tlsVersions(tlsVersion)\n        .build()\n\n    var reuseSession = false\n\n    val sslContext = handshakeCertificates.sslContext()\n    val systemSslSocketFactory = sslContext.socketFactory\n    val sslSocketFactory =\n      object : DelegatingSSLSocketFactory(systemSslSocketFactory) {\n        override fun configureSocket(sslSocket: SSLSocket): SSLSocket =\n          sslSocket.apply {\n            if (reuseSession) {\n              this.enableSessionCreation = false\n            }\n          }\n      }\n\n    client =\n      client\n        .newBuilder()\n        .connectionSpecs(listOf(spec))\n        .eventListenerFactory(\n          clientTestRule.wrap(\n            object : EventListener() {\n              override fun connectionAcquired(\n                call: Call,\n                connection: Connection,\n              ) {\n                val sslSocket = connection.socket() as SSLSocket\n\n                sessionIds.add(\n                  sslSocket.session.id\n                    .toByteString()\n                    .hex(),\n                )\n              }\n            },\n          ),\n        ).sslSocketFactory(sslSocketFactory, handshakeCertificates.trustManager)\n        .build()\n\n    server.enqueue(MockResponse(body = \"abc1\"))\n    server.enqueue(MockResponse(body = \"abc2\"))\n\n    val request = Request(server.url(\"/\"))\n\n    client.newCall(request).execute().use { response ->\n      assertEquals(200, response.code)\n    }\n\n    client.connectionPool.evictAll()\n    assertEquals(0, client.connectionPool.connectionCount())\n\n    // Force reuse. This appears flaky (30% of the time) even though sessions are reused.\n    // javax.net.ssl.SSLHandshakeException: No new session is allowed and no existing\n    // session can be resumed\n    //\n    // Report https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8264944\n    // Sessions improvement https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8245576\n    if (!platform.isJdk9() && !platform.isOpenJsse() && !platform.isJdk8Alpn()) {\n      reuseSession = true\n    }\n\n    client.newCall(request).execute().use { response ->\n      assertEquals(200, response.code)\n    }\n\n    assertEquals(2, sessionIds.size)\n    val directSessionIds =\n      sslContext.clientSessionContext.ids\n        .toList()\n        .map { it.toByteString().hex() }\n\n    if (platform.isConscrypt()) {\n      if (tlsVersion == TlsVersion.TLS_1_3) {\n        assertThat(sessionIds[0]).isEmpty()\n        assertThat(sessionIds[1]).isEmpty()\n\n        // https://github.com/google/conscrypt/issues/985\n        // assertThat(directSessionIds).containsExactlyInAnyOrder(sessionIds[0], sessionIds[1])\n      } else {\n        assertThat(sessionIds[0]).isNotEmpty()\n        assertThat(sessionIds[1]).isNotEmpty()\n\n        assertThat(directSessionIds).containsExactlyInAnyOrder(sessionIds[1])\n      }\n    } else {\n      if (tlsVersion == TlsVersion.TLS_1_3) {\n        // We can't rely on the same session id with TLSv1.3 ids.\n        assertNotEquals(sessionIds[0], sessionIds[1])\n      } else {\n        // With TLSv1.2 it is really JDK specific.\n        // assertEquals(sessionIds[0], sessionIds[1])\n        // assertThat(directSessionIds).contains(sessionIds[0], sessionIds[1])\n      }\n      assertThat(sessionIds[0]).isNotEmpty()\n    }\n  }\n\n  private fun enableTls() {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/SocketChannelTest.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport app.cash.burst.Burst\nimport app.cash.burst.burstValues\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotEmpty\nimport assertk.assertions.isNotNull\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.util.concurrent.CompletableFuture\nimport java.util.concurrent.TimeUnit.SECONDS\nimport javax.net.ssl.SNIHostName\nimport javax.net.ssl.SNIMatcher\nimport javax.net.ssl.SNIServerName\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.StandardConstants\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Protocol.HTTP_1_1\nimport okhttp3.Protocol.HTTP_2\nimport okhttp3.Provider.CONSCRYPT\nimport okhttp3.TlsExtensionMode.STANDARD\nimport okhttp3.TlsVersion.TLS_1_2\nimport okhttp3.TlsVersion.TLS_1_3\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport org.junit.jupiter.api.Assumptions.assumeFalse\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Suppress(\"UsePropertyAccessSyntax\")\n@Timeout(6)\n@Tag(\"slow\")\n@Burst\nclass SocketChannelTest {\n  @JvmField @RegisterExtension\n  val platform = PlatformRule()\n\n  @JvmField @RegisterExtension\n  val clientTestRule =\n    OkHttpClientTestRule().apply {\n      recordFrames = true\n      // recordSslDebug = true\n    }\n\n  // https://tools.ietf.org/html/rfc6066#page-6 specifies a FQDN is required.\n  val hostname = \"local.host\"\n  private val handshakeCertificates =\n    run {\n      // Generate a self-signed cert for the server to serve and the client to trust.\n      val heldCertificate =\n        HeldCertificate\n          .Builder()\n          .commonName(hostname)\n          .addSubjectAlternativeName(hostname)\n          .build()\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(heldCertificate)\n        .addTrustedCertificate(heldCertificate.certificate)\n        .build()\n    }\n  private var acceptedHostName: String? = null\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setUp() {\n    // Test designed for Conscrypt and JSSE\n    platform.assumeNotBouncyCastle()\n  }\n\n  @Test\n  fun testHttp(socketMode: SocketMode = burstValues(Channel, Standard)) {\n    testConnection(socketMode)\n  }\n\n  @Test\n  fun testHttps(\n    provider: Provider = Provider.JSSE,\n    protocol: Protocol = burstValues(HTTP_1_1, HTTP_2),\n    tlsVersion: TlsVersion = burstValues(TLS_1_3, TLS_1_2),\n    socketMode: SocketMode = burstValues(Channel, Standard),\n    tlsExtensionMode: TlsExtensionMode = TlsExtensionMode.STANDARD,\n  ) {\n    testConnection(TlsInstance(provider, protocol, tlsVersion, socketMode, tlsExtensionMode))\n  }\n\n  private fun testConnection(socketMode: SocketMode) {\n    // https://github.com/square/okhttp/pull/6554\n    assumeFalse(\n      socketMode is TlsInstance &&\n        socketMode.socketMode == Channel &&\n        socketMode.protocol == HTTP_2 &&\n        socketMode.tlsExtensionMode == STANDARD,\n      \"failing for channel and h2\",\n    )\n\n    if (socketMode is TlsInstance) {\n      assumeTrue((socketMode.provider == CONSCRYPT) == platform.isConscrypt())\n    }\n\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .dns { listOf(InetAddress.getByName(\"localhost\")) }\n        .callTimeout(4, SECONDS)\n        .writeTimeout(2, SECONDS)\n        .readTimeout(2, SECONDS)\n        .apply {\n          if (socketMode is TlsInstance) {\n            if (socketMode.socketMode == Channel) {\n              socketFactory(ChannelSocketFactory())\n            }\n\n            connectionSpecs(\n              listOf(\n                ConnectionSpec\n                  .Builder(ConnectionSpec.COMPATIBLE_TLS)\n                  .tlsVersions(socketMode.tlsVersion)\n                  .supportsTlsExtensions(socketMode.tlsExtensionMode == STANDARD)\n                  .build(),\n              ),\n            )\n\n            val sslSocketFactory = handshakeCertificates.sslSocketFactory()\n\n            sslSocketFactory(\n              sslSocketFactory,\n              handshakeCertificates.trustManager,\n            )\n\n            when (socketMode.protocol) {\n              HTTP_2 -> protocols(listOf(HTTP_2, HTTP_1_1))\n              HTTP_1_1 -> protocols(listOf(HTTP_1_1))\n              else -> TODO()\n            }\n\n            val serverSslSocketFactory =\n              object : DelegatingSSLSocketFactory(sslSocketFactory) {\n                override fun configureSocket(sslSocket: SSLSocket): SSLSocket {\n                  return sslSocket.apply {\n                    sslParameters =\n                      sslParameters.apply {\n                        sniMatchers =\n                          listOf(\n                            object : SNIMatcher(StandardConstants.SNI_HOST_NAME) {\n                              override fun matches(serverName: SNIServerName): Boolean {\n                                acceptedHostName = (serverName as SNIHostName).asciiName\n                                return true\n                              }\n                            },\n                          )\n                      }\n                  }\n                }\n              }\n            server.useHttps(serverSslSocketFactory)\n          } else if (socketMode == Channel) {\n            socketFactory(ChannelSocketFactory())\n          }\n        }.build()\n\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    @Suppress(\"HttpUrlsUsage\")\n    val url =\n      if (socketMode is TlsInstance) {\n        \"https://$hostname:${server.port}/get\"\n      } else {\n        \"http://$hostname:${server.port}/get\"\n      }\n\n    val request =\n      Request\n        .Builder()\n        .url(url)\n        .build()\n\n    val promise = CompletableFuture<Response>()\n\n    val call = client.newCall(request)\n    call.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          promise.completeExceptionally(e)\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          promise.complete(response)\n        }\n      },\n    )\n\n    val response = promise.get(4, SECONDS)\n\n    assertThat(response).isNotNull()\n\n    assertThat(response.body.string()).isNotEmpty()\n\n    if (socketMode is TlsInstance) {\n      assertThat(response.handshake!!.tlsVersion).isEqualTo(socketMode.tlsVersion)\n\n      assertThat(acceptedHostName).isEqualTo(hostname)\n\n      if (socketMode.tlsExtensionMode == STANDARD) {\n        assertThat(response.protocol).isEqualTo(socketMode.protocol)\n      } else {\n        assertThat(response.protocol).isEqualTo(HTTP_1_1)\n      }\n    }\n  }\n}\n\nsealed class SocketMode\n\nobject Channel : SocketMode() {\n  override fun toString(): String = \"Channel\"\n}\n\nobject Standard : SocketMode() {\n  override fun toString(): String = \"Standard\"\n}\n\ndata class TlsInstance(\n  val provider: Provider,\n  val protocol: Protocol,\n  val tlsVersion: TlsVersion,\n  val socketMode: SocketMode,\n  val tlsExtensionMode: TlsExtensionMode,\n) : SocketMode() {\n  override fun toString(): String = \"$provider/$protocol/$tlsVersion/$socketMode/$tlsExtensionMode\"\n}\n\nenum class Provider {\n  JSSE,\n  CONSCRYPT,\n}\n\nenum class TlsExtensionMode {\n  DISABLED,\n  STANDARD,\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/SocksProxy.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.ProtocolException\nimport java.net.Proxy\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.net.SocketException\nimport java.util.concurrent.ConcurrentHashMap\nimport java.util.concurrent.Executors\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.logging.Level\nimport java.util.logging.Logger\nimport okhttp3.TestUtil.threadFactory\nimport okhttp3.internal.and\nimport okhttp3.internal.closeQuietly\nimport okhttp3.internal.threadName\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.buffer\nimport okio.sink\nimport okio.source\nimport okio.use\n\n/**\n * A limited implementation of SOCKS Protocol Version 5, intended to be similar to MockWebServer.\n * See [RFC 1928](https://www.ietf.org/rfc/rfc1928.txt).\n */\nclass SocksProxy {\n  private val executor = Executors.newCachedThreadPool(threadFactory(\"SocksProxy\"))\n  private var serverSocket: ServerSocket? = null\n  private val connectionCount = AtomicInteger()\n  private val openSockets: MutableSet<Socket> = ConcurrentHashMap.newKeySet()\n\n  fun play() {\n    serverSocket = ServerSocket(0)\n    executor.execute {\n      val threadName = \"SocksProxy ${serverSocket!!.localPort}\"\n      Thread.currentThread().name = threadName\n      try {\n        while (true) {\n          val socket = serverSocket!!.accept()\n          connectionCount.incrementAndGet()\n          service(socket)\n        }\n      } catch (e: SocketException) {\n        logger.info(\"$threadName done accepting connections: ${e.message}\")\n      } catch (e: IOException) {\n        logger.log(Level.WARNING, \"$threadName failed unexpectedly\", e)\n      } finally {\n        for (socket in openSockets) {\n          socket.closeQuietly()\n        }\n        Thread.currentThread().name = \"SocksProxy\"\n      }\n    }\n  }\n\n  fun proxy(): Proxy =\n    Proxy(\n      Proxy.Type.SOCKS,\n      InetSocketAddress.createUnresolved(\"localhost\", serverSocket!!.localPort),\n    )\n\n  fun connectionCount(): Int = connectionCount.get()\n\n  fun shutdown() {\n    serverSocket!!.close()\n    executor.shutdown()\n    if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {\n      throw IOException(\"Gave up waiting for executor to shut down\")\n    }\n  }\n\n  private fun service(from: Socket) {\n    val name = \"SocksProxy ${from.remoteSocketAddress}\"\n    threadName(name) {\n      try {\n        val fromSource = from.source().buffer()\n        val fromSink = from.sink().buffer()\n        hello(fromSource, fromSink)\n        acceptCommand(from.inetAddress, fromSource, fromSink)\n        openSockets.add(from)\n      } catch (e: IOException) {\n        logger.log(Level.WARNING, \"$name failed\", e)\n        from.closeQuietly()\n      }\n    }\n  }\n\n  private fun hello(\n    fromSource: BufferedSource,\n    fromSink: BufferedSink,\n  ) {\n    val version = fromSource.readByte() and 0xff\n    val methodCount = fromSource.readByte() and 0xff\n    var selectedMethod = METHOD_NONE\n    if (version != VERSION_5) {\n      throw ProtocolException(\"unsupported version: $version\")\n    }\n    for (i in 0 until methodCount) {\n      val candidateMethod: Int = fromSource.readByte() and 0xff\n      if (candidateMethod == METHOD_NO_AUTHENTICATION_REQUIRED) {\n        selectedMethod = candidateMethod\n      }\n    }\n    when (selectedMethod) {\n      METHOD_NO_AUTHENTICATION_REQUIRED -> {\n        fromSink.writeByte(VERSION_5)\n        fromSink.writeByte(selectedMethod)\n        fromSink.emit()\n      }\n\n      else -> {\n        throw ProtocolException(\"unsupported method: $selectedMethod\")\n      }\n    }\n  }\n\n  private fun acceptCommand(\n    fromAddress: InetAddress,\n    fromSource: BufferedSource,\n    fromSink: BufferedSink,\n  ) {\n    // Read the command.\n    val version = fromSource.readByte() and 0xff\n    if (version != VERSION_5) throw ProtocolException(\"unexpected version: $version\")\n\n    val command = fromSource.readByte() and 0xff\n\n    val reserved = fromSource.readByte() and 0xff\n    if (reserved != 0) throw ProtocolException(\"unexpected reserved: $reserved\")\n\n    val addressType = fromSource.readByte() and 0xff\n    val toAddress =\n      when (addressType) {\n        ADDRESS_TYPE_IPV4 -> {\n          InetAddress.getByAddress(fromSource.readByteArray(4L))\n        }\n\n        ADDRESS_TYPE_DOMAIN_NAME -> {\n          val domainNameLength: Int = fromSource.readByte() and 0xff\n          val domainName = fromSource.readUtf8(domainNameLength.toLong())\n          // Resolve HOSTNAME_THAT_ONLY_THE_PROXY_KNOWS to localhost.\n          when {\n            domainName.equals(HOSTNAME_THAT_ONLY_THE_PROXY_KNOWS, ignoreCase = true) -> {\n              InetAddress.getByName(\"localhost\")\n            }\n\n            else -> {\n              InetAddress.getByName(domainName)\n            }\n          }\n        }\n\n        else -> {\n          throw ProtocolException(\"unsupported address type: $addressType\")\n        }\n      }\n\n    val port = fromSource.readShort() and 0xffff\n\n    when (command) {\n      COMMAND_CONNECT -> {\n        val toSocket = Socket(toAddress, port)\n        val localAddress = toSocket.localAddress.address\n        if (localAddress.size != 4) {\n          throw ProtocolException(\"unexpected address: \" + toSocket.localAddress)\n        }\n\n        // Write the reply.\n        fromSink.writeByte(VERSION_5)\n        fromSink.writeByte(REPLY_SUCCEEDED)\n        fromSink.writeByte(0)\n        fromSink.writeByte(ADDRESS_TYPE_IPV4)\n        fromSink.write(localAddress)\n        fromSink.writeShort(toSocket.localPort)\n        fromSink.emit()\n        logger.log(Level.INFO, \"SocksProxy connected $fromAddress to $toAddress\")\n\n        // Copy sources to sinks in both directions.\n        val toSource = toSocket.source().buffer()\n        val toSink = toSocket.sink().buffer()\n        openSockets.add(toSocket)\n        transfer(fromAddress, toAddress, fromSource, toSink)\n        transfer(fromAddress, toAddress, toSource, fromSink)\n      }\n\n      else -> {\n        throw ProtocolException(\"unexpected command: $command\")\n      }\n    }\n  }\n\n  private fun transfer(\n    fromAddress: InetAddress,\n    toAddress: InetAddress,\n    source: BufferedSource,\n    sink: BufferedSink,\n  ) {\n    executor.execute {\n      val name = \"SocksProxy $fromAddress to $toAddress\"\n      threadName(name) {\n        val buffer = Buffer()\n        try {\n          sink.use {\n            source.use {\n              while (true) {\n                val byteCount = source.read(buffer, 8192L)\n                if (byteCount == -1L) break\n                sink.write(buffer, byteCount)\n                sink.emit()\n              }\n            }\n          }\n        } catch (e: IOException) {\n          logger.log(Level.WARNING, \"$name failed\", e)\n        }\n      }\n    }\n  }\n\n  companion object {\n    const val HOSTNAME_THAT_ONLY_THE_PROXY_KNOWS = \"onlyProxyCanResolveMe.org\"\n    private const val VERSION_5 = 5\n    private const val METHOD_NONE = 0xff\n    private const val METHOD_NO_AUTHENTICATION_REQUIRED = 0\n    private const val ADDRESS_TYPE_IPV4 = 1\n    private const val ADDRESS_TYPE_DOMAIN_NAME = 3\n    private const val COMMAND_CONNECT = 1\n    private const val REPLY_SUCCEEDED = 0\n    private val logger = Logger.getLogger(SocksProxy::class.java.name)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/SocksProxyTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport java.net.ProxySelector\nimport java.net.SocketAddress\nimport java.net.URI\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass SocksProxyTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private val socksProxy = SocksProxy()\n\n  @BeforeEach\n  fun setUp() {\n    socksProxy.play()\n  }\n\n  @AfterEach\n  fun tearDown() {\n    socksProxy.shutdown()\n  }\n\n  @Test\n  fun proxy() {\n    server.enqueue(MockResponse.Builder().body(\"abc\").build())\n    server.enqueue(MockResponse.Builder().body(\"def\").build())\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .proxy(socksProxy.proxy())\n        .build()\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.body.string()).isEqualTo(\"abc\")\n    val request2 = Request.Builder().url(server.url(\"/\")).build()\n    val response2 = client.newCall(request2).execute()\n    assertThat(response2.body.string()).isEqualTo(\"def\")\n\n    // The HTTP calls should share a single connection.\n    assertThat(socksProxy.connectionCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun proxySelector() {\n    server.enqueue(MockResponse.Builder().body(\"abc\").build())\n    val proxySelector: ProxySelector =\n      object : ProxySelector() {\n        override fun select(uri: URI) = listOf(socksProxy.proxy())\n\n        override fun connectFailed(\n          uri: URI,\n          socketAddress: SocketAddress,\n          e: IOException,\n        ) = error(\"unexpected call\")\n      }\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .proxySelector(proxySelector)\n        .build()\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    assertThat(socksProxy.connectionCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun checkRemoteDNSResolve() {\n    // This testcase will fail if the target is resolved locally instead of through the proxy.\n    server.enqueue(MockResponse.Builder().body(\"abc\").build())\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .proxy(socksProxy.proxy())\n        .build()\n    val url =\n      server\n        .url(\"/\")\n        .newBuilder()\n        .host(SocksProxy.HOSTNAME_THAT_ONLY_THE_PROXY_KNOWS)\n        .build()\n    val request = Request.Builder().url(url).build()\n    val response1 = client.newCall(request).execute()\n    assertThat(response1.body.string()).isEqualTo(\"abc\")\n    assertThat(socksProxy.connectionCount()).isEqualTo(1)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/TestLogHandler.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.TimeUnit\nimport java.util.logging.Handler\nimport java.util.logging.Level\nimport java.util.logging.LogRecord\nimport java.util.logging.Logger\nimport org.junit.jupiter.api.extension.AfterEachCallback\nimport org.junit.jupiter.api.extension.BeforeEachCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.rules.TestRule\nimport org.junit.runner.Description\nimport org.junit.runners.model.Statement\n\n/**\n * A log handler that records which log messages were published so that a calling test can make\n * assertions about them.\n */\nclass TestLogHandler(\n  private val logger: Logger,\n) : TestRule,\n  BeforeEachCallback,\n  AfterEachCallback {\n  constructor(loggerName: Class<*>) : this(Logger.getLogger(loggerName.getName()))\n\n  private val logs = LinkedBlockingQueue<String>()\n\n  private val handler =\n    object : Handler() {\n      override fun publish(logRecord: LogRecord) {\n        logs += \"${logRecord.level}: ${logRecord.message}\"\n      }\n\n      override fun flush() {\n      }\n\n      override fun close() {\n      }\n    }\n\n  private var previousLevel: Level? = null\n\n  override fun beforeEach(context: ExtensionContext?) {\n    previousLevel = logger.level\n    logger.addHandler(handler)\n    logger.setLevel(Level.FINEST)\n  }\n\n  override fun afterEach(context: ExtensionContext?) {\n    logger.setLevel(previousLevel)\n    logger.removeHandler(handler)\n  }\n\n  override fun apply(\n    base: Statement,\n    description: Description,\n  ): Statement =\n    object : Statement() {\n      override fun evaluate() {\n        beforeEach(null)\n        try {\n          base.evaluate()\n        } finally {\n          afterEach(null)\n        }\n      }\n    }\n\n  fun takeAll(): List<String> {\n    val list = mutableListOf<String>()\n    logs.drainTo(list)\n    return list\n  }\n\n  fun take(): String =\n    logs.poll(10, TimeUnit.SECONDS)\n      ?: throw AssertionError(\"Timed out waiting for log message.\")\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/TestTls13Request.kt",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.security.Security\nimport okhttp3.internal.platform.Platform\nimport org.conscrypt.Conscrypt\n\n// TLS 1.3\nprivate val TLS13_CIPHER_SUITES =\n  listOf(\n    CipherSuite.TLS_AES_128_GCM_SHA256,\n    CipherSuite.TLS_AES_256_GCM_SHA384,\n    CipherSuite.TLS_CHACHA20_POLY1305_SHA256,\n    CipherSuite.TLS_AES_128_CCM_SHA256,\n    CipherSuite.TLS_AES_128_CCM_8_SHA256,\n  )\n\n/**\n * A TLS 1.3 only Connection Spec. This will be eventually be exposed\n * as part of MODERN_TLS or folded into the default OkHttp client once published and\n * available in JDK11 or Conscrypt.\n */\nprivate val TLS_13 =\n  ConnectionSpec\n    .Builder(true)\n    .cipherSuites(*TLS13_CIPHER_SUITES.toTypedArray())\n    .tlsVersions(TlsVersion.TLS_1_3)\n    .build()\n\nprivate val TLS_12 =\n  ConnectionSpec\n    .Builder(ConnectionSpec.RESTRICTED_TLS)\n    .tlsVersions(TlsVersion.TLS_1_2)\n    .build()\n\nprivate fun testClient(\n  urls: List<String>,\n  client: OkHttpClient,\n) {\n  try {\n    for (url in urls) {\n      sendRequest(client, url)\n    }\n  } finally {\n    client.dispatcher.executorService.shutdownNow()\n    client.connectionPool.evictAll()\n  }\n}\n\nprivate fun buildClient(vararg specs: ConnectionSpec): OkHttpClient =\n  OkHttpClient\n    .Builder()\n    .connectionSpecs(listOf(*specs))\n    .build()\n\nprivate fun sendRequest(\n  client: OkHttpClient,\n  url: String,\n) {\n  System.out.printf(\"%-40s \", url)\n  System.out.flush()\n  println(Platform.get())\n  val request =\n    Request\n      .Builder()\n      .url(url)\n      .build()\n  try {\n    client.newCall(request).execute().use { response ->\n      val handshake = response.handshake\n      println(\n        \"${handshake!!.tlsVersion} ${handshake.cipherSuite} ${response.protocol} \" +\n          \"${response.code} ${response.body.bytes().size}b\",\n      )\n    }\n  } catch (ioe: IOException) {\n    println(ioe)\n  }\n}\n\nfun main(vararg args: String) {\n  // System.setProperty(\"javax.net.debug\", \"ssl:handshake:verbose\");\n  Security.insertProviderAt(Conscrypt.newProviderBuilder().provideTrustManager().build(), 1)\n  println(\"Running tests using ${Platform.get()} ${System.getProperty(\"java.vm.version\")}\")\n\n  // https://github.com/tlswg/tls13-spec/wiki/Implementations\n  val urls =\n    listOf(\n      \"https://enabled.tls13.com\",\n      \"https://www.howsmyssl.com/a/check\",\n      \"https://tls13.cloudflare.com\",\n      \"https://www.allizom.org/robots.txt\",\n      \"https://tls13.crypto.mozilla.org/\",\n      \"https://tls.ctf.network/robots.txt\",\n      \"https://rustls.jbp.io/\",\n      \"https://h2o.examp1e.net\",\n      \"https://mew.org/\",\n      \"https://tls13.baishancloud.com/\",\n      \"https://tls13.akamai.io/\",\n      \"https://swifttls.org/\",\n      \"https://www.googleapis.com/robots.txt\",\n      \"https://graph.facebook.com/robots.txt\",\n      \"https://api.twitter.com/robots.txt\",\n      \"https://connect.squareup.com/robots.txt\",\n    )\n\n  println(\"TLS1.3+TLS1.2\")\n  testClient(urls, buildClient(ConnectionSpec.RESTRICTED_TLS))\n\n  println(\"\\nTLS1.3 only\")\n  testClient(urls, buildClient(TLS_13))\n\n  println(\"\\nTLS1.3 then fallback\")\n  testClient(urls, buildClient(TLS_13, TLS_12))\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/TrailersTest.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isGreaterThan\nimport assertk.assertions.isIn\nimport assertk.assertions.isLessThan\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.lang.Thread.sleep\nimport java.util.concurrent.TimeUnit\nimport kotlin.concurrent.thread\nimport kotlin.test.assertFailsWith\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.measureTime\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okhttp3.internal.http.ExchangeCodec.Companion.DISCARD_STREAM_TIMEOUT_MILLIS\nimport okhttp3.internal.http2.Http2Connection.Companion.OKHTTP_CLIENT_WINDOW_SIZE\nimport okhttp3.testing.PlatformRule\nimport okio.BufferedSource\nimport okio.IOException\nimport okio.Path.Companion.toPath\nimport okio.fakefilesystem.FakeFileSystem\nimport okio.use\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Timeout(30)\nopen class TrailersTest {\n  private val fileSystem = FakeFileSystem()\n\n  @JvmField\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .cache(Cache(fileSystem, \"/cache/\".toPath(), Long.MAX_VALUE))\n      .build()\n\n  @Test\n  fun trailersHttp1() {\n    trailers(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun trailersHttp2() {\n    trailers(Protocol.HTTP_2)\n  }\n\n  private fun trailers(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"h1\", \"v1\")\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, \"Hello\")\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      val source = response.body.source()\n      assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n      assertThat(source.readUtf8()).isEqualTo(\"Hello\")\n      assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v2\"))\n      assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v2\")) // Idempotent.\n    }\n  }\n\n  @Test\n  fun trailersEmptyResponseBodyHttp1() {\n    trailersEmptyResponseBody(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun trailersEmptyResponseBodyHttp2() {\n    trailersEmptyResponseBody(Protocol.HTTP_2)\n  }\n\n  private fun trailersEmptyResponseBody(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, \"\")\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      val source = response.body.source()\n      assertThat(source.readUtf8()).isEqualTo(\"\")\n      assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v2\"))\n    }\n  }\n\n  @Test\n  fun trailersWithoutReadingFullResponseBodyHttp1() {\n    trailersWithoutReadingFullResponseBody(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun trailersWithoutReadingFullResponseBodyHttp2() {\n    trailersWithoutReadingFullResponseBody(Protocol.HTTP_2)\n  }\n\n  private fun trailersWithoutReadingFullResponseBody(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, \"Hello\")\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v2\"))\n      assertThat(response.body.source().exhausted()).isTrue()\n    }\n  }\n\n  @Test\n  @Disabled\n  fun trailersAndCacheHttp1() {\n    trailersAndCache(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  @Disabled\n  fun trailersAndCacheHttp2() {\n    trailersAndCache(Protocol.HTTP_2)\n  }\n\n  private fun trailersAndCache(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"h1\", \"v1\")\n        .addHeader(\"Cache-Control: max-age=30\")\n        .body(protocol, \"Hello\")\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .build(),\n    )\n\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    call1.execute().use { response ->\n      val source = response.body.source()\n      assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n      assertThat(source.readUtf8()).isEqualTo(\"Hello\")\n      assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v2\"))\n    }\n\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    call2.execute().use { response ->\n      val source = response.body.source()\n      assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n      assertThat(source.readUtf8()).isEqualTo(\"Hello\")\n      assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v2\"))\n    }\n  }\n\n  @Test\n  fun delayBeforeTrailersHttp1() {\n    delayBeforeTrailers(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun delayBeforeTrailersHttp2() {\n    trailers(Protocol.HTTP_2)\n  }\n\n  /** Confirm the client will block if necessary to consume trailers. */\n  private fun delayBeforeTrailers(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, \"Hello\")\n        .trailersDelay(500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      val source = response.body.source()\n      assertThat(source.readUtf8(5)).isEqualTo(\"Hello\")\n      val trailersDelay =\n        measureTime {\n          val trailers = response.trailers()\n          assertThat(trailers).isEqualTo(headersOf(\"t1\", \"v2\"))\n        }\n      assertThat(trailersDelay).isGreaterThan(250.milliseconds)\n    }\n  }\n\n  @Test\n  fun disconnectBeforeTrailersHttp1() {\n    disconnectBeforeTrailers(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun disconnectBeforeTrailersHttp2() {\n    disconnectBeforeTrailers(Protocol.HTTP_2)\n  }\n\n  /** Confirm we can get an [IOException] reading trailers. */\n  private fun disconnectBeforeTrailers(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, \"Hello\")\n        .onResponseBody(CloseSocket())\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      assertFailsWith<IOException> {\n        response.trailers()\n      }\n    }\n  }\n\n  @Test\n  fun cannotReadTrailersAfterEarlyResponseCloseHttp1() {\n    cannotReadTrailersAfterEarlyResponseClose(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun cannotReadTrailersAfterEarlyResponseCloseHttp2() {\n    cannotReadTrailersAfterEarlyResponseClose(Protocol.HTTP_2)\n  }\n\n  private fun cannotReadTrailersAfterEarlyResponseClose(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .bodyDelay(1, TimeUnit.SECONDS)\n        .body(protocol, \"Hello\")\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      response.close()\n      assertFailsWith<IOException> {\n        response.trailers()\n      }\n    }\n  }\n\n  @Test\n  fun readTrailersAfterEarlyEofAndCloseHttp1() {\n    readTrailersAfterEarlyEofAndClose(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun readTrailersAfterEarlyEofAndCloseHttp2() {\n    readTrailersAfterEarlyEofAndClose(Protocol.HTTP_2)\n  }\n\n  private fun readTrailersAfterEarlyEofAndClose(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, \"Hello\")\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      assertThat(response.body.source().readUtf8()).isEqualTo(\"Hello\")\n      response.body.source().close()\n      assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v2\"))\n    }\n  }\n\n  @Test\n  fun readEmptyTrailersHttp1EmptyFixedLengthResponse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"\")\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      assertThat(response.body.source().readUtf8()).isEqualTo(\"\")\n      assertThat(response.trailers()).isEqualTo(Headers.EMPTY)\n    }\n  }\n\n  @Test\n  fun readEmptyTrailersHttp1NonEmptyFixedLengthResponse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Hello\")\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      assertThat(response.body.source().readUtf8()).isEqualTo(\"Hello\")\n      assertThat(response.trailers()).isEqualTo(Headers.EMPTY)\n    }\n  }\n\n  @Test\n  fun readEmptyTrailersHttp1UnknownLengthResponse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Hello\")\n        .removeHeader(\"Content-Length\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      assertThat(response.headers[\"Content-Length\"]).isNull()\n      assertThat(response.body.source().readUtf8()).isEqualTo(\"Hello\")\n      assertThat(response.trailers()).isEqualTo(Headers.EMPTY)\n    }\n  }\n\n  @Test\n  fun cancelWhileReadingTrailersHttp1() {\n    cancelWhileReadingTrailers(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun cancelWhileReadingTrailersHttp2() {\n    cancelWhileReadingTrailers(Protocol.HTTP_2)\n  }\n\n  private fun cancelWhileReadingTrailers(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"h1\", \"v1\")\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, \"Hello\")\n        .trailersDelay(1, TimeUnit.SECONDS)\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      val source = response.body.source()\n      assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n      assertThat(source.readUtf8(5)).isEqualTo(\"Hello\")\n      call.cancelLater(500.milliseconds)\n      val trailersDelay =\n        measureTime {\n          val exception =\n            assertFailsWith<IOException> {\n              response.trailers()\n            }\n          assertThat(exception.message).isIn(\n            \"Socket closed\", // HTTP/1.1\n            \"stream was reset: CANCEL\", // HTTP/2\n          )\n        }\n      assertThat(trailersDelay).isGreaterThan(250.milliseconds)\n      assertThat(trailersDelay).isLessThan(750.milliseconds)\n    }\n  }\n\n  @Test\n  fun bufferResponseBodyAndReadTrailersHttp1() {\n    bufferResponseBodyAndReadTrailers(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun bufferResponseBodyAndReadTrailersHttp2() {\n    bufferResponseBodyAndReadTrailers(Protocol.HTTP_2)\n  }\n\n  private fun bufferResponseBodyAndReadTrailers(protocol: Protocol) {\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .trailers(headersOf(\"t1\", \"v1\"))\n        .body(protocol, \"Hello\")\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { originalResponse ->\n      val responseBodyData = originalResponse.body.byteString()\n      val responseTrailers = originalResponse.trailers()\n      assertThat(responseTrailers).isEqualTo(headersOf(\"t1\", \"v1\"))\n\n      val rewrittenResponse =\n        originalResponse\n          .newBuilder()\n          .body(responseBodyData.toResponseBody())\n          .build()\n      assertThat(rewrittenResponse.body.string()).isEqualTo(\"Hello\")\n      assertThat(rewrittenResponse.trailers()).isEqualTo(headersOf(\"t1\", \"v1\"))\n    }\n  }\n\n  /**\n   * We had a bug where a custom `ResponseBody` interacted poorly with `Response.trailers()`.\n   * Confirm custom trailers can be read without even accessing the response body.\n   */\n  @Test\n  fun customTrailersDoNotUseResponseBody() {\n    val response =\n      Response\n        .Builder()\n        .request(Request(url = \"https://example.com\".toHttpUrl()))\n        .protocol(Protocol.HTTP_1_1)\n        .code(200)\n        .message(\"OK\")\n        .body(\n          object : ResponseBody() {\n            override fun contentType(): MediaType? = null\n\n            override fun contentLength(): Long = -1L\n\n            override fun source(): BufferedSource = error(\"unexpected call\")\n          },\n        ).trailers(\n          object : TrailersSource {\n            override fun get(): Headers = headersOf(\"t1\", \"v1\")\n          },\n        ).build()\n\n    assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v1\"))\n  }\n\n  @Test\n  fun peekTrailersHttp1() {\n    peekTrailers(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun peekTrailersHttp2() {\n    peekTrailers(Protocol.HTTP_2)\n  }\n\n  private fun peekTrailers(protocol: Protocol) {\n    val responseBody = \"a\".repeat(OKHTTP_CLIENT_WINDOW_SIZE)\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"h1\", \"v1\")\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, responseBody)\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      val source = response.body.source()\n      assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n      assertThat(response.peekTrailers()).isNull()\n      assertThat(source.readUtf8()).isEqualTo(responseBody)\n      assertThat(response.peekTrailers()).isEqualTo(headersOf(\"t1\", \"v2\"))\n      assertThat(response.peekTrailers()).isEqualTo(headersOf(\"t1\", \"v2\")) // Idempotent.\n      assertThat(response.trailers()).isEqualTo(headersOf(\"t1\", \"v2\"))\n    }\n  }\n\n  @Test\n  fun trailersWithServerTruncatedResponseHttp1() {\n    trailersWithServerTruncatedResponse(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun trailersWithServerTruncatedResponseHttp2() {\n    trailersWithServerTruncatedResponse(Protocol.HTTP_2)\n  }\n\n  /**\n   * If the server closes the connection while the client is consuming the response body, attempts\n   * to peek or read the trailers should throw.\n   */\n  private fun trailersWithServerTruncatedResponse(protocol: Protocol) {\n    val responseBody = \"a\".repeat(OKHTTP_CLIENT_WINDOW_SIZE)\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"h1\", \"v1\")\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, responseBody)\n        .onResponseBody(CloseSocket())\n        .build(),\n    )\n\n    val call = client.newCall(Request(server.url(\"/\")))\n    call.execute().use { response ->\n      val source = response.body.source()\n      assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n      assertThat(response.peekTrailers()).isNull()\n      assertFailsWith<IOException> {\n        source.readUtf8()\n      }\n      try {\n        assertThat(response.peekTrailers()).isNull() // Okay. This is what HTTP/1 does.\n      } catch (_: IOException) {\n        // Also okay. This is what HTTP/2 does.\n      }\n      assertFailsWith<IOException> {\n        response.trailers()\n      }\n    }\n  }\n\n  @Test\n  fun trailersWithClientPrematureCloseHttp1() {\n    trailersWithClientPrematureClose(Protocol.HTTP_1_1)\n  }\n\n  @Test\n  fun trailersWithClientPrematureCloseHttp2() {\n    trailersWithClientPrematureClose(Protocol.HTTP_2)\n  }\n\n  /**\n   * If the client closes the connection while it is consuming the response body, attempts to peek\n   * or read the trailers should throw.\n   *\n   * This test needs to make two interventions to prevent OkHttp from reading the entire response\n   * body, which it will attempt to do otherwise:\n   *\n   *  * Don't cache the response. The cache will try to read the entire response body so that it\n   *    can successfully complete the cache entry.\n   *\n   *  * Throttle the response. The HTTP/1 connection pool will attempt to read the entire response\n   *    body so that it can pool the connection.\n   */\n  private fun trailersWithClientPrematureClose(protocol: Protocol) {\n    val halfResponseBody = \"a\".repeat(OKHTTP_CLIENT_WINDOW_SIZE)\n    enableProtocol(protocol)\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"h1\", \"v1\")\n        .trailers(headersOf(\"t1\", \"v2\"))\n        .body(protocol, halfResponseBody + halfResponseBody)\n        .throttleBody(\n          OKHTTP_CLIENT_WINDOW_SIZE.toLong(),\n          DISCARD_STREAM_TIMEOUT_MILLIS.toLong() + 1L,\n          TimeUnit.MILLISECONDS,\n        ).build(),\n    )\n\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/\"),\n          headers = headersOf(\"Cache-Control\", \"no-store\"),\n        ),\n      )\n    call.execute().use { response ->\n      val source = response.body.source()\n      assertThat(response.header(\"h1\")).isEqualTo(\"v1\")\n      assertThat(response.peekTrailers()).isNull()\n      assertThat(source.readUtf8(halfResponseBody.length.toLong())).isEqualTo(halfResponseBody)\n      source.close()\n      assertFailsWith<IllegalStateException> {\n        source.readUtf8()\n      }\n      assertFailsWith<IOException> {\n        response.peekTrailers()\n      }\n      assertFailsWith<IOException> {\n        response.trailers()\n      }\n    }\n  }\n\n  private fun MockResponse.Builder.body(\n    protocol: Protocol,\n    body: String,\n  ) = apply {\n    when (protocol) {\n      Protocol.HTTP_1_1 -> chunkedBody(body, 1024)\n\n      // Force multiple chunks.\n      else -> body(body)\n    }\n  }\n\n  private fun enableProtocol(protocol: Protocol) {\n    if (protocol == Protocol.HTTP_2) {\n      enableTls()\n      client =\n        client\n          .newBuilder()\n          .protocols(listOf(protocol, Protocol.HTTP_1_1))\n          .build()\n      server.protocols = client.protocols\n    }\n  }\n\n  private fun enableTls() {\n    val handshakeCertificates = platform.localhostHandshakeCertificates()\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n  }\n\n  private fun Call.cancelLater(delay: Duration) {\n    thread(name = \"canceler\") {\n      sleep(delay.inWholeMilliseconds)\n      cancel()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/URLConnectionTest.kt",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"ktlint:standard:filename\")\n\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isGreaterThanOrEqualTo\nimport assertk.assertions.isIn\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isLessThan\nimport assertk.assertions.isNotEmpty\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport assertk.fail\nimport java.io.File\nimport java.io.IOException\nimport java.io.InputStream\nimport java.net.ConnectException\nimport java.net.CookieManager\nimport java.net.HttpURLConnection\nimport java.net.InetAddress\nimport java.net.PasswordAuthentication\nimport java.net.ProtocolException\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.net.SocketAddress\nimport java.net.SocketException\nimport java.net.SocketTimeoutException\nimport java.net.URI\nimport java.net.URLConnection\nimport java.net.UnknownHostException\nimport java.security.KeyStore\nimport java.security.cert.CertificateException\nimport java.security.cert.X509Certificate\nimport java.time.Duration\nimport java.util.Arrays\nimport java.util.EnumSet\nimport java.util.Locale\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicReference\nimport java.util.zip.GZIPInputStream\nimport javax.net.SocketFactory\nimport javax.net.ssl.SSLException\nimport javax.net.ssl.SSLHandshakeException\nimport javax.net.ssl.SSLProtocolException\nimport javax.net.ssl.TrustManager\nimport javax.net.ssl.TrustManagerFactory\nimport javax.net.ssl.X509TrustManager\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Credentials.basic\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.TestUtil.assertSuppressed\nimport okhttp3.internal.RecordingAuthenticator\nimport okhttp3.internal.RecordingOkAuthenticator\nimport okhttp3.internal.USER_AGENT\nimport okhttp3.internal.addHeaderLenient\nimport okhttp3.internal.authenticator.JavaNetAuthenticator\nimport okhttp3.internal.http.HTTP_PERM_REDIRECT\nimport okhttp3.internal.http.HTTP_TEMP_REDIRECT\nimport okhttp3.internal.platform.Platform.Companion.get\nimport okhttp3.java.net.cookiejar.JavaNetCookieJar\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.GzipSink\nimport okio.buffer\nimport okio.utf8Size\nimport org.bouncycastle.tls.TlsFatalAlert\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junit.jupiter.api.io.TempDir\nimport org.opentest4j.TestAbortedException\n\n/** Android's URLConnectionTest, ported to exercise OkHttp's Call API.  */\n@Tag(\"Slow\")\nclass URLConnectionTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @TempDir\n  lateinit var tempDir: File\n\n  @StartStop\n  val server = MockWebServer()\n\n  @StartStop\n  val server2 = MockWebServer()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private var client = clientTestRule.newClient()\n  private var cache: Cache? = null\n\n  @BeforeEach\n  fun setUp() {\n    server.protocolNegotiationEnabled = false\n  }\n\n  @AfterEach\n  fun tearDown() {\n    java.net.Authenticator.setDefault(null)\n    System.clearProperty(\"proxyHost\")\n    System.clearProperty(\"proxyPort\")\n    System.clearProperty(\"http.proxyHost\")\n    System.clearProperty(\"http.proxyPort\")\n    System.clearProperty(\"https.proxyHost\")\n    System.clearProperty(\"https.proxyPort\")\n    if (cache != null) {\n      cache!!.delete()\n    }\n  }\n\n  @Test\n  fun requestHeaders() {\n    server.enqueue(MockResponse())\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .addHeader(\"D\", \"e\")\n        .addHeader(\"D\", \"f\")\n        .build()\n    assertThat(request.header(\"D\")).isEqualTo(\"f\")\n    assertThat(request.header(\"d\")).isEqualTo(\"f\")\n    val requestHeaders = request.headers\n    assertThat(LinkedHashSet(requestHeaders.values(\"D\"))).isEqualTo(newSet(\"e\", \"f\"))\n    assertThat(LinkedHashSet(requestHeaders.values(\"d\"))).isEqualTo(newSet(\"e\", \"f\"))\n    val response = getResponse(request)\n    response.close()\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers.values(\"D\")).isEqualTo(listOf(\"e\", \"f\"))\n    assertThat(recordedRequest.headers[\"G\"]).isNull()\n    assertThat(recordedRequest.headers[\"null\"]).isNull()\n  }\n\n  @Test\n  fun getRequestPropertyReturnsLastValue() {\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .addHeader(\"A\", \"value1\")\n        .addHeader(\"A\", \"value2\")\n        .build()\n    assertThat(request.header(\"A\")).isEqualTo(\"value2\")\n  }\n\n  @Test\n  fun responseHeaders() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.0 200 Fantastic\")\n        .addHeader(\"A: c\")\n        .addHeader(\"B: d\")\n        .addHeader(\"A: e\")\n        .chunkedBody(\"ABCDE\\nFGHIJ\\nKLMNO\\nPQR\", 8)\n        .build(),\n    )\n    val request = newRequest(\"/\")\n    val response = getResponse(request)\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.message).isEqualTo(\"Fantastic\")\n    val responseHeaders = response.headers\n    assertThat(LinkedHashSet(responseHeaders.values(\"A\"))).isEqualTo(newSet(\"c\", \"e\"))\n    assertThat(LinkedHashSet(responseHeaders.values(\"a\"))).isEqualTo(newSet(\"c\", \"e\"))\n    assertThat(responseHeaders.name(0)).isEqualTo(\"A\")\n    assertThat(responseHeaders.value(0)).isEqualTo(\"c\")\n    assertThat(responseHeaders.name(1)).isEqualTo(\"B\")\n    assertThat(responseHeaders.value(1)).isEqualTo(\"d\")\n    assertThat(responseHeaders.name(2)).isEqualTo(\"A\")\n    assertThat(responseHeaders.value(2)).isEqualTo(\"e\")\n    response.body.close()\n  }\n\n  @Test\n  fun serverSendsInvalidStatusLine() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTP/1.1 200 OK\")\n        .build(),\n    )\n    val request = newRequest(\"/\")\n    assertFailsWith<IOException> {\n      getResponse(request)\n    }\n  }\n\n  @Test\n  fun serverSendsInvalidCodeTooLarge() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 2147483648 OK\")\n        .build(),\n    )\n    val request = newRequest(\"/\")\n    assertFailsWith<IOException> {\n      getResponse(request)\n    }\n  }\n\n  @Test\n  fun serverSendsInvalidCodeNotANumber() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 00a OK\")\n        .build(),\n    )\n    val request = newRequest(\"/\")\n    assertFailsWith<IOException> {\n      getResponse(request)\n    }\n  }\n\n  @Test\n  fun serverSendsUnnecessaryWhitespace() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\" HTTP/1.1 2147483648 OK\")\n        .build(),\n    )\n    val request = newRequest(\"/\")\n    assertFailsWith<IOException> {\n      getResponse(request)\n    }\n  }\n\n  @Test\n  fun connectRetriesUntilConnectedOrFailed() {\n    val request = newRequest(\"/foo\")\n    server.close()\n    assertFailsWith<IOException> {\n      getResponse(request)\n    }\n  }\n\n  @Test\n  fun requestBodySurvivesRetriesWithFixedLength() {\n    testRequestBodySurvivesRetries(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun requestBodySurvivesRetriesWithChunkedStreaming() {\n    testRequestBodySurvivesRetries(TransferKind.CHUNKED)\n  }\n\n  private fun testRequestBodySurvivesRetries(transferKind: TransferKind) {\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    // Use a misconfigured proxy to guarantee that the request is retried.\n    client =\n      client\n        .newBuilder()\n        .proxySelector(\n          FakeProxySelector()\n            .addProxy(server2.proxyAddress)\n            .addProxy(Proxy.NO_PROXY),\n        ).build()\n    server2.close()\n    val request =\n      Request(\n        url = server.url(\"/def\"),\n        body = transferKind.newRequestBody(\"body\"),\n      )\n    val response = getResponse(request)\n    assertContent(\"abc\", response)\n    assertThat(server.takeRequest().body?.utf8()).isEqualTo(\"body\")\n  }\n\n  // Check that if we don't read to the end of a response, the next request on the\n  // recycled connection doesn't get the unread tail of the first request's response.\n  // http://code.google.com/p/android/issues/detail?id=2939\n  @Test\n  fun bug2939() {\n    val response =\n      MockResponse\n        .Builder()\n        .chunkedBody(\"ABCDE\\nFGHIJ\\nKLMNO\\nPQR\", 8)\n        .build()\n    server.enqueue(response)\n    server.enqueue(response)\n    val request = newRequest(\"/\")\n    val c1 = getResponse(request)\n    assertContent(\"ABCDE\", c1, 5)\n    val c2 = getResponse(request)\n    assertContent(\"ABCDE\", c2, 5)\n    c1.close()\n    c2.close()\n  }\n\n  @Test\n  fun connectionsArePooled() {\n    val response =\n      MockResponse(\n        body = \"ABCDEFGHIJKLMNOPQR\",\n      )\n    server.enqueue(response)\n    server.enqueue(response)\n    server.enqueue(response)\n    assertContent(\"ABCDEFGHIJKLMNOPQR\", getResponse(newRequest(\"/foo\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertContent(\"ABCDEFGHIJKLMNOPQR\", getResponse(newRequest(\"/bar?baz=quux\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertContent(\"ABCDEFGHIJKLMNOPQR\", getResponse(newRequest(\"/z\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun chunkedConnectionsArePooled() {\n    val response =\n      MockResponse\n        .Builder()\n        .chunkedBody(\"ABCDEFGHIJKLMNOPQR\", 5)\n        .build()\n    server.enqueue(response)\n    server.enqueue(response)\n    server.enqueue(response)\n    assertContent(\"ABCDEFGHIJKLMNOPQR\", getResponse(newRequest(\"/foo\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertContent(\"ABCDEFGHIJKLMNOPQR\", getResponse(newRequest(\"/bar?baz=quux\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertContent(\"ABCDEFGHIJKLMNOPQR\", getResponse(newRequest(\"/z\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun serverClosesSocket() {\n    testServerClosesOutput(\n      MockResponse\n        .Builder()\n        .body(\"This connection won't pool properly\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n  }\n\n  @Test\n  fun serverShutdownInput() {\n    testServerClosesOutput(\n      MockResponse\n        .Builder()\n        .body(\"This connection won't pool properly\")\n        .onResponseEnd(\n          CloseSocket(\n            closeSocket = false,\n            shutdownInput = true,\n          ),\n        ).build(),\n    )\n  }\n\n  @Test\n  fun serverShutdownOutput() {\n    testServerClosesOutput(\n      MockResponse\n        .Builder()\n        .body(\"This connection won't pool properly\")\n        .onResponseEnd(\n          CloseSocket(\n            closeSocket = false,\n            shutdownOutput = true,\n          ),\n        ).build(),\n    )\n  }\n\n  @Test\n  fun invalidHost() {\n    // Note that 1234.1.1.1 is an invalid host in a URI, but URL isn't as strict.\n    client =\n      client\n        .newBuilder()\n        .dns(FakeDns())\n        .build()\n    assertFailsWith<UnknownHostException> {\n      getResponse(\n        Request\n          .Builder()\n          .url(\"http://1234.1.1.1/index.html\".toHttpUrl())\n          .build(),\n      )\n    }\n  }\n\n  private fun testServerClosesOutput(mockResponse: MockResponse) {\n    server.enqueue(mockResponse)\n    val responseAfter = MockResponse(body = \"This comes after a busted connection\")\n    server.enqueue(responseAfter)\n    server.enqueue(responseAfter) // Enqueue 2x because the broken connection may be reused.\n    val response1 = getResponse(newRequest(\"/a\"))\n    response1.body\n      .source()\n      .timeout()\n      .timeout(100, TimeUnit.MILLISECONDS)\n    assertContent(\"This connection won't pool properly\", response1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // Give the server time to enact the socket policy if it's one that could happen after the\n    // client has received the response.\n    Thread.sleep(500)\n    val response2 = getResponse(newRequest(\"/b\"))\n    response1.body\n      .source()\n      .timeout()\n      .timeout(100, TimeUnit.MILLISECONDS)\n    assertContent(\"This comes after a busted connection\", response2)\n\n    // Check that a fresh connection was created, either immediately or after attempting reuse.\n    // We know that a fresh connection was created if the server recorded a request with sequence\n    // number 0. Since the client may have attempted to reuse the broken connection just before\n    // creating a fresh connection, the server may have recorded 2 requests at this point. The order\n    // of recording is non-deterministic.\n    val requestAfter = server.takeRequest()\n    assertThat(\n      requestAfter.exchangeIndex == 0 ||\n        server.requestCount == 3 &&\n        server.takeRequest().exchangeIndex == 0,\n    ).isTrue()\n  }\n\n  internal enum class WriteKind {\n    BYTE_BY_BYTE,\n    SMALL_BUFFERS,\n    LARGE_BUFFERS,\n  }\n\n  @Test\n  fun chunkedUpload_byteByByte() {\n    doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE)\n  }\n\n  @Test\n  fun chunkedUpload_smallBuffers() {\n    doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS)\n  }\n\n  @Test\n  fun chunkedUpload_largeBuffers() {\n    doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS)\n  }\n\n  @Test\n  fun fixedLengthUpload_byteByByte() {\n    doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE)\n  }\n\n  @Test\n  fun fixedLengthUpload_smallBuffers() {\n    doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS)\n  }\n\n  @Test\n  fun fixedLengthUpload_largeBuffers() {\n    doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS)\n  }\n\n  private fun doUpload(\n    uploadKind: TransferKind,\n    writeKind: WriteKind,\n  ) {\n    val n = 512 * 1024\n    server.bodyLimit = 0\n    server.enqueue(MockResponse())\n    val requestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = if (uploadKind === TransferKind.CHUNKED) -1L else n.toLong()\n\n        override fun writeTo(sink: BufferedSink) {\n          if (writeKind == WriteKind.BYTE_BY_BYTE) {\n            for (i in 0 until n) {\n              sink.writeByte('x'.code)\n            }\n          } else {\n            val buf = ByteArray(if (writeKind == WriteKind.SMALL_BUFFERS) 256 else 64 * 1024)\n            Arrays.fill(buf, 'x'.code.toByte())\n            var i = 0\n            while (i < n) {\n              sink.write(buf, 0, Math.min(buf.size, n - i))\n              i += buf.size\n            }\n          }\n        }\n      }\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = requestBody,\n        ),\n      )\n    assertThat(response.code).isEqualTo(200)\n    val request = server.takeRequest()\n    assertThat(request.bodySize).isEqualTo(n.toLong())\n    if (uploadKind === TransferKind.CHUNKED) {\n      assertThat(request.chunkSizes!!).isNotEmpty()\n    } else {\n      assertThat(request.chunkSizes).isNull()\n    }\n  }\n\n  @Test\n  fun connectViaHttps() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse(body = \"this response comes via HTTPS\"))\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val response = getResponse(newRequest(\"/foo\"))\n    assertContent(\"this response comes via HTTPS\", response)\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n  }\n\n  @Test\n  fun connectViaHttpsReusingConnections() {\n    connectViaHttpsReusingConnections(false)\n  }\n\n  @Test\n  fun connectViaHttpsReusingConnectionsAfterRebuildingClient() {\n    connectViaHttpsReusingConnections(true)\n  }\n\n  private fun connectViaHttpsReusingConnections(rebuildClient: Boolean) {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse(body = \"this response comes via HTTPS\"))\n    server.enqueue(MockResponse(body = \"another response via HTTPS\"))\n\n    // The pool will only reuse sockets if the SSL socket factories are the same.\n    val clientSocketFactory = handshakeCertificates.sslSocketFactory()\n    val hostnameVerifier = RecordingHostnameVerifier()\n    val cookieJar: CookieJar = JavaNetCookieJar(CookieManager())\n    val connectionPool = ConnectionPool()\n    client =\n      OkHttpClient\n        .Builder()\n        .cache(cache)\n        .connectionPool(connectionPool)\n        .cookieJar(cookieJar)\n        .sslSocketFactory(clientSocketFactory, handshakeCertificates.trustManager)\n        .hostnameVerifier(hostnameVerifier)\n        .build()\n    val response1 = getResponse(newRequest(\"/\"))\n    assertContent(\"this response comes via HTTPS\", response1)\n    if (rebuildClient) {\n      client =\n        OkHttpClient\n          .Builder()\n          .cache(cache)\n          .connectionPool(connectionPool)\n          .cookieJar(cookieJar)\n          .sslSocketFactory(clientSocketFactory, handshakeCertificates.trustManager)\n          .hostnameVerifier(hostnameVerifier)\n          .build()\n    }\n    val response2 = getResponse(newRequest(\"/\"))\n    assertContent(\"another response via HTTPS\", response2)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun connectViaHttpsReusingConnectionsDifferentFactories() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse(body = \"this response comes via HTTPS\"))\n    server.enqueue(MockResponse(body = \"another response via HTTPS\"))\n\n    // install a custom SSL socket factory so the server can be authorized\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val response1 = getResponse(newRequest(\"/\"))\n    assertContent(\"this response comes via HTTPS\", response1)\n    val sslContext2 = get().newSSLContext()\n    sslContext2.init(null, null, null)\n    val sslSocketFactory2 = sslContext2.socketFactory\n    val trustManagerFactory =\n      TrustManagerFactory.getInstance(\n        TrustManagerFactory.getDefaultAlgorithm(),\n      )\n    trustManagerFactory.init(null as KeyStore?)\n    val trustManager = trustManagerFactory.trustManagers[0] as X509TrustManager\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(sslSocketFactory2, trustManager)\n        .build()\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/\"))\n    }.also { expected ->\n      when (expected) {\n        is SSLException, is TlsFatalAlert -> {}\n\n        else -> {\n          throw expected\n        }\n      }\n    }\n  }\n\n  // TODO(jwilson): tests below this marker need to be migrated to OkHttp's request/response API.\n  @Test\n  fun connectViaHttpsWithSSLFallback() {\n    platform.assumeNotBouncyCastle()\n\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    server.enqueue(MockResponse(body = \"this response comes via SSL\"))\n    client =\n      client\n        .newBuilder()\n        .hostnameVerifier(\n          RecordingHostnameVerifier(),\n        ) // Attempt RESTRICTED_TLS then fall back to MODERN_TLS.\n        .connectionSpecs(Arrays.asList(ConnectionSpec.RESTRICTED_TLS, ConnectionSpec.MODERN_TLS))\n        .sslSocketFactory(\n          suppressTlsFallbackClientSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    val response = getResponse(newRequest(\"/foo\"))\n    assertContent(\"this response comes via SSL\", response)\n    val failHandshakeRequest = server.takeRequest()\n    assertThat(failHandshakeRequest.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    val fallbackRequest = server.takeRequest()\n    assertThat(fallbackRequest.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n    assertThat(fallbackRequest.handshake?.tlsVersion).isIn(TlsVersion.TLS_1_2, TlsVersion.TLS_1_3)\n  }\n\n  @Test\n  fun connectViaHttpsWithSSLFallbackFailuresRecorded() {\n    platform.assumeNotBouncyCastle()\n\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    client =\n      client\n        .newBuilder()\n        .connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .sslSocketFactory(\n          suppressTlsFallbackClientSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/foo\"))\n    }.also { expected ->\n      expected.assertSuppressed { throwables: List<Throwable>? ->\n        assertThat(throwables!!.size).isEqualTo(1)\n      }\n    }\n  }\n\n  /**\n   * When a pooled connection fails, don't blame the route. Otherwise pooled connection failures can\n   * cause unnecessary SSL fallbacks.\n   *\n   * https://github.com/square/okhttp/issues/515\n   */\n  @Test\n  fun sslFallbackNotUsedWhenRecycledConnectionFails() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"def\"))\n    client =\n      client\n        .newBuilder()\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .sslSocketFactory(\n          suppressTlsFallbackClientSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    assertContent(\"abc\", getResponse(newRequest(\"/\")))\n\n    // Give the server time to disconnect.\n    Thread.sleep(500)\n    assertContent(\"def\", getResponse(newRequest(\"/\")))\n    val tlsVersions: Set<TlsVersion?> =\n      EnumSet.of(\n        TlsVersion.TLS_1_0,\n        TlsVersion.TLS_1_2,\n        TlsVersion.TLS_1_3,\n      ) // v1.2 on OpenJDK 8.\n    val request1 = server.takeRequest()\n    assertThat(tlsVersions).contains(request1.handshake?.tlsVersion)\n    val request2 = server.takeRequest()\n    assertThat(tlsVersions).contains(request2.handshake?.tlsVersion)\n  }\n\n  /**\n   * Verify that we don't retry connections on certificate verification errors.\n   *\n   * http://code.google.com/p/android/issues/detail?id=13178\n   */\n  @Flaky\n  @Test\n  fun connectViaHttpsToUntrustedServer() {\n    // Flaky https://github.com/square/okhttp/issues/5222\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse()) // unused\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/foo\"))\n    }.also { expected ->\n      when (expected) {\n        is SSLHandshakeException -> {\n          // Allow conscrypt to fail in different ways\n          if (!platform.isConscrypt()) {\n            assertThat(expected.cause!!).isInstanceOf<CertificateException>()\n          }\n        }\n\n        is TlsFatalAlert -> {}\n\n        else -> {\n          throw expected\n        }\n      }\n    }\n    assertThat(server.requestCount).isEqualTo(0)\n  }\n\n  @Test\n  fun connectViaProxyUsingProxyArg() {\n    testConnectViaProxy(ProxyConfig.CREATE_ARG)\n  }\n\n  @Test\n  fun connectViaProxyUsingProxySystemProperty() {\n    testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY)\n  }\n\n  @Test\n  fun connectViaProxyUsingHttpProxySystemProperty() {\n    testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY)\n  }\n\n  private fun testConnectViaProxy(proxyConfig: ProxyConfig) {\n    server.enqueue(\n      MockResponse(body = \"this response comes via a proxy\"),\n    )\n    val url = \"http://android.com/foo\".toHttpUrl()\n    val response = proxyConfig.connect(server, client, url).execute()\n    assertContent(\"this response comes via a proxy\", response)\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\n      \"GET http://android.com/foo HTTP/1.1\",\n    )\n    assertThat(request.headers[\"Host\"]).isEqualTo(\"android.com\")\n  }\n\n  @Test\n  fun contentDisagreesWithContentLengthHeaderBodyTooLong() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\\r\\nYOU SHOULD NOT SEE THIS\")\n        .clearHeaders()\n        .addHeader(\"Content-Length: 3\")\n        .build(),\n    )\n    assertContent(\"abc\", getResponse(newRequest(\"/\")))\n  }\n\n  @Test\n  fun contentDisagreesWithContentLengthHeaderBodyTooShort() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .setHeader(\"Content-Length\", \"5\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    assertFailsWith<ProtocolException> {\n      val response = getResponse(newRequest(\"/\"))\n      response.body.source().readUtf8(5)\n    }\n  }\n\n  private fun testConnectViaSocketFactory(useHttps: Boolean) {\n    val uselessSocketFactory: SocketFactory =\n      object : SocketFactory() {\n        override fun createSocket(): Socket = throw IllegalArgumentException(\"useless\")\n\n        override fun createSocket(\n          host: InetAddress,\n          port: Int,\n        ): Socket? = null\n\n        override fun createSocket(\n          address: InetAddress,\n          port: Int,\n          localAddress: InetAddress,\n          localPort: Int,\n        ): Socket? = null\n\n        override fun createSocket(\n          host: String,\n          port: Int,\n        ): Socket? = null\n\n        override fun createSocket(\n          host: String,\n          port: Int,\n          localHost: InetAddress,\n          localPort: Int,\n        ): Socket? = null\n      }\n    if (useHttps) {\n      server.useHttps(handshakeCertificates.sslSocketFactory())\n      client =\n        client\n          .newBuilder()\n          .sslSocketFactory(\n            handshakeCertificates.sslSocketFactory(),\n            handshakeCertificates.trustManager,\n          ).hostnameVerifier(RecordingHostnameVerifier())\n          .build()\n    }\n    server.enqueue(MockResponse())\n    client =\n      client\n        .newBuilder()\n        .socketFactory(uselessSocketFactory)\n        .build()\n    assertFailsWith<IllegalArgumentException> {\n      getResponse(newRequest(\"/\"))\n    }\n    client =\n      client\n        .newBuilder()\n        .socketFactory(SocketFactory.getDefault())\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(200)\n  }\n\n  @Test\n  fun connectHttpViaSocketFactory() {\n    testConnectViaSocketFactory(false)\n  }\n\n  @Test\n  fun connectHttpsViaSocketFactory() {\n    testConnectViaSocketFactory(true)\n  }\n\n  @Test\n  fun contentDisagreesWithChunkedHeaderBodyTooLong() {\n    val builder =\n      MockResponse\n        .Builder()\n        .chunkedBody(\"abc\", 3)\n    val buffer = Buffer()\n    builder.body!!.writeTo(buffer)\n    buffer.writeUtf8(\"\\r\\nYOU SHOULD NOT SEE THIS\")\n    builder.body(buffer)\n    builder.clearHeaders()\n    builder.addHeader(\"Transfer-encoding: chunked\")\n    server.enqueue(builder.build())\n    assertContent(\"abc\", getResponse(newRequest(\"/\")))\n  }\n\n  @Test\n  fun contentDisagreesWithChunkedHeaderBodyTooShort() {\n    val builder =\n      MockResponse\n        .Builder()\n        .chunkedBody(\"abcdefg\", 5)\n    val fullBody = Buffer()\n    builder.body!!.writeTo(fullBody)\n    val truncatedBody = Buffer()\n    truncatedBody.write(fullBody, 4)\n    builder.body(truncatedBody)\n    builder.clearHeaders()\n    builder.addHeader(\"Transfer-encoding: chunked\")\n    builder.onResponseEnd(ShutdownConnection)\n    server.enqueue(builder.build())\n    assertFailsWith<IOException> {\n      val response = getResponse(newRequest(\"/\"))\n      response.body.source().readUtf8(7)\n    }\n  }\n\n  @Test\n  fun connectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() {\n    testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY)\n  }\n\n  @Test\n  fun connectViaHttpProxyToHttpsUsingHttpProxySystemProperty() {\n    // https should not use http proxy\n    testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY)\n  }\n\n  private fun testConnectViaDirectProxyToHttps(proxyConfig: ProxyConfig) {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse(body = \"this response comes via HTTPS\"),\n    )\n    val url = server.url(\"/foo\")\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val call = proxyConfig.connect(server, client, url)\n    assertContent(\"this response comes via HTTPS\", call.execute())\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n  }\n\n  @Test\n  fun connectViaHttpProxyToHttpsUsingProxyArg() {\n    testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG)\n  }\n\n  /**\n   * We weren't honoring all of the appropriate proxy system properties when connecting via HTTPS.\n   * http://b/3097518\n   */\n  @Test\n  fun connectViaHttpProxyToHttpsUsingProxySystemProperty() {\n    testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY)\n  }\n\n  @Test\n  fun connectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() {\n    testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY)\n  }\n\n  /**\n   * We were verifying the wrong hostname when connecting to an HTTPS site through a proxy.\n   * http://b/3097277\n   */\n  private fun testConnectViaHttpProxyToHttps(proxyConfig: ProxyConfig) {\n    val hostnameVerifier = RecordingHostnameVerifier()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"this response comes via a secure proxy\"))\n    val url = \"https://android.com/foo\".toHttpUrl()\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(hostnameVerifier)\n        .build()\n    val call = proxyConfig.connect(server, client, url)\n    assertContent(\"this response comes via a secure proxy\", call.execute())\n    val connect = server.takeRequest()\n    assertThat(connect.requestLine, \"Connect line failure on proxy\")\n      .isEqualTo(\"CONNECT android.com:443 HTTP/1.1\")\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\"android.com:443\")\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n    assertThat(get.headers[\"Host\"]).isEqualTo(\"android.com\")\n    assertThat(hostnameVerifier.calls).isEqualTo(\n      Arrays.asList(\"verify android.com\"),\n    )\n  }\n\n  /** Tolerate bad https proxy response when using HttpResponseCache. Android bug 6754912.  */\n  @Test\n  fun connectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() {\n    initResponseCache()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    // The inclusion of a body in the response to a CONNECT is key to reproducing b/6754912.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .body(\"bogus proxy connect response content\")\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"response\"))\n\n    // Configure a single IP address for the host and a single configuration, so we only need one\n    // failure to fail permanently.\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).connectionSpecs(listOf(ConnectionSpec.MODERN_TLS))\n        .hostnameVerifier(RecordingHostnameVerifier())\n        .proxy(server.proxyAddress)\n        .build()\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(\"https://android.com/foo\".toHttpUrl())\n          .build(),\n      )\n    assertContent(\"response\", response)\n    val connect = server.takeRequest()\n    assertThat(connect.requestLine).isEqualTo(\n      \"CONNECT android.com:443 HTTP/1.1\",\n    )\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\"android.com:443\")\n  }\n\n  private fun initResponseCache() {\n    cache = Cache(tempDir, Int.MAX_VALUE.toLong())\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n  }\n\n  /** Test which headers are sent unencrypted to the HTTP proxy.  */\n  @Test\n  fun proxyConnectIncludesProxyHeadersOnly() {\n    val hostnameVerifier = RecordingHostnameVerifier()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"encrypted response from the origin server\"))\n    client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(hostnameVerifier)\n        .build()\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(\"https://android.com/foo\".toHttpUrl())\n          .header(\"Private\", \"Secret\")\n          .header(\"Proxy-Authorization\", \"bar\")\n          .header(\"User-Agent\", \"baz\")\n          .build(),\n      )\n    assertContent(\"encrypted response from the origin server\", response)\n    val connect = server.takeRequest()\n    assertThat(connect.headers[\"Private\"]).isNull()\n    assertThat(connect.headers[\"Proxy-Authorization\"]).isNull()\n    assertThat(connect.headers[\"User-Agent\"]).isEqualTo(USER_AGENT)\n    assertThat(connect.headers[\"Host\"]).isEqualTo(\"android.com:443\")\n    assertThat(connect.headers[\"Proxy-Connection\"]).isEqualTo(\"Keep-Alive\")\n    val get = server.takeRequest()\n    assertThat(get.headers[\"Private\"]).isEqualTo(\"Secret\")\n    assertThat(hostnameVerifier.calls).isEqualTo(listOf(\"verify android.com\"))\n  }\n\n  @Test\n  fun proxyAuthenticateOnConnect() {\n    java.net.Authenticator.setDefault(RecordingAuthenticator())\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(407)\n        .headers(headersOf(\"Proxy-Authenticate\", \"Basic realm=\\\"localhost\\\"\"))\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"A\"))\n    client =\n      client\n        .newBuilder()\n        .proxyAuthenticator(Authenticator.JAVA_NET_AUTHENTICATOR)\n        .proxy(server.proxyAddress)\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(\"https://android.com/foo\".toHttpUrlOrNull()!!)\n          .build(),\n      )\n    assertContent(\"A\", response)\n    val connect1 = server.takeRequest()\n    assertThat(connect1.requestLine).isEqualTo(\"CONNECT android.com:443 HTTP/1.1\")\n    assertThat(connect1.headers[\"Proxy-Authorization\"]).isNull()\n    val connect2 = server.takeRequest()\n    assertThat(connect2.requestLine).isEqualTo(\"CONNECT android.com:443 HTTP/1.1\")\n    assertThat(connect2.headers[\"Proxy-Authorization\"])\n      .isEqualTo(\"Basic ${RecordingAuthenticator.BASE_64_CREDENTIALS}\")\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n    assertThat(get.headers[\"Proxy-Authorization\"]).isNull()\n  }\n\n  // Don't disconnect after building a tunnel with CONNECT\n  // http://code.google.com/p/android/issues/detail?id=37221\n  @Test\n  fun proxyWithConnectionClose() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"this response comes via a proxy\"))\n    client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(\"https://android.com/foo\")\n          .header(\"Connection\", \"close\")\n          .build(),\n      )\n    assertContent(\"this response comes via a proxy\", response)\n  }\n\n  @Test\n  fun proxyWithConnectionReuse() {\n    val socketFactory = handshakeCertificates.sslSocketFactory()\n    val hostnameVerifier = RecordingHostnameVerifier()\n    server.useHttps(socketFactory)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"response 1\"))\n    server.enqueue(MockResponse(body = \"response 2\"))\n    client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .sslSocketFactory(socketFactory, handshakeCertificates.trustManager)\n        .hostnameVerifier(hostnameVerifier)\n        .build()\n    assertContent(\"response 1\", getResponse(Request(\"https://android.com/foo\".toHttpUrl())))\n    assertContent(\"response 2\", getResponse(Request(\"https://android.com/foo\".toHttpUrl())))\n  }\n\n  @Test\n  fun proxySelectorHttpWithConnectionReuse() {\n    server.enqueue(\n      MockResponse(body = \"response 1\"),\n    )\n    server.enqueue(\n      MockResponse(code = 407),\n    )\n    client =\n      client\n        .newBuilder()\n        .proxySelector(\n          object : ProxySelector() {\n            override fun select(uri: URI): List<Proxy> = listOf(server.proxyAddress)\n\n            override fun connectFailed(\n              uri: URI,\n              socketAddress: SocketAddress,\n              e: IOException,\n            ) {\n            }\n          },\n        ).build()\n    val url = \"http://android.com/foo\".toHttpUrl()\n    assertContent(\"response 1\", getResponse(Request(url)))\n    assertThat(getResponse(Request(url)).code).isEqualTo(407)\n  }\n\n  @Test\n  fun disconnectedConnection() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .throttleBody(2, 100, TimeUnit.MILLISECONDS)\n        .body(\"ABCD\")\n        .build(),\n    )\n    val call = client.newCall(newRequest(\"/\"))\n    val response = call.execute()\n    val inputStream = response.body.byteStream()\n    assertThat(inputStream.read().toChar()).isEqualTo('A')\n    call.cancel()\n    assertFailsWith<IOException> {\n      // Reading 'B' may succeed if it's buffered.\n      inputStream.read()\n\n      // But 'C' shouldn't be buffered (the response is throttled) and this should fail.\n      inputStream.read()\n    }\n    inputStream.close()\n  }\n\n  @Test\n  fun disconnectDuringConnect_cookieJar() {\n    val callReference = AtomicReference<Call>()\n\n    class DisconnectingCookieJar : CookieJar {\n      override fun saveFromResponse(\n        url: HttpUrl,\n        cookies: List<Cookie>,\n      ) {}\n\n      override fun loadForRequest(url: HttpUrl): List<Cookie> {\n        callReference.get().cancel()\n        return emptyList()\n      }\n    }\n    client =\n      client\n        .newBuilder()\n        .cookieJar(DisconnectingCookieJar())\n        .build()\n    val call = client.newCall(newRequest(\"/\"))\n    callReference.set(call)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Canceled\")\n    }\n  }\n\n  @Test\n  fun disconnectBeforeConnect() {\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    val call = client.newCall(newRequest(\"/\"))\n    call.cancel()\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n  }\n\n  @Test\n  fun defaultRequestProperty() {\n    URLConnection.setDefaultRequestProperty(\"X-testSetDefaultRequestProperty\", \"A\")\n    assertThat(URLConnection.getDefaultRequestProperty(\"X-setDefaultRequestProperty\")).isNull()\n  }\n\n  /**\n   * Reads `count` characters from the stream. If the stream is exhausted before `count`\n   * characters can be read, the remaining characters are returned and the stream is closed.\n   */\n  private fun readAscii(\n    inputStream: InputStream,\n    count: Int,\n  ): String {\n    val result = StringBuilder()\n    for (i in 0 until count) {\n      val value = inputStream.read()\n      if (value == -1) {\n        inputStream.close()\n        break\n      }\n      result.append(value.toChar())\n    }\n    return result.toString()\n  }\n\n  @Test\n  fun markAndResetWithContentLengthHeader() {\n    testMarkAndReset(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun markAndResetWithChunkedEncoding() {\n    testMarkAndReset(TransferKind.CHUNKED)\n  }\n\n  @Test\n  fun markAndResetWithNoLengthHeaders() {\n    testMarkAndReset(TransferKind.END_OF_STREAM)\n  }\n\n  private fun testMarkAndReset(transferKind: TransferKind) {\n    val builder = MockResponse.Builder()\n    transferKind.setBody(builder, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\", 1024)\n    server.enqueue(builder.build())\n    server.enqueue(builder.build())\n    val inputStream = getResponse(newRequest(\"/\")).body.byteStream()\n    assertThat(inputStream.markSupported(), \"This implementation claims to support mark().\")\n      .isFalse()\n    inputStream.mark(5)\n    assertThat(readAscii(inputStream, 5)).isEqualTo(\"ABCDE\")\n    assertFailsWith<IOException> {\n      inputStream.reset()\n    }\n    assertThat(readAscii(inputStream, Int.MAX_VALUE)).isEqualTo(\n      \"FGHIJKLMNOPQRSTUVWXYZ\",\n    )\n    inputStream.close()\n    assertContent(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\", getResponse(newRequest(\"/\")))\n  }\n\n  /**\n   * We've had a bug where we forget the HTTP response when we see response code 401. This causes a\n   * new HTTP request to be issued for every call into the URLConnection.\n   */\n  @Test\n  fun unauthorizedResponseHandling() {\n    val mockResponse =\n      MockResponse(\n        code = HttpURLConnection.HTTP_UNAUTHORIZED,\n        headers = headersOf(\"WWW-Authenticate\", \"challenge\"),\n        body = \"Unauthorized\",\n      )\n    server.enqueue(mockResponse)\n    server.enqueue(mockResponse)\n    server.enqueue(mockResponse)\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(401)\n    assertThat(response.code).isEqualTo(401)\n    assertThat(response.code).isEqualTo(401)\n    assertThat(server.requestCount).isEqualTo(1)\n    response.body.close()\n  }\n\n  @Test\n  fun nonHexChunkSize() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"5\\r\\nABCDE\\r\\nG\\r\\nFGHIJKLMNOPQRSTU\\r\\n0\\r\\n\\r\\n\")\n        .clearHeaders()\n        .addHeader(\"Transfer-encoding: chunked\")\n        .build(),\n    )\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/\")).use { response ->\n        response.body.string()\n      }\n    }\n  }\n\n  @Test\n  fun malformedChunkSize() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"5:x\\r\\nABCDE\\r\\n0\\r\\n\\r\\n\")\n        .clearHeaders()\n        .addHeader(\"Transfer-encoding: chunked\")\n        .build(),\n    )\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/\")).use { response ->\n        readAscii(response.body.byteStream(), Int.MAX_VALUE)\n      }\n    }\n  }\n\n  @Test\n  fun extensionAfterChunkSize() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"5;x\\r\\nABCDE\\r\\n0\\r\\n\\r\\n\")\n        .clearHeaders()\n        .addHeader(\"Transfer-encoding: chunked\")\n        .build(),\n    )\n    getResponse(newRequest(\"/\")).use { response -> assertContent(\"ABCDE\", response) }\n  }\n\n  @Test\n  fun missingChunkBody() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"5\")\n        .clearHeaders()\n        .addHeader(\"Transfer-encoding: chunked\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/\")).use { response ->\n        readAscii(response.body.byteStream(), Int.MAX_VALUE)\n      }\n    }\n  }\n\n  /**\n   * This test checks whether connections are gzipped by default. This behavior in not required by\n   * the API, so a failure of this test does not imply a bug in the implementation.\n   */\n  @Test\n  fun gzipEncodingEnabledByDefault() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(gzip(\"ABCABCABC\"))\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\n      \"ABCABCABC\",\n    )\n    assertThat(response.header(\"Content-Encoding\")).isNull()\n    assertThat(response.body.contentLength()).isEqualTo(-1L)\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Accept-Encoding\"]).isEqualTo(\"gzip\")\n  }\n\n  @Test\n  fun clientConfiguredGzipContentEncoding() {\n    val bodyBytes = gzip(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(bodyBytes)\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .header(\"Accept-Encoding\", \"gzip\")\n          .build(),\n      )\n    val gunzippedIn: InputStream = GZIPInputStream(response.body.byteStream())\n    assertThat(readAscii(gunzippedIn, Int.MAX_VALUE)).isEqualTo(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n    assertThat(response.body.contentLength()).isEqualTo(bodyBytes.size)\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Accept-Encoding\"]).isEqualTo(\"gzip\")\n  }\n\n  @Test\n  fun gzipAndConnectionReuseWithFixedLength() {\n    testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH, false)\n  }\n\n  @Test\n  fun gzipAndConnectionReuseWithChunkedEncoding() {\n    testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED, false)\n  }\n\n  @Test\n  fun gzipAndConnectionReuseWithFixedLengthAndTls() {\n    testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH, true)\n  }\n\n  @Test\n  fun gzipAndConnectionReuseWithChunkedEncodingAndTls() {\n    testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED, true)\n  }\n\n  @Test\n  fun clientConfiguredCustomContentEncoding() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Encoding\", \"custom\"),\n        body = \"ABCDE\",\n      ),\n    )\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .header(\"Accept-Encoding\", \"custom\")\n          .build(),\n      )\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\"ABCDE\")\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Accept-Encoding\"]).isEqualTo(\"custom\")\n  }\n\n  /**\n   * Test a bug where gzip input streams weren't exhausting the input stream, which corrupted the\n   * request that followed or prevented connection reuse. http://code.google.com/p/android/issues/detail?id=7059\n   * http://code.google.com/p/android/issues/detail?id=38817\n   */\n  private fun testClientConfiguredGzipContentEncodingAndConnectionReuse(\n    transferKind: TransferKind,\n    tls: Boolean,\n  ) {\n    if (tls) {\n      val socketFactory = handshakeCertificates.sslSocketFactory()\n      val hostnameVerifier = RecordingHostnameVerifier()\n      server.useHttps(socketFactory)\n      client =\n        client\n          .newBuilder()\n          .sslSocketFactory(socketFactory, handshakeCertificates.trustManager)\n          .hostnameVerifier(hostnameVerifier)\n          .build()\n    }\n    val responseOne =\n      MockResponse\n        .Builder()\n        .addHeader(\"Content-Encoding: gzip\")\n    transferKind.setBody(responseOne, gzip(\"one (gzipped)\"), 5)\n    server.enqueue(responseOne.build())\n    val responseTwo = MockResponse.Builder()\n    transferKind.setBody(responseTwo, \"two (identity)\", 5)\n    server.enqueue(responseTwo.build())\n    val response1 =\n      getResponse(\n        Request\n          .Builder()\n          .header(\"Accept-Encoding\", \"gzip\")\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val gunzippedIn: InputStream = GZIPInputStream(response1.body.byteStream())\n    assertThat(readAscii(gunzippedIn, Int.MAX_VALUE)).isEqualTo(\"one (gzipped)\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    val response2 =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    assertThat(readAscii(response2.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\"two (identity)\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun transparentGzipWorksAfterExceptionRecovery() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"a\")\n        .onResponseEnd(\n          CloseSocket(\n            closeSocket = false,\n            shutdownInput = true,\n          ),\n        ).build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Content-Encoding: gzip\")\n        .body(gzip(\"b\"))\n        .build(),\n    )\n\n    // Seed the pool with a bad connection.\n    assertContent(\"a\", getResponse(newRequest(\"/\")))\n\n    // Give the server time to disconnect.\n    Thread.sleep(500)\n\n    // This connection will need to be recovered. When it is, transparent gzip should still work!\n    assertContent(\"b\", getResponse(newRequest(\"/\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Connection is not pooled.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun endOfStreamResponseIsNotPooled() {\n    client.connectionPool.evictAll()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"{}\")\n        .clearHeaders()\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertContent(\"{}\", response)\n    assertThat(client.connectionPool.idleConnectionCount()).isEqualTo(0)\n  }\n\n  @Test\n  fun earlyDisconnectDoesntHarmPoolingWithChunkedEncoding() {\n    testEarlyDisconnectDoesntHarmPooling(TransferKind.CHUNKED)\n  }\n\n  @Test\n  fun earlyDisconnectDoesntHarmPoolingWithFixedLengthEncoding() {\n    testEarlyDisconnectDoesntHarmPooling(TransferKind.FIXED_LENGTH)\n  }\n\n  private fun testEarlyDisconnectDoesntHarmPooling(transferKind: TransferKind) {\n    val mockResponse1 = MockResponse.Builder()\n    transferKind.setBody(mockResponse1, \"ABCDEFGHIJK\", 1024)\n    server.enqueue(mockResponse1.build())\n    val mockResponse2 = MockResponse.Builder()\n    transferKind.setBody(mockResponse2, \"LMNOPQRSTUV\", 1024)\n    server.enqueue(mockResponse2.build())\n    val call1 = client.newCall(newRequest(\"/\"))\n    val response1 = call1.execute()\n    val in1 = response1.body.byteStream()\n    assertThat(readAscii(in1, 5)).isEqualTo(\"ABCDE\")\n    in1.close()\n    call1.cancel()\n    val call2 = client.newCall(newRequest(\"/\"))\n    val response2 = call2.execute()\n    val in2 = response2.body.byteStream()\n    assertThat(readAscii(in2, 5)).isEqualTo(\"LMNOP\")\n    in2.close()\n    call2.cancel()\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Connection is pooled!\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun streamDiscardingIsTimely() {\n    // This response takes at least a full second to serve: 10,000 bytes served 100 bytes at a time.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(Buffer().write(ByteArray(10000)))\n        .throttleBody(100, 10, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    val startNanos = System.nanoTime()\n    val connection1 = getResponse(newRequest(\"/\"))\n    val inputStream = connection1.body.byteStream()\n    inputStream.close()\n    val elapsedNanos = System.nanoTime() - startNanos\n    val elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos)\n\n    // If we're working correctly, this should be greater than 100ms, but less than double that.\n    // Previously we had a bug where we would download the entire response body as long as no\n    // individual read took longer than 100ms.\n    assertThat(elapsedMillis).isLessThan(500L)\n\n    // Do another request to confirm that the discarded connection was not pooled.\n    assertContent(\"A\", getResponse(newRequest(\"/\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Connection is not pooled.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun setChunkedStreamingMode() {\n    server.enqueue(MockResponse())\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = TransferKind.CHUNKED.newRequestBody(\"ABCDEFGHIJKLMNOPQ\"),\n        ),\n      )\n    assertThat(response.code).isEqualTo(200)\n    val request = server.takeRequest()\n    assertThat(request.body?.utf8()).isEqualTo(\"ABCDEFGHIJKLMNOPQ\")\n    assertThat(request.chunkSizes).isEqualTo(listOf(\"ABCDEFGHIJKLMNOPQ\".length))\n  }\n\n  @Test\n  fun authenticateWithFixedLengthStreaming() {\n    testAuthenticateWithStreamingPost(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun authenticateWithChunkedStreaming() {\n    testAuthenticateWithStreamingPost(TransferKind.CHUNKED)\n  }\n\n  private fun testAuthenticateWithStreamingPost(streamingMode: TransferKind) {\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        headers = headersOf(\"WWW-Authenticate\", \"Basic realm=\\\"protected area\\\"\"),\n        body = \"Please authenticate.\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"Authenticated!\"),\n    )\n    java.net.Authenticator.setDefault(RecordingAuthenticator())\n    client =\n      client\n        .newBuilder()\n        .authenticator(JavaNetAuthenticator())\n        .build()\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body = streamingMode.newRequestBody(\"ABCD\"),\n      )\n    val response = getResponse(request)\n    assertThat(response.code).isEqualTo(200)\n    assertContent(\"Authenticated!\", response)\n\n    // No authorization header for the request...\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers[\"Authorization\"]).isNull()\n    assertThat(recordedRequest.body?.utf8()).isEqualTo(\"ABCD\")\n  }\n\n  @Test\n  fun postBodyRetransmittedAfterAuthorizationFail() {\n    postBodyRetransmittedAfterAuthorizationFail(\"abc\")\n  }\n\n  @Test\n  fun postBodyRetransmittedAfterAuthorizationFail_HTTP_2() {\n    platform.assumeHttp2Support()\n    enableProtocol(Protocol.HTTP_2)\n    postBodyRetransmittedAfterAuthorizationFail(\"abc\")\n  }\n\n  /** Don't explode when resending an empty post. https://github.com/square/okhttp/issues/1131  */\n  @Test\n  fun postEmptyBodyRetransmittedAfterAuthorizationFail() {\n    postBodyRetransmittedAfterAuthorizationFail(\"\")\n  }\n\n  @Test\n  fun postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2() {\n    platform.assumeHttp2Support()\n    enableProtocol(Protocol.HTTP_2)\n    postBodyRetransmittedAfterAuthorizationFail(\"\")\n  }\n\n  private fun postBodyRetransmittedAfterAuthorizationFail(body: String) {\n    server.enqueue(\n      MockResponse(code = 401),\n    )\n    server.enqueue(MockResponse())\n    val credential = basic(\"jesse\", \"secret\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(credential, null))\n        .build()\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = body.toRequestBody(),\n        ),\n      )\n    assertThat(response.code).isEqualTo(200)\n    response.body.byteStream().close()\n    val recordedRequest1 = server.takeRequest()\n    assertThat(recordedRequest1.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest1.body?.utf8()).isEqualTo(body)\n    assertThat(recordedRequest1.headers[\"Authorization\"]).isNull()\n    val recordedRequest2 = server.takeRequest()\n    assertThat(recordedRequest2.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest2.body?.utf8()).isEqualTo(body)\n    assertThat(recordedRequest2.headers[\"Authorization\"]).isEqualTo(credential)\n  }\n\n  @Test\n  fun nonStandardAuthenticationScheme() {\n    val calls = authCallsForHeader(\"WWW-Authenticate: Foo\")\n    assertThat(calls).isEqualTo(emptyList<String>())\n  }\n\n  @Test\n  fun nonStandardAuthenticationSchemeWithRealm() {\n    val calls = authCallsForHeader(\"WWW-Authenticate: Foo realm=\\\"Bar\\\"\")\n    assertThat(calls.size).isEqualTo(0)\n  }\n\n  // Digest auth is currently unsupported. Test that digest requests should fail reasonably.\n  // http://code.google.com/p/android/issues/detail?id=11140\n  @Test\n  fun digestAuthentication() {\n    val calls =\n      authCallsForHeader(\n        \"WWW-Authenticate: Digest \" +\n          \"realm=\\\"testrealm@host.com\\\", qop=\\\"auth,auth-int\\\", \" +\n          \"nonce=\\\"dcd98b7102dd2f0e8b11d0f600bfb0c093\\\", \" +\n          \"opaque=\\\"5ccc069c403ebaf9f0171e9517f40e41\\\"\",\n      )\n    assertThat(calls.size).isEqualTo(0)\n  }\n\n  @Test\n  fun allAttributesSetInServerAuthenticationCallbacks() {\n    val calls = authCallsForHeader(\"WWW-Authenticate: Basic realm=\\\"Bar\\\"\")\n    assertThat(calls.size).isEqualTo(1)\n    val url = server.url(\"/\").toUrl()\n    val call = calls[0]\n    assertThat(call).contains(\"host=\" + url.host)\n    assertThat(call).contains(\"port=\" + url.port)\n    assertThat(call).contains(\"site=\" + url.host)\n    assertThat(call).contains(\"url=$url\")\n    assertThat(call).contains(\"type=\" + java.net.Authenticator.RequestorType.SERVER)\n    assertThat(call).contains(\"prompt=Bar\")\n    assertThat(call).contains(\"protocol=http\")\n    assertThat(call.lowercase(Locale.US))\n      .contains(\"scheme=basic\") // lowercase for the RI.\n  }\n\n  @Test\n  fun allAttributesSetInProxyAuthenticationCallbacks() {\n    val calls = authCallsForHeader(\"Proxy-Authenticate: Basic realm=\\\"Bar\\\"\")\n    assertThat(calls.size).isEqualTo(1)\n    val url = server.url(\"/\").toUrl()\n    val call = calls[0]\n    assertThat(call).contains(\"host=\" + url.host)\n    assertThat(call).contains(\"port=\" + url.port)\n    assertThat(call).contains(\"site=\" + url.host)\n    assertThat(call).contains(\"url=http://android.com\")\n    assertThat(call).contains(\"type=\" + java.net.Authenticator.RequestorType.PROXY)\n    assertThat(call).contains(\"prompt=Bar\")\n    assertThat(call).contains(\"protocol=http\")\n    assertThat(call.lowercase(Locale.US)).contains(\"scheme=basic\")\n  }\n\n  private fun authCallsForHeader(authHeader: String): List<String> {\n    val proxy = authHeader.startsWith(\"Proxy-\")\n    val responseCode = if (proxy) 407 else 401\n    val authenticator = RecordingAuthenticator(null)\n    java.net.Authenticator.setDefault(authenticator)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(responseCode)\n        .addHeader(authHeader)\n        .body(\"Please authenticate.\")\n        .build(),\n    )\n    val response: Response\n    if (proxy) {\n      client =\n        client\n          .newBuilder()\n          .proxy(server.proxyAddress)\n          .proxyAuthenticator(JavaNetAuthenticator())\n          .build()\n      response = getResponse(Request(\"http://android.com/\".toHttpUrl()))\n    } else {\n      client =\n        client\n          .newBuilder()\n          .authenticator(JavaNetAuthenticator())\n          .build()\n      response = getResponse(newRequest(\"/\"))\n    }\n    assertThat(response.code).isEqualTo(responseCode)\n    response.body.byteStream().close()\n    return authenticator.calls\n  }\n\n  @Test\n  fun setValidRequestMethod() {\n    assertMethodForbidsRequestBody(\"GET\")\n    assertMethodPermitsRequestBody(\"DELETE\")\n    assertMethodForbidsRequestBody(\"HEAD\")\n    assertMethodPermitsRequestBody(\"OPTIONS\")\n    assertMethodPermitsRequestBody(\"POST\")\n    assertMethodPermitsRequestBody(\"PUT\")\n    assertMethodPermitsRequestBody(\"TRACE\")\n    assertMethodPermitsRequestBody(\"PATCH\")\n    assertMethodPermitsNoRequestBody(\"GET\")\n    assertMethodPermitsNoRequestBody(\"DELETE\")\n    assertMethodPermitsNoRequestBody(\"HEAD\")\n    assertMethodPermitsNoRequestBody(\"OPTIONS\")\n    assertMethodForbidsNoRequestBody(\"POST\")\n    assertMethodForbidsNoRequestBody(\"PUT\")\n    assertMethodPermitsNoRequestBody(\"TRACE\")\n    assertMethodForbidsNoRequestBody(\"PATCH\")\n  }\n\n  private fun assertMethodPermitsRequestBody(requestMethod: String) {\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(requestMethod, \"abc\".toRequestBody(null))\n        .build()\n    assertThat(request.method).isEqualTo(requestMethod)\n  }\n\n  private fun assertMethodForbidsRequestBody(requestMethod: String) {\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(requestMethod, \"abc\".toRequestBody(null))\n        .build()\n    }\n  }\n\n  private fun assertMethodPermitsNoRequestBody(requestMethod: String) {\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(requestMethod, null)\n        .build()\n    assertThat(request.method).isEqualTo(requestMethod)\n  }\n\n  private fun assertMethodForbidsNoRequestBody(requestMethod: String) {\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(requestMethod, null)\n        .build()\n    }\n  }\n\n  @Test\n  fun setInvalidRequestMethodLowercase() {\n    assertValidRequestMethod(\"get\")\n  }\n\n  @Test\n  fun setInvalidRequestMethodConnect() {\n    assertValidRequestMethod(\"CONNECT\")\n  }\n\n  private fun assertValidRequestMethod(requestMethod: String) {\n    server.enqueue(MockResponse())\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .method(requestMethod, null)\n          .build(),\n      )\n    assertThat(response.code).isEqualTo(200)\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(requestMethod)\n  }\n\n  @Test\n  fun shoutcast() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"ICY 200 OK\")\n        .addHeader(\"Accept-Ranges: none\")\n        .addHeader(\"Content-Type: audio/mpeg\")\n        .addHeader(\"icy-br:128\")\n        .addHeader(\"ice-audio-info: bitrate=128;samplerate=44100;channels=2\")\n        .addHeader(\"icy-br:128\")\n        .addHeader(\"icy-description:Rock\")\n        .addHeader(\"icy-genre:riders\")\n        .addHeader(\"icy-name:A2RRock\")\n        .addHeader(\"icy-pub:1\")\n        .addHeader(\"icy-url:http://www.A2Rradio.com\")\n        .addHeader(\"Server: Icecast 2.3.3-kh8\")\n        .addHeader(\"Cache-Control: no-cache\")\n        .addHeader(\"Pragma: no-cache\")\n        .addHeader(\"Expires: Mon, 26 Jul 1997 05:00:00 GMT\")\n        .addHeader(\"icy-metaint:16000\")\n        .body(\"mp3 data\")\n        .build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.message).isEqualTo(\"OK\")\n    assertContent(\"mp3 data\", response)\n  }\n\n  @Test\n  fun ntripr1() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"SOURCETABLE 200 OK\")\n        .addHeader(\"Server: NTRIP Caster 1.5.5/1.0\")\n        .addHeader(\"Date: 23/Jan/2004:08:54:59 UTC\")\n        .addHeader(\"Content-Type: text/plain\")\n        .body(\"STR;FFMJ2;Frankfurt;RTCM 2.1;1(1),3(19),16(59);0;GPS;GREF;DEU;50.12;8.68;0;1;GPSNet V2.10;none;N;N;560;Demo\\nENDSOURCETABLE\")\n        .build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.message).isEqualTo(\"OK\")\n    assertContent(\n      \"STR;FFMJ2;Frankfurt;RTCM 2.1;1(1),3(19),16(59);0;GPS;GREF;DEU;50.12;8.68;0;1;GPSNet V2.10;none;N;N;560;Demo\\nENDSOURCETABLE\",\n      response,\n    )\n  }\n\n  @Test\n  fun secureFixedLengthStreaming() {\n    testSecureStreamingPost(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun secureChunkedStreaming() {\n    testSecureStreamingPost(TransferKind.CHUNKED)\n  }\n\n  /**\n   * Users have reported problems using HTTPS with streaming request bodies.\n   * http://code.google.com/p/android/issues/detail?id=12860\n   */\n  private fun testSecureStreamingPost(streamingMode: TransferKind) {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse(body = \"Success!\"),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = streamingMode.newRequestBody(\"ABCD\"),\n        ),\n      )\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\n      \"Success!\",\n    )\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"POST / HTTP/1.1\")\n    if (streamingMode === TransferKind.FIXED_LENGTH) {\n      assertThat(request.chunkSizes).isNull()\n    } else if (streamingMode === TransferKind.CHUNKED) {\n      assertThat(request.chunkSizes).isEqualTo(listOf(4))\n    }\n    assertThat(request.body?.utf8()).isEqualTo(\"ABCD\")\n  }\n\n  @Test\n  fun authenticateWithPost() {\n    val pleaseAuthenticate =\n      MockResponse(\n        code = 401,\n        headers = headersOf(\"WWW-Authenticate\", \"Basic realm=\\\"protected area\\\"\"),\n        body = \"Please authenticate.\",\n      )\n    // Fail auth three times...\n    server.enqueue(pleaseAuthenticate)\n    server.enqueue(pleaseAuthenticate)\n    server.enqueue(pleaseAuthenticate)\n    // ...then succeed the fourth time.\n    server.enqueue(\n      MockResponse(body = \"Successful auth!\"),\n    )\n    java.net.Authenticator.setDefault(RecordingAuthenticator())\n    client =\n      client\n        .newBuilder()\n        .authenticator(JavaNetAuthenticator())\n        .build()\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = \"ABCD\".toRequestBody(null),\n        ),\n      )\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\n      \"Successful auth!\",\n    )\n\n    // No authorization header for the first request...\n    var request = server.takeRequest()\n    assertThat(request.headers[\"Authorization\"]).isNull()\n\n    // ...but the three requests that follow include an authorization header.\n    for (i in 0..2) {\n      request = server.takeRequest()\n      assertThat(request.requestLine).isEqualTo(\"POST / HTTP/1.1\")\n      assertThat(request.headers[\"Authorization\"]).isEqualTo(\n        \"Basic \" + RecordingAuthenticator.BASE_64_CREDENTIALS,\n      )\n      assertThat(request.body?.utf8()).isEqualTo(\"ABCD\")\n    }\n  }\n\n  @Test\n  fun authenticateWithGet() {\n    val pleaseAuthenticate =\n      MockResponse(\n        code = 401,\n        headers = headersOf(\"WWW-Authenticate\", \"Basic realm=\\\"protected area\\\"\"),\n        body = \"Please authenticate.\",\n      )\n    // Fail auth three times...\n    server.enqueue(pleaseAuthenticate)\n    server.enqueue(pleaseAuthenticate)\n    server.enqueue(pleaseAuthenticate)\n    // ...then succeed the fourth time.\n    server.enqueue(\n      MockResponse(body = \"Successful auth!\"),\n    )\n    java.net.Authenticator.setDefault(RecordingAuthenticator())\n    client =\n      client\n        .newBuilder()\n        .authenticator(JavaNetAuthenticator())\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"Successful auth!\")\n\n    // No authorization header for the first request...\n    var request = server.takeRequest()\n    assertThat(request.headers[\"Authorization\"]).isNull()\n\n    // ...but the three requests that follow requests include an authorization header.\n    for (i in 0..2) {\n      request = server.takeRequest()\n      assertThat(request.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n      assertThat(request.headers[\"Authorization\"])\n        .isEqualTo(\"Basic ${RecordingAuthenticator.BASE_64_CREDENTIALS}\")\n    }\n  }\n\n  @Test\n  fun authenticateWithCharset() {\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        headers =\n          headersOf(\n            \"WWW-Authenticate\",\n            \"Basic realm=\\\"protected area\\\", charset=\\\"UTF-8\\\"\",\n          ),\n        body = \"Please authenticate with UTF-8.\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        headers =\n          headersOf(\n            \"WWW-Authenticate\",\n            \"Basic realm=\\\"protected area\\\"\",\n          ),\n        body = \"Please authenticate with ISO-8859-1.\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"Successful auth!\"),\n    )\n    java.net.Authenticator.setDefault(\n      RecordingAuthenticator(\n        PasswordAuthentication(\"username\", \"mötorhead\".toCharArray()),\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .authenticator(JavaNetAuthenticator())\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"Successful auth!\")\n\n    // No authorization header for the first request...\n    val request1 = server.takeRequest()\n    assertThat(request1.headers[\"Authorization\"]).isNull()\n\n    // UTF-8 encoding for the first credential.\n    val request2 = server.takeRequest()\n    assertThat(request2.headers[\"Authorization\"]).isEqualTo(\n      \"Basic dXNlcm5hbWU6bcO2dG9yaGVhZA==\",\n    )\n\n    // ISO-8859-1 encoding for the second credential.\n    val request3 = server.takeRequest()\n    assertThat(request3.headers[\"Authorization\"])\n      .isEqualTo(\"Basic dXNlcm5hbWU6bfZ0b3JoZWFk\")\n  }\n\n  /** https://code.google.com/p/android/issues/detail?id=74026  */\n  @Test\n  fun authenticateWithGetAndTransparentGzip() {\n    val pleaseAuthenticate =\n      MockResponse(\n        code = 401,\n        headers = headersOf(\"WWW-Authenticate\", \"Basic realm=\\\"protected area\\\"\"),\n        body = \"Please authenticate.\",\n      )\n    // Fail auth three times...\n    server.enqueue(pleaseAuthenticate)\n    server.enqueue(pleaseAuthenticate)\n    server.enqueue(pleaseAuthenticate)\n    // ...then succeed the fourth time.\n    val successfulResponse =\n      MockResponse\n        .Builder()\n        .addHeader(\"Content-Encoding\", \"gzip\")\n        .body(gzip(\"Successful auth!\"))\n        .build()\n    server.enqueue(successfulResponse)\n    java.net.Authenticator.setDefault(RecordingAuthenticator())\n    client =\n      client\n        .newBuilder()\n        .authenticator(JavaNetAuthenticator())\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"Successful auth!\")\n\n    // no authorization header for the first request...\n    var request = server.takeRequest()\n    assertThat(request.headers[\"Authorization\"]).isNull()\n\n    // ...but the three requests that follow requests include an authorization header\n    for (i in 0..2) {\n      request = server.takeRequest()\n      assertThat(request.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n      assertThat(request.headers[\"Authorization\"]).isEqualTo(\n        \"Basic ${RecordingAuthenticator.BASE_64_CREDENTIALS}\",\n      )\n    }\n  }\n\n  /** https://github.com/square/okhttp/issues/342  */\n  @Test\n  fun authenticateRealmUppercase() {\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        headers = headersOf(\"wWw-aUtHeNtIcAtE\", \"bAsIc rEaLm=\\\"pRoTeCtEd aReA\\\"\"),\n        body = \"Please authenticate.\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"Successful auth!\"),\n    )\n    java.net.Authenticator.setDefault(RecordingAuthenticator())\n    client =\n      client\n        .newBuilder()\n        .authenticator(JavaNetAuthenticator())\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"Successful auth!\")\n  }\n\n  @Test\n  fun redirectedWithChunkedEncoding() {\n    testRedirected(TransferKind.CHUNKED, true)\n  }\n\n  @Test\n  fun redirectedWithContentLengthHeader() {\n    testRedirected(TransferKind.FIXED_LENGTH, true)\n  }\n\n  @Test\n  fun redirectedWithNoLengthHeaders() {\n    testRedirected(TransferKind.END_OF_STREAM, false)\n  }\n\n  private fun testRedirected(\n    transferKind: TransferKind,\n    reuse: Boolean,\n  ) {\n    val mockResponse =\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .addHeader(\"Location: /foo\")\n    transferKind.setBody(mockResponse, \"This page has moved!\", 10)\n    server.enqueue(mockResponse.build())\n    server.enqueue(MockResponse(body = \"This is the new location!\"))\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\n      \"This is the new location!\",\n    )\n    val first = server.takeRequest()\n    assertThat(first.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    val retry = server.takeRequest()\n    assertThat(retry.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n    if (reuse) {\n      assertThat(retry.exchangeIndex, \"Expected connection reuse\")\n        .isEqualTo(1)\n    }\n  }\n\n  @Test\n  fun redirectedOnHttps() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", \"/foo\"),\n        body = \"This page has moved!\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"This is the new location!\"),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\n      \"This is the new location!\",\n    )\n    val first = server.takeRequest()\n    assertThat(first.requestLine).isEqualTo(\"GET / HTTP/1.1\")\n    val retry = server.takeRequest()\n    assertThat(retry.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n    assertThat(retry.exchangeIndex, \"Expected connection reuse\")\n      .isEqualTo(1)\n  }\n\n  @Test\n  fun notRedirectedFromHttpsToHttp() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", \"http://anyhost/foo\"),\n        body = \"This page has moved!\",\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .followSslRedirects(false)\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"This page has moved!\")\n  }\n\n  @Test\n  fun notRedirectedFromHttpToHttps() {\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", \"https://anyhost/foo\"),\n        body = \"This page has moved!\",\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .followSslRedirects(false)\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"This page has moved!\")\n  }\n\n  @Test\n  fun redirectedFromHttpsToHttpFollowingProtocolRedirects() {\n    server2.enqueue(\n      MockResponse(body = \"This is insecure HTTP!\"),\n    )\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", server2.url(\"/\").toString()),\n        body = \"This page has moved!\",\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .followSslRedirects(true)\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertContent(\"This is insecure HTTP!\", response)\n    assertThat(response.handshake).isNull()\n  }\n\n  @Test\n  fun redirectedFromHttpToHttpsFollowingProtocolRedirects() {\n    server2.useHttps(handshakeCertificates.sslSocketFactory())\n    server2.enqueue(\n      MockResponse(body = \"This is secure HTTPS!\"),\n    )\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", server2.url(\"/\").toString()),\n        body = \"This page has moved!\",\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .followSslRedirects(true)\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertContent(\"This is secure HTTPS!\", response)\n  }\n\n  @Test\n  fun redirectToAnotherOriginServer() {\n    redirectToAnotherOriginServer(false)\n  }\n\n  @Test\n  fun redirectToAnotherOriginServerWithHttps() {\n    redirectToAnotherOriginServer(true)\n  }\n\n  private fun redirectToAnotherOriginServer(https: Boolean) {\n    if (https) {\n      server.useHttps(handshakeCertificates.sslSocketFactory())\n      server2.useHttps(handshakeCertificates.sslSocketFactory())\n      server2.protocolNegotiationEnabled = false\n      client =\n        client\n          .newBuilder()\n          .sslSocketFactory(\n            handshakeCertificates.sslSocketFactory(),\n            handshakeCertificates.trustManager,\n          ).hostnameVerifier(RecordingHostnameVerifier())\n          .build()\n    }\n    server2.enqueue(\n      MockResponse(body = \"This is the 2nd server!\"),\n    )\n    server2.enqueue(\n      MockResponse(body = \"This is the 2nd server, again!\"),\n    )\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", server2.url(\"/\").toString()),\n        body = \"This page has moved!\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"This is the first server again!\"),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertContent(\"This is the 2nd server!\", response)\n    assertThat(response.request.url).isEqualTo(\n      server2.url(\"/\"),\n    )\n\n    // make sure the first server was careful to recycle the connection\n    assertContent(\"This is the first server again!\", getResponse(Request(server.url(\"/\"))))\n    assertContent(\"This is the 2nd server, again!\", getResponse(Request(server2.url(\"/\"))))\n    val server1Host = server.hostName + \":\" + server.port\n    val server2Host = server2.hostName + \":\" + server2.port\n    assertThat(server.takeRequest().headers[\"Host\"]).isEqualTo(server1Host)\n    assertThat(server2.takeRequest().headers[\"Host\"]).isEqualTo(server2Host)\n    assertThat(server.takeRequest().exchangeIndex, \"Expected connection reuse\")\n      .isEqualTo(1)\n    assertThat(server2.takeRequest().exchangeIndex, \"Expected connection reuse\")\n      .isEqualTo(1)\n  }\n\n  @Test\n  fun redirectWithProxySelector() {\n    val proxySelectionRequests: MutableList<URI> = ArrayList()\n    client =\n      client\n        .newBuilder()\n        .proxySelector(\n          object : ProxySelector() {\n            override fun select(uri: URI): List<Proxy> {\n              proxySelectionRequests.add(uri)\n              val proxyServer = if (uri.port == server.port) server else server2\n              return listOf(proxyServer.proxyAddress)\n            }\n\n            override fun connectFailed(\n              uri: URI,\n              address: SocketAddress,\n              failure: IOException,\n            ): Unit = throw AssertionError()\n          },\n        ).build()\n    server2.enqueue(\n      MockResponse(body = \"This is the 2nd server!\"),\n    )\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", server2.url(\"/b\").toString()),\n        body = \"This page has moved!\",\n      ),\n    )\n    assertContent(\"This is the 2nd server!\", getResponse(newRequest(\"/a\")))\n    assertThat(proxySelectionRequests).isEqualTo(\n      listOf(\n        server.url(\"/\").toUrl().toURI(),\n        server2.url(\"/\").toUrl().toURI(),\n      ),\n    )\n  }\n\n  @Test\n  fun redirectWithAuthentication() {\n    server2.enqueue(\n      MockResponse(body = \"Page 2\"),\n    )\n    server.enqueue(\n      MockResponse(code = 401),\n    )\n    server.enqueue(\n      MockResponse(\n        code = 302,\n        headers = headersOf(\"Location\", server2.url(\"/b\").toString()),\n      ),\n    )\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(basic(\"jesse\", \"secret\"), null))\n        .build()\n    assertContent(\"Page 2\", getResponse(newRequest(\"/a\")))\n    val redirectRequest = server2.takeRequest()\n    assertThat(redirectRequest.headers[\"Authorization\"]).isNull()\n    assertThat(redirectRequest.url.encodedPath).isEqualTo(\"/b\")\n  }\n\n  @Test\n  fun response300MultipleChoiceWithPost() {\n    // Chrome doesn't follow the redirect, but Firefox and the RI both do\n    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MULT_CHOICE, TransferKind.END_OF_STREAM)\n  }\n\n  @Test\n  fun response301MovedPermanentlyWithPost() {\n    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_PERM, TransferKind.END_OF_STREAM)\n  }\n\n  @Test\n  fun response302MovedTemporarilyWithPost() {\n    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.END_OF_STREAM)\n  }\n\n  @Test\n  fun response303SeeOtherWithPost() {\n    testResponseRedirectedWithPost(HttpURLConnection.HTTP_SEE_OTHER, TransferKind.END_OF_STREAM)\n  }\n\n  @Test\n  fun postRedirectToGetWithChunkedRequest() {\n    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.CHUNKED)\n  }\n\n  @Test\n  fun postRedirectToGetWithStreamedRequest() {\n    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.FIXED_LENGTH)\n  }\n\n  private fun testResponseRedirectedWithPost(\n    redirectCode: Int,\n    transferKind: TransferKind,\n  ) {\n    server.enqueue(\n      MockResponse(\n        code = redirectCode,\n        headers = headersOf(\"Location\", \"/page2\"),\n        body = \"This page has moved!\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"Page 2\"),\n    )\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/page1\"),\n          body = transferKind.newRequestBody(\"ABCD\"),\n        ),\n      )\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"Page 2\")\n    val page1 = server.takeRequest()\n    assertThat(page1.requestLine).isEqualTo(\"POST /page1 HTTP/1.1\")\n    assertThat(page1.body?.utf8()).isEqualTo(\"ABCD\")\n    val page2 = server.takeRequest()\n    assertThat(page2.requestLine).isEqualTo(\"GET /page2 HTTP/1.1\")\n  }\n\n  @Test\n  fun redirectedPostStripsRequestBodyHeaders() {\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", \"/page2\"),\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"Page 2\"),\n    )\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/page1\"))\n          .post(\"ABCD\".toRequestBody(\"text/plain; charset=utf-8\".toMediaType()))\n          .header(\"Transfer-Encoding\", \"identity\")\n          .build(),\n      )\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"Page 2\")\n    assertThat(server.takeRequest().requestLine)\n      .isEqualTo(\"POST /page1 HTTP/1.1\")\n    val page2 = server.takeRequest()\n    assertThat(page2.requestLine).isEqualTo(\"GET /page2 HTTP/1.1\")\n    assertThat(page2.headers[\"Content-Length\"]).isNull()\n    assertThat(page2.headers[\"Content-Type\"]).isNull()\n    assertThat(page2.headers[\"Transfer-Encoding\"]).isNull()\n  }\n\n  @Test\n  fun response305UseProxy() {\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_USE_PROXY,\n        headers = headersOf(\"Location\", server.url(\"/\").toString()),\n        body = \"This page has moved!\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"Proxy Response\"),\n    )\n    val response = getResponse(newRequest(\"/foo\"))\n    // Fails on the RI, which gets \"Proxy Response\".\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"This page has moved!\")\n    val page1 = server.takeRequest()\n    assertThat(page1.requestLine).isEqualTo(\"GET /foo HTTP/1.1\")\n    assertThat(server.requestCount).isEqualTo(1)\n  }\n\n  @Test\n  fun response307WithGet() {\n    testRedirect(true, \"GET\")\n  }\n\n  @Test\n  fun response307WithHead() {\n    testRedirect(true, \"HEAD\")\n  }\n\n  @Test\n  fun response307WithOptions() {\n    testRedirect(true, \"OPTIONS\")\n  }\n\n  @Test\n  fun response307WithPost() {\n    testRedirect(true, \"POST\")\n  }\n\n  @Test\n  fun response308WithGet() {\n    testRedirect(false, \"GET\")\n  }\n\n  @Test\n  fun response308WithHead() {\n    testRedirect(false, \"HEAD\")\n  }\n\n  @Test\n  fun response308WithOptions() {\n    testRedirect(false, \"OPTIONS\")\n  }\n\n  @Test\n  fun response308WithPost() {\n    testRedirect(false, \"POST\")\n  }\n\n  /**\n   * In OkHttp 4.5 and earlier, HTTP 307 and 308 redirects were only honored if the request method\n   * was GET or HEAD.\n   *\n   * In OkHttp 4.6 and later, HTTP 307 and 308 redirects are honored for all request methods.\n   *\n   * If you're upgrading to OkHttp 4.6 and would like to retain the previous behavior, install this\n   * as a **network interceptor**. It will strip the `Location` header of impacted responses to\n   * prevent the redirect.\n   *\n   * ```\n   * OkHttpClient client = client.newBuilder()\n   *   .addNetworkInterceptor(new LegacyRedirectInterceptor())\n   *   .build();\n   * ```\n   */\n  internal class LegacyRedirectInterceptor : Interceptor {\n    override fun intercept(chain: Interceptor.Chain): Response {\n      val response = chain.proceed(chain.request())\n      val code = response.code\n      if (code != HTTP_TEMP_REDIRECT && code != HTTP_PERM_REDIRECT) return response\n      val method = response.request.method\n      if (method == \"GET\" || method == \"HEAD\") return response\n      val location = response.header(\"Location\") ?: return response\n      return response\n        .newBuilder()\n        .removeHeader(\"Location\")\n        .header(\"LegacyRedirectInterceptor-Location\", location)\n        .build()\n    }\n  }\n\n  @Test\n  fun response307WithPostReverted() {\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(LegacyRedirectInterceptor())\n        .build()\n    val response1 =\n      MockResponse(\n        code = HTTP_TEMP_REDIRECT,\n        headers = headersOf(\"Location\", \"/page2\"),\n        body = \"This page has moved!\",\n      )\n    server.enqueue(response1)\n    val request =\n      Request(\n        url = server.url(\"/page1\"),\n        body = \"ABCD\".toRequestBody(null),\n      )\n    val response = getResponse(request)\n    val responseString = readAscii(response.body.byteStream(), Int.MAX_VALUE)\n    val page1 = server.takeRequest()\n    assertThat(page1.requestLine).isEqualTo(\"POST /page1 HTTP/1.1\")\n    assertThat(page1.body?.utf8()).isEqualTo(\"ABCD\")\n    assertThat(server.requestCount).isEqualTo(1)\n    assertThat(responseString).isEqualTo(\"This page has moved!\")\n  }\n\n  @Test\n  fun response308WithPostReverted() {\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(LegacyRedirectInterceptor())\n        .build()\n    val response1 =\n      MockResponse(\n        code = HTTP_PERM_REDIRECT,\n        body = \"This page has moved!\",\n        headers = headersOf(\"Location\", \"/page2\"),\n      )\n    server.enqueue(response1)\n    val request =\n      Request(\n        url = server.url(\"/page1\"),\n        body = \"ABCD\".toRequestBody(null),\n      )\n    val response = getResponse(request)\n    val responseString = readAscii(response.body.byteStream(), Int.MAX_VALUE)\n    val page1 = server.takeRequest()\n    assertThat(page1.requestLine).isEqualTo(\"POST /page1 HTTP/1.1\")\n    assertThat(page1.body?.utf8()).isEqualTo(\"ABCD\")\n    assertThat(server.requestCount).isEqualTo(1)\n    assertThat(responseString).isEqualTo(\"This page has moved!\")\n  }\n\n  private fun testRedirect(\n    temporary: Boolean,\n    method: String,\n  ) {\n    val response1 =\n      MockResponse\n        .Builder()\n        .code(\n          if (temporary) HTTP_TEMP_REDIRECT else HTTP_PERM_REDIRECT,\n        ).addHeader(\"Location: /page2\")\n    if (method != \"HEAD\") {\n      response1.body(\"This page has moved!\")\n    }\n    server.enqueue(response1.build())\n    server.enqueue(MockResponse(body = \"Page 2\"))\n    val requestBuilder =\n      Request\n        .Builder()\n        .url(server.url(\"/page1\"))\n    if (method == \"POST\") {\n      requestBuilder.post(\"ABCD\".toRequestBody(null))\n    } else {\n      requestBuilder.method(method, null)\n    }\n    val response = getResponse(requestBuilder.build())\n    val responseString = readAscii(response.body.byteStream(), Int.MAX_VALUE)\n    val page1 = server.takeRequest()\n    assertThat(page1.requestLine).isEqualTo(\n      \"$method /page1 HTTP/1.1\",\n    )\n    if (method == \"GET\") {\n      assertThat(responseString).isEqualTo(\"Page 2\")\n    } else if (method == \"HEAD\") {\n      assertThat(responseString).isEqualTo(\"\")\n    }\n    assertThat(server.requestCount).isEqualTo(2)\n    val page2 = server.takeRequest()\n    assertThat(page2.requestLine)\n      .isEqualTo(\"$method /page2 HTTP/1.1\")\n  }\n\n  @Test\n  fun follow20Redirects() {\n    for (i in 0..19) {\n      server.enqueue(\n        MockResponse(\n          code = HttpURLConnection.HTTP_MOVED_TEMP,\n          headers = headersOf(\"Location\", \"/\" + (i + 1)),\n          body = \"Redirecting to /\" + (i + 1),\n        ),\n      )\n    }\n    server.enqueue(\n      MockResponse(body = \"Success!\"),\n    )\n    val response = getResponse(newRequest(\"/0\"))\n    assertContent(\"Success!\", response)\n    assertThat(response.request.url)\n      .isEqualTo(server.url(\"/20\"))\n  }\n\n  @Test\n  fun doesNotFollow21Redirects() {\n    for (i in 0..20) {\n      server.enqueue(\n        MockResponse(\n          code = HttpURLConnection.HTTP_MOVED_TEMP,\n          headers = headersOf(\"Location\", \"/\" + (i + 1)),\n          body = \"Redirecting to /\" + (i + 1),\n        ),\n      )\n    }\n    assertFailsWith<ProtocolException> {\n      getResponse(newRequest(\"/0\"))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Too many follow-up requests: 21\",\n      )\n    }\n  }\n\n  @Test\n  fun httpsWithCustomTrustManager() {\n    val hostnameVerifier = RecordingHostnameVerifier()\n    val trustManager = RecordingTrustManager(handshakeCertificates.trustManager)\n    val sslContext = get().newSSLContext()\n    sslContext.init(null, arrayOf<TrustManager>(trustManager), null)\n    client =\n      client\n        .newBuilder()\n        .hostnameVerifier(hostnameVerifier)\n        .sslSocketFactory(sslContext.socketFactory, trustManager)\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse(body = \"ABC\"))\n    server.enqueue(MockResponse(body = \"DEF\"))\n    server.enqueue(MockResponse(body = \"GHI\"))\n    assertContent(\"ABC\", getResponse(newRequest(\"/\")))\n    assertContent(\"DEF\", getResponse(newRequest(\"/\")))\n    assertContent(\"GHI\", getResponse(newRequest(\"/\")))\n    assertThat(hostnameVerifier.calls)\n      .isEqualTo(listOf(\"verify \" + server.hostName))\n    assertThat(trustManager.calls)\n      .isEqualTo(listOf(\"checkServerTrusted [CN=localhost 1]\"))\n  }\n\n  @Test\n  fun getClientRequestTimeout() {\n    enqueueClientRequestTimeoutResponses()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(200)\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"Body\")\n  }\n\n  private fun enqueueClientRequestTimeoutResponses() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_CLIENT_TIMEOUT)\n        .addHeader(\"Connection\", \"Close\")\n        .body(\"You took too long!\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"Body\"),\n    )\n  }\n\n  @Test\n  fun bufferedBodyWithClientRequestTimeout() {\n    enqueueClientRequestTimeoutResponses()\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = \"Hello\".toRequestBody(null),\n        ),\n      )\n    assertThat(response.code).isEqualTo(200)\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"Body\")\n    val request1 = server.takeRequest()\n    assertThat(request1.body?.utf8()).isEqualTo(\"Hello\")\n    val request2 = server.takeRequest()\n    assertThat(request2.body?.utf8()).isEqualTo(\"Hello\")\n  }\n\n  @Test\n  fun streamedBodyWithClientRequestTimeout() {\n    enqueueClientRequestTimeoutResponses()\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = TransferKind.CHUNKED.newRequestBody(\"Hello\"),\n        ),\n      )\n    assertThat(response.code).isEqualTo(200)\n    assertContent(\"Body\", response)\n    response.close()\n    assertThat(server.requestCount).isEqualTo(2)\n  }\n\n  @Test\n  fun readTimeouts() {\n    // This relies on the fact that MockWebServer doesn't close the\n    // connection after a response has been sent. This causes the client to\n    // try to read more bytes than are sent, which results in a timeout.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .clearHeaders()\n        .addHeader(\"Content-Length: 4\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"unused\"),\n    ) // to keep the server alive\n    val response = getResponse(newRequest(\"/\"))\n    val source = response.body.source()\n    source.timeout().timeout(1000, TimeUnit.MILLISECONDS)\n    assertThat(source.readByte()).isEqualTo('A'.code.toByte())\n    assertThat(source.readByte()).isEqualTo('B'.code.toByte())\n    assertThat(source.readByte()).isEqualTo('C'.code.toByte())\n    assertFailsWith<SocketTimeoutException> {\n      source.readByte() // If Content-Length was accurate, this would return -1 immediately.\n    }\n    source.close()\n  }\n\n  /** Confirm that an unacknowledged write times out.  */\n  @Test\n  fun writeTimeouts() {\n    val server = MockWebServer()\n    // Sockets on some platforms can have large buffers that mean writes do not block when\n    // required. These socket factories explicitly set the buffer sizes on sockets created.\n    val socketBufferSize = 4 * 1024\n    server.serverSocketFactory =\n      object : DelegatingServerSocketFactory(getDefault()) {\n        override fun configureServerSocket(serverSocket: ServerSocket): ServerSocket {\n          serverSocket.receiveBufferSize = socketBufferSize\n          return serverSocket\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .socketFactory(\n          object : DelegatingSocketFactory(getDefault()) {\n            override fun configureSocket(socket: Socket): Socket {\n              socket.receiveBufferSize = socketBufferSize\n              socket.sendBufferSize = socketBufferSize\n              return socket\n            }\n          },\n        ).writeTimeout(Duration.ofMillis(500))\n        .build()\n    server.start()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .throttleBody(1, 1, TimeUnit.SECONDS)\n        .build(),\n    ) // Prevent the server from reading!\n    val request =\n      Request(\n        url = server.url(\"/\"),\n        body =\n          object : RequestBody() {\n            override fun contentType(): MediaType? = null\n\n            override fun writeTo(sink: BufferedSink) {\n              val data = ByteArray(2 * 1024 * 1024) // 2 MiB.\n              sink.write(data)\n            }\n          },\n      )\n    assertFailsWith<SocketTimeoutException> {\n      getResponse(request)\n    }\n  }\n\n  @Test\n  fun setChunkedEncodingAsRequestProperty() {\n    server.enqueue(MockResponse())\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .header(\"Transfer-encoding\", \"chunked\")\n          .post(TransferKind.CHUNKED.newRequestBody(\"ABC\"))\n          .build(),\n      )\n    assertThat(response.code).isEqualTo(200)\n    val request = server.takeRequest()\n    assertThat(request.body?.utf8()).isEqualTo(\"ABC\")\n  }\n\n  @Test\n  fun connectionCloseInRequest() {\n    server.enqueue(MockResponse()) // Server doesn't honor the connection: close header!\n    server.enqueue(MockResponse())\n    val a =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .header(\"Connection\", \"close\")\n          .build(),\n      )\n    assertThat(a.code).isEqualTo(200)\n    val b = getResponse(newRequest(\"/\"))\n    assertThat(b.code).isEqualTo(200)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(\n      server.takeRequest().exchangeIndex,\n      \"When connection: close is used, each request should get its own connection\",\n    ).isEqualTo(0)\n  }\n\n  @Test\n  fun connectionCloseInResponse() {\n    server.enqueue(MockResponse(headers = headersOf(\"Connection\", \"close\")))\n    server.enqueue(MockResponse())\n    val a = getResponse(newRequest(\"/\"))\n    assertThat(a.code).isEqualTo(200)\n    val b = getResponse(newRequest(\"/\"))\n    assertThat(b.code).isEqualTo(200)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(\n      server.takeRequest().exchangeIndex,\n      \"When connection: close is used, each request should get its own connection\",\n    ).isEqualTo(0)\n  }\n\n  @Test\n  fun connectionCloseWithRedirect() {\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers =\n          headersOf(\n            \"Location\",\n            \"/foo\",\n            \"Connection\",\n            \"close\",\n          ),\n      ),\n    )\n    server.enqueue(MockResponse(body = \"This is the new location!\"))\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\n      \"This is the new location!\",\n    )\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(\n      server.takeRequest().exchangeIndex,\n      \"When connection: close is used, each request should get its own connection\",\n    ).isEqualTo(0)\n  }\n\n  /**\n   * Retry redirects if the socket is closed.\n   * https://code.google.com/p/android/issues/detail?id=41576\n   */\n  @Test\n  fun sameConnectionRedirectAndReuse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .addHeader(\"Location\", \"/foo\")\n        .onResponseEnd(\n          CloseSocket(\n            closeSocket = false,\n            shutdownInput = true,\n          ),\n        ).build(),\n    )\n    server.enqueue(MockResponse(body = \"This is the new page!\"))\n    assertContent(\"This is the new page!\", getResponse(newRequest(\"/\")))\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun responseCodeDisagreesWithHeaders() {\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_NO_CONTENT,\n        body = \"This body is not allowed!\",\n      ),\n    )\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/\"))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"HTTP 204 had non-zero Content-Length: 25\")\n    }\n  }\n\n  @Test\n  fun singleByteReadIsSigned() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          Buffer()\n            .writeByte(-2)\n            .writeByte(-1),\n        ).build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    val inputStream = response.body.byteStream()\n    assertThat(inputStream.read()).isEqualTo(254)\n    assertThat(inputStream.read()).isEqualTo(255)\n    assertThat(inputStream.read()).isEqualTo(-1)\n  }\n\n  @Test\n  fun flushAfterStreamTransmittedWithChunkedEncoding() {\n    testFlushAfterStreamTransmitted(TransferKind.CHUNKED)\n  }\n\n  @Test\n  fun flushAfterStreamTransmittedWithFixedLength() {\n    testFlushAfterStreamTransmitted(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun flushAfterStreamTransmittedWithNoLengthHeaders() {\n    testFlushAfterStreamTransmitted(TransferKind.END_OF_STREAM)\n  }\n\n  /**\n   * We explicitly permit apps to close the upload stream even after it has been transmitted.  We\n   * also permit flush so that buffered streams can do a no-op flush when they are closed.\n   * http://b/3038470\n   */\n  private fun testFlushAfterStreamTransmitted(transferKind: TransferKind) {\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    val sinkReference = AtomicReference<BufferedSink>()\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body =\n            object : ForwardingRequestBody(transferKind.newRequestBody(\"def\")) {\n              override fun writeTo(sink: BufferedSink) {\n                sinkReference.set(sink)\n                super.writeTo(sink)\n              }\n            },\n        ),\n      )\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE))\n      .isEqualTo(\"abc\")\n    assertFailsWith<IllegalStateException> {\n      sinkReference.get().flush()\n    }\n    assertFailsWith<IllegalStateException> {\n      sinkReference.get().write(\"ghi\".toByteArray())\n      sinkReference.get().emit()\n    }\n  }\n\n  @Test\n  fun getHeadersThrows() {\n    server.enqueue(MockResponse.Builder().onRequestStart(CloseSocket()).build())\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/\"))\n    }\n  }\n\n  @Test\n  fun dnsFailureThrowsIOException() {\n    client =\n      client\n        .newBuilder()\n        .dns(FakeDns())\n        .build()\n    assertFailsWith<IOException> {\n      getResponse(Request(\"http://host.unlikelytld\".toHttpUrl()))\n    }\n  }\n\n  @Test\n  fun malformedUrlThrowsUnknownHostException() {\n    assertFailsWith<IOException> {\n      getResponse(Request(\"http://-/foo.html\".toHttpUrl()))\n    }\n  }\n\n  // The request should work once and then fail.\n  @Test\n  fun getKeepAlive() {\n    server.enqueue(MockResponse(body = \"ABC\"))\n\n    // The request should work once and then fail.\n    val connection1 = getResponse(newRequest(\"/\"))\n    val source1 = connection1.body.source()\n    source1.timeout().timeout(100, TimeUnit.MILLISECONDS)\n    assertThat(readAscii(source1.inputStream(), Int.MAX_VALUE)).isEqualTo(\"ABC\")\n    server.close()\n    assertFailsWith<ConnectException> {\n      getResponse(newRequest(\"/\"))\n    }\n  }\n\n  /** http://code.google.com/p/android/issues/detail?id=14562  */\n  @Test\n  fun readAfterLastByte() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .clearHeaders()\n        .addHeader(\"Connection: close\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    val inputStream = response.body.byteStream()\n    assertThat(readAscii(inputStream, 3)).isEqualTo(\"ABC\")\n    assertThat(inputStream.read()).isEqualTo(-1)\n    // throws IOException in Gingerbread.\n    assertThat(inputStream.read()).isEqualTo(-1)\n  }\n\n  @Test\n  fun getOutputStreamOnGetFails() {\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"GET\", \"abc\".toRequestBody(null))\n        .build()\n    }\n  }\n\n  @Test\n  fun clientSendsContentLength() {\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = \"ABC\".toRequestBody(null),\n        ),\n      )\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\"A\")\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Content-Length\"]).isEqualTo(\"3\")\n    response.body.close()\n  }\n\n  @Test\n  fun getContentLengthConnects() {\n    server.enqueue(\n      MockResponse(body = \"ABC\"),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.body.contentLength()).isEqualTo(3L)\n    response.body.close()\n  }\n\n  @Test\n  fun getContentTypeConnects() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Type\", \"text/plain\"),\n        body = \"ABC\",\n      ),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.body.contentType()).isEqualTo(\n      \"text/plain\".toMediaType(),\n    )\n    response.body.close()\n  }\n\n  @Test\n  fun getContentEncodingConnects() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"Content-Encoding\", \"identity\"),\n        body = \"ABC\",\n      ),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.header(\"Content-Encoding\")).isEqualTo(\"identity\")\n    response.body.close()\n  }\n\n  @Test\n  fun urlContainsQueryButNoPath() {\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    val url = server.url(\"?query\")\n    val response = getResponse(Request(url))\n    assertThat(readAscii(response.body.byteStream(), Int.MAX_VALUE)).isEqualTo(\"A\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET /?query HTTP/1.1\")\n  }\n\n  @Test\n  fun doOutputForMethodThatDoesntSupportOutput() {\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .method(\"HEAD\", \"\".toRequestBody(null))\n        .build()\n    }\n  }\n\n  // http://code.google.com/p/android/issues/detail?id=20442\n  @Test\n  fun inputStreamAvailableWithChunkedEncoding() {\n    testInputStreamAvailable(TransferKind.CHUNKED)\n  }\n\n  @Test\n  fun inputStreamAvailableWithContentLengthHeader() {\n    testInputStreamAvailable(TransferKind.FIXED_LENGTH)\n  }\n\n  @Test\n  fun inputStreamAvailableWithNoLengthHeaders() {\n    testInputStreamAvailable(TransferKind.END_OF_STREAM)\n  }\n\n  private fun testInputStreamAvailable(transferKind: TransferKind) {\n    val body = \"ABCDEFGH\"\n    val builder = MockResponse.Builder()\n    transferKind.setBody(builder, body, 4)\n    server.enqueue(builder.build())\n    val response = getResponse(newRequest(\"/\"))\n    val inputStream = response.body.byteStream()\n    for (i in 0 until body.length) {\n      assertThat(inputStream.available()).isGreaterThanOrEqualTo(0)\n      assertThat(inputStream.read()).isEqualTo(body[i].code)\n    }\n    assertThat(inputStream.available()).isEqualTo(0)\n    assertThat(inputStream.read()).isEqualTo(-1)\n  }\n\n  @Test\n  fun postFailsWithBufferedRequestForSmallRequest() {\n    reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 1024)\n  }\n\n  @Test\n  fun postFailsWithBufferedRequestForLargeRequest() {\n    reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 16384)\n  }\n\n  @Test\n  fun postFailsWithChunkedRequestForSmallRequest() {\n    reusedConnectionFailsWithPost(TransferKind.CHUNKED, 1024)\n  }\n\n  @Test\n  fun postFailsWithChunkedRequestForLargeRequest() {\n    reusedConnectionFailsWithPost(TransferKind.CHUNKED, 16384)\n  }\n\n  @Test\n  fun postFailsWithFixedLengthRequestForSmallRequest() {\n    reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 1024)\n  }\n\n  @Test\n  fun postFailsWithFixedLengthRequestForLargeRequest() {\n    reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 16384)\n  }\n\n  private fun reusedConnectionFailsWithPost(\n    transferKind: TransferKind,\n    requestSize: Int,\n  ) {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"B\"))\n    server.enqueue(MockResponse(body = \"C\"))\n    assertContent(\"A\", getResponse(newRequest(\"/a\")))\n\n    // Give the server time to disconnect.\n    Thread.sleep(500)\n\n    // If the request body is larger than OkHttp's replay buffer, the failure may still occur.\n    val requestBodyChars = CharArray(requestSize)\n    Arrays.fill(requestBodyChars, 'x')\n    val requestBody = String(requestBodyChars)\n    for (j in 0..1) {\n      try {\n        val response =\n          getResponse(\n            Request(\n              url = server.url(\"/b\"),\n              body = transferKind.newRequestBody(requestBody),\n            ),\n          )\n        assertContent(\"B\", response)\n        break\n      } catch (socketException: IOException) {\n        // If there's a socket exception, this must have a streamed request body.\n        assertThat(j).isEqualTo(0)\n        assertThat(transferKind).isIn(TransferKind.CHUNKED, TransferKind.FIXED_LENGTH)\n      }\n    }\n    val requestA = server.takeRequest()\n    assertThat(requestA.url.encodedPath).isEqualTo(\"/a\")\n    val requestB = server.takeRequest()\n    assertThat(requestB.url.encodedPath).isEqualTo(\"/b\")\n    assertThat(requestB.body?.utf8()).isEqualTo(requestBody)\n  }\n\n  @Test\n  fun postBodyRetransmittedOnFailureRecovery() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse.Builder().onResponseStart(CloseSocket()).build())\n    server.enqueue(MockResponse(body = \"def\"))\n\n    // Seed the connection pool so we have something that can fail.\n    assertContent(\"abc\", getResponse(newRequest(\"/\")))\n    val post =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = \"body!\".toRequestBody(null),\n        ),\n      )\n    assertContent(\"def\", post)\n    val get = server.takeRequest()\n    assertThat(get.exchangeIndex).isEqualTo(0)\n    val post1 = server.takeRequest()\n    assertThat(post1.body?.utf8()).isEqualTo(\"body!\")\n    assertThat(post1.exchangeIndex).isEqualTo(1)\n    val post2 = server.takeRequest()\n    assertThat(post2.body?.utf8()).isEqualTo(\"body!\")\n    assertThat(post2.exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun fullyBufferedPostIsTooShort() {\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    val requestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 4L\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.writeUtf8(\"abc\")\n        }\n      }\n    assertFailsWith<IOException> {\n      getResponse(\n        Request(\n          url = server.url(\"/b\"),\n          body = requestBody,\n        ),\n      )\n    }\n  }\n\n  @Test\n  fun fullyBufferedPostIsTooLong() {\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    val requestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun contentLength(): Long = 3L\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.writeUtf8(\"abcd\")\n        }\n      }\n    assertFailsWith<IOException> {\n      getResponse(\n        Request(\n          url = server.url(\"/b\"),\n          body = requestBody,\n        ),\n      )\n    }\n  }\n\n  @Test\n  @Disabled\n  fun testPooledConnectionsDetectHttp10() {\n    // TODO: write a test that shows pooled connections detect HTTP/1.0 (vs. HTTP/1.1)\n    fail(\"TODO\")\n  }\n\n  @Test\n  @Disabled\n  fun postBodiesRetransmittedOnAuthProblems() {\n    fail(\"TODO\")\n  }\n\n  @Test\n  @Disabled\n  fun cookiesAndTrailers() {\n    // Do cookie headers get processed too many times?\n    fail(\"TODO\")\n  }\n\n  @Test\n  fun emptyRequestHeaderValueIsAllowed() {\n    server.enqueue(\n      MockResponse(body = \"body\"),\n    )\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .header(\"B\", \"\")\n          .build(),\n      )\n    assertContent(\"body\", response)\n    assertThat(response.request.header(\"B\")).isEqualTo(\"\")\n  }\n\n  @Test\n  fun emptyResponseHeaderValueIsAllowed() {\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"A\", \"\"),\n        body = \"body\",\n      ),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertContent(\"body\", response)\n    assertThat(response.header(\"A\")).isEqualTo(\"\")\n  }\n\n  @Test\n  fun emptyRequestHeaderNameIsStrict() {\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"\", \"A\")\n        .build()\n    }\n  }\n\n  @Test\n  fun emptyResponseHeaderNameIsLenient() {\n    val headers = Headers.Builder()\n    addHeaderLenient(headers, \":A\")\n    server.enqueue(\n      MockResponse(\n        headers = headers.build(),\n        body = \"body\",\n      ),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.header(\"\")).isEqualTo(\"A\")\n    response.body.close()\n  }\n\n  @Test\n  fun requestHeaderValidationIsStrict() {\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .addHeader(\"a\\tb\", \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .addHeader(\"Name\", \"c\\u007fd\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .addHeader(\"\", \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .addHeader(\"\\ud83c\\udf69\", \"Value\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      Request\n        .Builder()\n        .addHeader(\"Name\", \"\\u2615\\ufe0f\")\n    }\n  }\n\n  @Test\n  fun responseHeaderParsingIsLenient() {\n    val headersBuilder = Headers.Builder()\n    headersBuilder.add(\"Content-Length\", \"0\")\n    addHeaderLenient(headersBuilder, \"a\\tb: c\\u007fd\")\n    addHeaderLenient(headersBuilder, \": ef\")\n    addHeaderLenient(headersBuilder, \"\\ud83c\\udf69: \\u2615\\ufe0f\")\n    val headers = headersBuilder.build()\n    server.enqueue(MockResponse(headers = headers))\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.header(\"a\\tb\")).isEqualTo(\"c\\u007fd\")\n    assertThat(response.header(\"\\ud83c\\udf69\")).isEqualTo(\"\\u2615\\ufe0f\")\n    assertThat(response.header(\"\")).isEqualTo(\"ef\")\n  }\n\n  @Test\n  @Disabled\n  fun deflateCompression() {\n    fail(\"TODO\")\n  }\n\n  @Test\n  @Disabled\n  fun postBodiesRetransmittedOnIpAddressProblems() {\n    fail(\"TODO\")\n  }\n\n  @Test\n  @Disabled\n  fun pooledConnectionProblemsNotReportedToProxySelector() {\n    fail(\"TODO\")\n  }\n\n  @Test\n  fun customBasicAuthenticator() {\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        headers = headersOf(\"WWW-Authenticate\", \"Basic realm=\\\"protected area\\\"\"),\n        body = \"Please authenticate.\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    val credential = basic(\"jesse\", \"peanutbutter\")\n    val authenticator = RecordingOkAuthenticator(credential, null)\n    client =\n      client\n        .newBuilder()\n        .authenticator(authenticator)\n        .build()\n    assertContent(\"A\", getResponse(newRequest(\"/private\")))\n    assertThat(server.takeRequest().headers[\"Authorization\"]).isNull()\n    assertThat(server.takeRequest().headers[\"Authorization\"]).isEqualTo(credential)\n    assertThat(authenticator.onlyRoute().proxy).isEqualTo(Proxy.NO_PROXY)\n    val response = authenticator.onlyResponse()\n    assertThat(\n      response.request.url\n        .toUrl()\n        .path,\n    ).isEqualTo(\"/private\")\n    assertThat(response.challenges()).isEqualTo(listOf(Challenge(\"Basic\", \"protected area\")))\n  }\n\n  @Test\n  fun customTokenAuthenticator() {\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        headers = headersOf(\"WWW-Authenticate\", \"Bearer realm=\\\"oauthed\\\"\"),\n        body = \"Please authenticate.\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    val authenticator = RecordingOkAuthenticator(\"oauthed abc123\", \"Bearer\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(authenticator)\n        .build()\n    assertContent(\"A\", getResponse(newRequest(\"/private\")))\n    assertThat(server.takeRequest().headers[\"Authorization\"]).isNull()\n    assertThat(server.takeRequest().headers[\"Authorization\"]).isEqualTo(\n      \"oauthed abc123\",\n    )\n    val response = authenticator.onlyResponse()\n    assertThat(\n      response.request.url\n        .toUrl()\n        .path,\n    ).isEqualTo(\"/private\")\n    assertThat(response.challenges()).isEqualTo(listOf(Challenge(\"Bearer\", \"oauthed\")))\n  }\n\n  @Test\n  fun authenticateCallsTrackedAsRedirects() {\n    server.enqueue(\n      MockResponse(\n        code = 302,\n        headers = headersOf(\"Location\", \"/b\"),\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        headers = headersOf(\"WWW-Authenticate\", \"Basic realm=\\\"protected area\\\"\"),\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"c\"),\n    )\n    val authenticator =\n      RecordingOkAuthenticator(\n        basic(\"jesse\", \"peanutbutter\"),\n        \"Basic\",\n      )\n    client =\n      client\n        .newBuilder()\n        .authenticator(authenticator)\n        .build()\n    assertContent(\"c\", getResponse(newRequest(\"/a\")))\n    val challengeResponse = authenticator.responses[0]\n    assertThat(\n      challengeResponse.request.url\n        .toUrl()\n        .path,\n    ).isEqualTo(\"/b\")\n    val redirectedBy = challengeResponse.priorResponse\n    assertThat(\n      redirectedBy!!\n        .request.url\n        .toUrl()\n        .path,\n    ).isEqualTo(\"/a\")\n  }\n\n  @Test\n  fun attemptAuthorization20Times() {\n    for (i in 0..19) {\n      server.enqueue(\n        MockResponse(code = 401),\n      )\n    }\n    server.enqueue(\n      MockResponse(body = \"Success!\"),\n    )\n    val credential = basic(\"jesse\", \"peanutbutter\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(credential, null))\n        .build()\n    val response = getResponse(newRequest(\"/0\"))\n    assertContent(\"Success!\", response)\n  }\n\n  @Test\n  fun doesNotAttemptAuthorization21Times() {\n    for (i in 0..20) {\n      server.enqueue(\n        MockResponse(code = 401),\n      )\n    }\n    val credential = basic(\"jesse\", \"peanutbutter\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(credential, null))\n        .build()\n    assertFailsWith<ProtocolException> {\n      getResponse(newRequest(\"/\"))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Too many follow-up requests: 21\")\n    }\n  }\n\n  @Test\n  fun setsNegotiatedProtocolHeader_HTTP_2() {\n    platform.assumeHttp2Support()\n    setsNegotiatedProtocolHeader(Protocol.HTTP_2)\n  }\n\n  private fun setsNegotiatedProtocolHeader(protocol: Protocol) {\n    enableProtocol(protocol)\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    client =\n      client\n        .newBuilder()\n        .protocols(Arrays.asList(protocol, Protocol.HTTP_1_1))\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.protocol).isEqualTo(protocol)\n    assertContent(\"A\", response)\n  }\n\n  @Test\n  fun http10SelectedProtocol() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.0 200 OK\")\n        .build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.protocol).isEqualTo(Protocol.HTTP_1_0)\n  }\n\n  @Test\n  fun http11SelectedProtocol() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 200 OK\")\n        .build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.protocol).isEqualTo(Protocol.HTTP_1_1)\n  }\n\n  /** For example, empty Protobuf RPC messages end up as a zero-length POST.  */\n  @Test\n  fun zeroLengthPost() {\n    zeroLengthPayload(\"POST\")\n  }\n\n  @Test\n  fun zeroLengthPost_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    zeroLengthPost()\n  }\n\n  /** For example, creating an Amazon S3 bucket ends up as a zero-length POST.  */\n  @Test\n  fun zeroLengthPut() {\n    zeroLengthPayload(\"PUT\")\n  }\n\n  @Test\n  fun zeroLengthPut_HTTP_2() {\n    enableProtocol(Protocol.HTTP_2)\n    zeroLengthPut()\n  }\n\n  private fun zeroLengthPayload(method: String) {\n    server.enqueue(MockResponse())\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .method(method, \"\".toRequestBody(null))\n          .build(),\n      )\n    assertContent(\"\", response)\n    val zeroLengthPayload = server.takeRequest()\n    assertThat(zeroLengthPayload.method).isEqualTo(method)\n    assertThat(zeroLengthPayload.headers[\"content-length\"]).isEqualTo(\"0\")\n    assertThat(zeroLengthPayload.bodySize).isEqualTo(0L)\n  }\n\n  @Test\n  fun setProtocols() {\n    server.enqueue(\n      MockResponse(body = \"A\"),\n    )\n    client =\n      client\n        .newBuilder()\n        .protocols(Arrays.asList(Protocol.HTTP_1_1))\n        .build()\n    assertContent(\"A\", getResponse(newRequest(\"/\")))\n  }\n\n  @Test\n  fun setProtocolsWithoutHttp11() {\n    assertFailsWith<IllegalArgumentException> {\n      OkHttpClient\n        .Builder()\n        .protocols(Arrays.asList(Protocol.HTTP_2))\n    }\n  }\n\n  @Test\n  fun setProtocolsWithNull() {\n    assertFailsWith<IllegalArgumentException> {\n      OkHttpClient\n        .Builder()\n        .protocols(Arrays.asList(Protocol.HTTP_1_1, null))\n    }\n  }\n\n  @Test\n  fun veryLargeFixedLengthRequest() {\n    server.bodyLimit = 0\n    server.enqueue(MockResponse())\n    val contentLength = Int.MAX_VALUE + 1L\n    val response =\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body =\n            object : RequestBody() {\n              override fun contentType(): MediaType? = null\n\n              override fun contentLength(): Long = contentLength\n\n              override fun writeTo(sink: BufferedSink) {\n                val buffer = ByteArray(1024 * 1024)\n                var bytesWritten: Long = 0\n                while (bytesWritten < contentLength) {\n                  val byteCount = Math.min(buffer.size.toLong(), contentLength - bytesWritten).toInt()\n                  bytesWritten += byteCount.toLong()\n                  sink.write(buffer, 0, byteCount)\n                }\n              }\n            },\n        ),\n      )\n    assertContent(\"\", response)\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Content-Length\"]).isEqualTo(\n      java.lang.Long.toString(contentLength),\n    )\n  }\n\n  @Test\n  fun testNoSslFallback() {\n    platform.assumeNotBouncyCastle()\n\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse.Builder().failHandshake().build())\n    server.enqueue(MockResponse(body = \"Response that would have needed fallbacks\"))\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).build()\n    assertFailsWith<IOException> {\n      getResponse(newRequest(\"/\"))\n    }.also { expected ->\n      when (expected) {\n        is SSLProtocolException -> {\n          // RI response to the FAIL_HANDSHAKE\n        }\n\n        is SSLHandshakeException -> {\n          // Android's response to the FAIL_HANDSHAKE\n        }\n\n        is SSLException -> {\n          // JDK 1.9 response to the FAIL_HANDSHAKE\n          // javax.net.ssl.SSLException: Unexpected handshake message: client_hello\n        }\n\n        is SocketException -> {\n          // Conscrypt's response to the FAIL_HANDSHAKE\n        }\n\n        else -> {\n          throw expected\n        }\n      }\n    }\n  }\n\n  /**\n   * We had a bug where we attempted to gunzip responses that didn't have a body. This only came up\n   * with 304s since that response code can include headers (like \"Content-Encoding\") without any\n   * content to go along with it. https://github.com/square/okhttp/issues/358\n   */\n  @Test\n  fun noTransparentGzipFor304NotModified() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .clearHeaders()\n        .code(HttpURLConnection.HTTP_NOT_MODIFIED)\n        .addHeader(\"Content-Encoding: gzip\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"b\"),\n    )\n    val response1 = getResponse(newRequest(\"/\"))\n    assertThat(response1.code).isEqualTo(HttpURLConnection.HTTP_NOT_MODIFIED)\n    assertContent(\"\", response1)\n    val response2 = getResponse(newRequest(\"/\"))\n    assertThat(response2.code).isEqualTo(HttpURLConnection.HTTP_OK)\n    assertContent(\"b\", response2)\n    val requestA = server.takeRequest()\n    assertThat(requestA.exchangeIndex).isEqualTo(0)\n    val requestB = server.takeRequest()\n    assertThat(requestB.exchangeIndex).isEqualTo(1)\n  }\n\n  /**\n   * We had a bug where we weren't closing Gzip streams on redirects.\n   * https://github.com/square/okhttp/issues/441\n   */\n  @Test\n  fun gzipWithRedirectAndConnectionReuse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .addHeader(\"Location: /foo\")\n        .addHeader(\"Content-Encoding: gzip\")\n        .body(gzip(\"Moved! Moved! Moved!\"))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"This is the new page!\"),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertContent(\"This is the new page!\", response)\n    val requestA = server.takeRequest()\n    assertThat(requestA.exchangeIndex).isEqualTo(0)\n    val requestB = server.takeRequest()\n    assertThat(requestB.exchangeIndex).isEqualTo(1)\n  }\n\n  /**\n   * The RFC is unclear in this regard as it only specifies that this should invalidate the cache\n   * entry (if any).\n   */\n  @Test\n  fun bodyPermittedOnDelete() {\n    server.enqueue(MockResponse())\n    val response =\n      getResponse(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .delete(\"BODY\".toRequestBody(null))\n          .build(),\n      )\n    assertThat(response.code).isEqualTo(200)\n    val request = server.takeRequest()\n    assertThat(request.method).isEqualTo(\"DELETE\")\n    assertThat(request.body?.utf8()).isEqualTo(\"BODY\")\n  }\n\n  @Test\n  fun userAgentDefaultsToOkHttpVersion() {\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    assertContent(\"abc\", getResponse(newRequest(\"/\")))\n    val request = server.takeRequest()\n    assertThat(request.headers[\"User-Agent\"]).isEqualTo(USER_AGENT)\n  }\n\n  @Test\n  fun urlWithSpaceInHost() {\n    assertFailsWith<IllegalArgumentException> {\n      \"http://and roid.com/\".toHttpUrl()\n    }\n  }\n\n  @Test\n  fun urlWithSpaceInHostViaHttpProxy() {\n    assertFailsWith<IllegalArgumentException> {\n      \"http://and roid.com/\".toHttpUrl()\n    }\n  }\n\n  @Test\n  fun urlHostWithNul() {\n    assertFailsWith<IllegalArgumentException> {\n      \"http://host\\u0000/\".toHttpUrl()\n    }\n  }\n\n  @Test\n  fun urlRedirectToHostWithNul() {\n    val redirectUrl = \"http://host\\u0000/\"\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(302)\n        .addHeaderLenient(\"Location\", redirectUrl)\n        .build(),\n    )\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(302)\n    assertThat(response.header(\"Location\")).isEqualTo(redirectUrl)\n  }\n\n  @Test\n  fun urlWithBadAsciiHost() {\n    assertFailsWith<IllegalArgumentException> {\n      \"http://host\\u0001/\".toHttpUrl()\n    }\n  }\n\n  @Suppress(\"DEPRECATION_ERROR\")\n  @Test\n  fun setSslSocketFactoryFailsOnJdk9() {\n    platform.assumeJdk9()\n    assertFailsWith<UnsupportedOperationException> {\n      client\n        .newBuilder()\n        .sslSocketFactory(handshakeCertificates.sslSocketFactory())\n    }\n  }\n\n  /** Confirm that runtime exceptions thrown inside of OkHttp propagate to the caller.  */\n  @Test\n  fun unexpectedExceptionSync() {\n    client =\n      client\n        .newBuilder()\n        .dns { hostname: String? -> throw RuntimeException(\"boom!\") }\n        .build()\n    server.enqueue(MockResponse())\n    assertFailsWith<RuntimeException> {\n      getResponse(newRequest(\"/\"))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"boom!\")\n    }\n  }\n\n  @Test\n  fun streamedBodyIsRetriedOnHttp2Shutdown() {\n    platform.assumeHttp2Support()\n    enableProtocol(Protocol.HTTP_2)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"def\"),\n    )\n\n    // Send a separate request which will trigger a GOAWAY frame on the healthy connection.\n    val response = getResponse(newRequest(\"/\"))\n    assertContent(\"abc\", response)\n\n    // Ensure the GOAWAY frame has time to be read and processed.\n    Thread.sleep(500)\n    assertContent(\n      \"def\",\n      getResponse(\n        Request(\n          url = server.url(\"/\"),\n          body = \"123\".toRequestBody(null),\n        ),\n      ),\n    )\n    val request1 = server.takeRequest()\n    assertThat(request1.exchangeIndex).isEqualTo(0)\n    val request2 = server.takeRequest()\n    assertThat(request2.body?.utf8()).isEqualTo(\"123\")\n    assertThat(request2.exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun authenticateNoConnection() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(401)\n        .addHeader(\"Connection\", \"close\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    java.net.Authenticator.setDefault(RecordingAuthenticator(null))\n    client =\n      client\n        .newBuilder()\n        .authenticator(JavaNetAuthenticator())\n        .build()\n    val response = getResponse(newRequest(\"/\"))\n    assertThat(response.code).isEqualTo(401)\n  }\n\n  private fun newRequest(s: String): Request = Request(server.url(s))\n\n  private fun getResponse(request: Request): Response = client.newCall(request).execute()\n\n  /** Returns a gzipped copy of `bytes`.  */\n  fun gzip(data: String?): Buffer {\n    val result = Buffer()\n    val gzipSink = GzipSink(result).buffer()\n    gzipSink.writeUtf8(data!!)\n    gzipSink.close()\n    return result\n  }\n\n  private fun assertContent(\n    expected: String,\n    response: Response,\n    limit: Int = Int.MAX_VALUE,\n  ) {\n    assertThat(readAscii(response.body.byteStream(), limit)).isEqualTo(expected)\n  }\n\n  private fun newSet(vararg elements: String): Set<String> = setOf(*elements)\n\n  internal enum class TransferKind {\n    CHUNKED {\n      override fun setBody(\n        response: MockResponse.Builder,\n        content: Buffer?,\n        chunkSize: Int,\n      ) {\n        response.chunkedBody(content!!, chunkSize)\n      }\n\n      override fun newRequestBody(body: String): RequestBody =\n        object : RequestBody() {\n          override fun contentLength(): Long = -1L\n\n          override fun contentType(): MediaType? = null\n\n          override fun writeTo(sink: BufferedSink) {\n            sink.writeUtf8(body)\n          }\n        }\n    },\n\n    FIXED_LENGTH {\n      override fun setBody(\n        response: MockResponse.Builder,\n        content: Buffer?,\n        chunkSize: Int,\n      ) {\n        response.body(content!!)\n      }\n\n      override fun newRequestBody(body: String): RequestBody =\n        object : RequestBody() {\n          override fun contentLength(): Long = body.utf8Size()\n\n          override fun contentType(): MediaType? = null\n\n          override fun writeTo(sink: BufferedSink) {\n            sink.writeUtf8(body)\n          }\n        }\n    },\n\n    END_OF_STREAM {\n      override fun setBody(\n        response: MockResponse.Builder,\n        content: Buffer?,\n        chunkSize: Int,\n      ) {\n        response.body(content!!)\n        response.onResponseEnd(ShutdownConnection)\n        response.removeHeader(\"Content-Length\")\n      }\n\n      override fun newRequestBody(body: String): RequestBody = throw TestAbortedException(\"END_OF_STREAM not implemented for requests\")\n    }, ;\n\n    abstract fun setBody(\n      response: MockResponse.Builder,\n      content: Buffer?,\n      chunkSize: Int,\n    )\n\n    abstract fun newRequestBody(body: String): RequestBody\n\n    fun setBody(\n      response: MockResponse.Builder,\n      content: String?,\n      chunkSize: Int,\n    ) {\n      setBody(response, Buffer().writeUtf8(content!!), chunkSize)\n    }\n  }\n\n  internal enum class ProxyConfig {\n    NO_PROXY {\n      override fun connect(\n        server: MockWebServer,\n        client: OkHttpClient,\n      ): Call.Factory =\n        client\n          .newBuilder()\n          .proxy(Proxy.NO_PROXY)\n          .build()\n    },\n    CREATE_ARG {\n      override fun connect(\n        server: MockWebServer,\n        client: OkHttpClient,\n      ): Call.Factory =\n        client\n          .newBuilder()\n          .proxy(server.proxyAddress)\n          .build()\n    },\n    PROXY_SYSTEM_PROPERTY {\n      override fun connect(\n        server: MockWebServer,\n        client: OkHttpClient,\n      ): Call.Factory {\n        System.setProperty(\"proxyHost\", server.hostName)\n        System.setProperty(\"proxyPort\", server.port.toString())\n        return client\n      }\n    },\n    HTTP_PROXY_SYSTEM_PROPERTY {\n      override fun connect(\n        server: MockWebServer,\n        client: OkHttpClient,\n      ): Call.Factory {\n        System.setProperty(\"http.proxyHost\", server.hostName)\n        System.setProperty(\"http.proxyPort\", server.port.toString())\n        return client\n      }\n    },\n    HTTPS_PROXY_SYSTEM_PROPERTY {\n      override fun connect(\n        server: MockWebServer,\n        client: OkHttpClient,\n      ): Call.Factory {\n        System.setProperty(\"https.proxyHost\", server.hostName)\n        System.setProperty(\"https.proxyPort\", server.port.toString())\n        return client\n      }\n    }, ;\n\n    abstract fun connect(\n      server: MockWebServer,\n      client: OkHttpClient,\n    ): Call.Factory\n\n    fun connect(\n      server: MockWebServer,\n      client: OkHttpClient,\n      url: HttpUrl,\n    ): Call =\n      connect(server, client)\n        .newCall(Request(url))\n  }\n\n  private class RecordingTrustManager(\n    private val delegate: X509TrustManager,\n  ) : X509TrustManager {\n    val calls: MutableList<String> = ArrayList()\n\n    override fun getAcceptedIssuers(): Array<X509Certificate> = delegate.acceptedIssuers\n\n    override fun checkClientTrusted(\n      chain: Array<X509Certificate>,\n      authType: String,\n    ) {\n      calls.add(\"checkClientTrusted \" + certificatesToString(chain))\n    }\n\n    override fun checkServerTrusted(\n      chain: Array<X509Certificate>,\n      authType: String,\n    ) {\n      calls.add(\"checkServerTrusted \" + certificatesToString(chain))\n    }\n\n    private fun certificatesToString(certificates: Array<X509Certificate>): String {\n      val result: MutableList<String> = ArrayList()\n      for (certificate in certificates) {\n        result.add(certificate.subjectDN.toString() + \" \" + certificate.serialNumber)\n      }\n      return result.toString()\n    }\n  }\n\n  /**\n   * Tests that use this will fail unless boot classpath is set. Ex. `-Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317`\n   */\n  private fun enableProtocol(protocol: Protocol) {\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .protocols(Arrays.asList(protocol, Protocol.HTTP_1_1))\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.protocolNegotiationEnabled = true\n    server.protocols = client.protocols\n  }\n\n  /**\n   * Used during tests that involve TLS connection fallback attempts. OkHttp includes the\n   * TLS_FALLBACK_SCSV cipher on fallback connections. See [FallbackTestClientSocketFactory]\n   * for details.\n   */\n  private fun suppressTlsFallbackClientSocketFactory() = FallbackTestClientSocketFactory(handshakeCertificates.sslSocketFactory())\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/UrlComponentEncodingTester.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.startsWith\nimport kotlin.test.assertFailsWith\nimport kotlin.test.fail\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.internal.idn.Punycode\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\n\n/**\n * Tests how each code point is encoded and decoded in the context of each URL component.\n *\n * This supports [HttpUrlTest].\n */\nclass UrlComponentEncodingTester private constructor() {\n  private val encodings: MutableMap<Int, Encoding> = LinkedHashMap()\n\n  private fun allAscii(encoding: Encoding) =\n    apply {\n      for (i in 0..127) {\n        encodings[i] = encoding\n      }\n    }\n\n  fun override(\n    encoding: Encoding,\n    vararg codePoints: Int,\n  ) = apply {\n    for (codePoint in codePoints) {\n      encodings[codePoint] = encoding\n    }\n  }\n\n  fun nonPrintableAscii(encoding: Encoding) =\n    apply {\n      encodings[0x0] = encoding // Null character\n      encodings[0x1] = encoding // Start of Header\n      encodings[0x2] = encoding // Start of Text\n      encodings[0x3] = encoding // End of Text\n      encodings[0x4] = encoding // End of Transmission\n      encodings[0x5] = encoding // Enquiry\n      encodings[0x6] = encoding // Acknowledgment\n      encodings[0x7] = encoding // Bell\n      encodings['\\b'.code] = encoding // Backspace\n      encodings[0xb] = encoding // Vertical Tab\n      encodings[0xe] = encoding // Shift Out\n      encodings[0xf] = encoding // Shift In\n      encodings[0x10] = encoding // Data Link Escape\n      encodings[0x11] = encoding // Device Control 1 (oft. XON)\n      encodings[0x12] = encoding // Device Control 2\n      encodings[0x13] = encoding // Device Control 3 (oft. XOFF)\n      encodings[0x14] = encoding // Device Control 4\n      encodings[0x15] = encoding // Negative Acknowledgment\n      encodings[0x16] = encoding // Synchronous idle\n      encodings[0x17] = encoding // End of Transmission Block\n      encodings[0x18] = encoding // Cancel\n      encodings[0x19] = encoding // End of Medium\n      encodings[0x1a] = encoding // Substitute\n      encodings[0x1b] = encoding // Escape\n      encodings[0x1c] = encoding // File Separator\n      encodings[0x1d] = encoding // Group Separator\n      encodings[0x1e] = encoding // Record Separator\n      encodings[0x1f] = encoding // Unit Separator\n      encodings[0x7f] = encoding // Delete\n    }\n\n  fun nonAscii(encoding: Encoding) =\n    apply {\n      encodings[UNICODE_2] = encoding\n      encodings[UNICODE_3] = encoding\n      encodings[UNICODE_4] = encoding\n    }\n\n  fun test(component: Component) =\n    apply {\n      for ((codePoint, encoding) in encodings) {\n        val codePointString = Encoding.IDENTITY.encode(codePoint)\n        if (encoding == Encoding.FORBIDDEN) {\n          testForbidden(codePoint, codePointString, component)\n          continue\n        }\n        if (encoding == Encoding.PUNYCODE) {\n          testPunycode(codePointString, component)\n          continue\n        }\n        testEncodeAndDecode(codePoint, codePointString, component)\n        if (encoding == Encoding.SKIP) continue\n        testParseOriginal(codePoint, codePointString, encoding, component)\n        testParseAlreadyEncoded(codePoint, encoding, component)\n\n        val platform = urlComponentEncodingTesterJvmPlatform(component)\n        platform.test(codePoint, codePointString, encoding, component)\n      }\n    }\n\n  private fun testParseAlreadyEncoded(\n    codePoint: Int,\n    encoding: Encoding,\n    component: Component,\n  ) {\n    val expected = component.canonicalize(encoding.encode(codePoint))\n    val urlString = component.urlString(expected)\n    val url = urlString.toHttpUrl()\n    val actual = component.encodedValue(url)\n    if (actual != expected) {\n      fail(\"Encoding $component $codePoint using $encoding: '$actual' != '$expected'\")\n    }\n  }\n\n  private fun testEncodeAndDecode(\n    codePoint: Int,\n    codePointString: String,\n    component: Component,\n  ) {\n    val builder = \"http://host/\".toHttpUrl().newBuilder()\n    component[builder] = codePointString\n    val url = builder.build()\n    val expected = component.canonicalize(codePointString)\n    val actual = component[url]\n    if (expected != actual) {\n      fail(\"Roundtrip $component $codePoint $url $expected != $actual\")\n    }\n  }\n\n  private fun testParseOriginal(\n    codePoint: Int,\n    codePointString: String,\n    encoding: Encoding,\n    component: Component,\n  ) {\n    val expected = encoding.encode(codePoint)\n    if (encoding !== Encoding.PERCENT) return\n    val urlString = component.urlString(codePointString)\n    val url = urlString.toHttpUrl()\n    val actual = component.encodedValue(url)\n    if (actual != expected) {\n      fail(\"Encoding $component $codePoint using $encoding: '$actual' != '$expected'\")\n    }\n  }\n\n  private fun testForbidden(\n    codePoint: Int,\n    codePointString: String,\n    component: Component,\n  ) {\n    val builder = \"http://host/\".toHttpUrl().newBuilder()\n    assertFailsWith<IllegalArgumentException> {\n      component[builder] = codePointString\n    }\n  }\n\n  private fun testPunycode(\n    codePointString: String,\n    component: Component,\n  ) {\n    val builder = \"http://host/\".toHttpUrl().newBuilder()\n    component[builder] = codePointString\n    val url = builder.build()\n    assertThat(url.host).startsWith(Punycode.PREFIX_STRING)\n  }\n\n  enum class Encoding {\n    IDENTITY {\n      override fun encode(codePoint: Int): String = String(codePoint)\n    },\n    PERCENT {\n      override fun encode(codePoint: Int): String {\n        val utf8 = IDENTITY.encode(codePoint).encodeUtf8()\n        val percentEncoded = Buffer()\n        for (i in 0 until utf8.size) {\n          percentEncoded\n            .writeUtf8(\"%\")\n            .writeUtf8(ByteString.of(utf8[i]).hex().uppercase())\n        }\n        return percentEncoded.readUtf8()\n      }\n    },\n\n    /** URLs that contain this character in this component are invalid.  */\n    FORBIDDEN,\n\n    /** Hostnames that contain this character are encoded with punycode.  */\n    PUNYCODE,\n\n    /** This code point is special and should not be tested.  */\n    SKIP,\n\n    ;\n\n    open fun encode(codePoint: Int): String = throw UnsupportedOperationException()\n  }\n\n  enum class Component {\n    USER {\n      override fun urlString(value: String): String = \"http://$value@example.com/\"\n\n      override fun encodedValue(url: HttpUrl): String = url.encodedUsername\n\n      override operator fun set(\n        builder: HttpUrl.Builder,\n        value: String,\n      ) {\n        builder.username(value)\n      }\n\n      override operator fun get(url: HttpUrl): String = url.username\n    },\n\n    PASSWORD {\n      override fun urlString(value: String): String = \"http://:$value@example.com/\"\n\n      override fun encodedValue(url: HttpUrl): String = url.encodedPassword\n\n      override operator fun set(\n        builder: HttpUrl.Builder,\n        value: String,\n      ) {\n        builder.password(value)\n      }\n\n      override operator fun get(url: HttpUrl): String = url.password\n    },\n\n    HOST {\n      override fun urlString(value: String): String = \"http://a${value}z.com/\"\n\n      override fun encodedValue(url: HttpUrl): String = get(url)\n\n      override operator fun set(\n        builder: HttpUrl.Builder,\n        value: String,\n      ) {\n        builder.host(\"a${value}z.com\")\n      }\n\n      override operator fun get(url: HttpUrl): String {\n        val host = url.host\n        return host.substring(1, host.length - 5).lowercase()\n      }\n\n      override fun canonicalize(s: String): String = s.lowercase()\n    },\n\n    PATH {\n      override fun urlString(value: String): String = \"http://example.com/a${value}z/\"\n\n      override fun encodedValue(url: HttpUrl): String {\n        val path = url.encodedPath\n        return path.substring(2, path.length - 2)\n      }\n\n      override operator fun set(\n        builder: HttpUrl.Builder,\n        value: String,\n      ) {\n        builder.addPathSegment(\"a${value}z\")\n      }\n\n      override operator fun get(url: HttpUrl): String {\n        val pathSegment = url.pathSegments[0]\n        return pathSegment.substring(1, pathSegment.length - 1)\n      }\n    },\n\n    QUERY {\n      override fun urlString(value: String): String = \"http://example.com/?a${value}z\"\n\n      override fun encodedValue(url: HttpUrl): String {\n        val query = url.encodedQuery\n        return query!!.substring(1, query.length - 1)\n      }\n\n      override operator fun set(\n        builder: HttpUrl.Builder,\n        value: String,\n      ) {\n        builder.query(\"a${value}z\")\n      }\n\n      override operator fun get(url: HttpUrl): String {\n        val query = url.query\n        return query!!.substring(1, query.length - 1)\n      }\n    },\n\n    QUERY_VALUE {\n      override fun urlString(value: String): String = \"http://example.com/?q=a${value}z\"\n\n      override fun encodedValue(url: HttpUrl): String {\n        val query = url.encodedQuery\n        return query!!.substring(3, query.length - 1)\n      }\n\n      override operator fun set(\n        builder: HttpUrl.Builder,\n        value: String,\n      ) {\n        builder.addQueryParameter(\"q\", \"a${value}z\")\n      }\n\n      override operator fun get(url: HttpUrl): String {\n        val value = url.queryParameter(\"q\")\n        return value!!.substring(1, value.length - 1)\n      }\n    },\n\n    FRAGMENT {\n      override fun urlString(value: String): String = \"http://example.com/#a${value}z\"\n\n      override fun encodedValue(url: HttpUrl): String {\n        val fragment = url.encodedFragment\n        return fragment!!.substring(1, fragment.length - 1)\n      }\n\n      override operator fun set(\n        builder: HttpUrl.Builder,\n        value: String,\n      ) {\n        builder.fragment(\"a${value}z\")\n      }\n\n      override operator fun get(url: HttpUrl): String {\n        val fragment = url.fragment\n        return fragment!!.substring(1, fragment.length - 1)\n      }\n    }, ;\n\n    abstract fun urlString(value: String): String\n\n    abstract fun encodedValue(url: HttpUrl): String\n\n    abstract operator fun set(\n      builder: HttpUrl.Builder,\n      value: String,\n    )\n\n    abstract operator fun get(url: HttpUrl): String\n\n    /**\n     * Returns a character equivalent to 's' in this component. This is used to convert hostname\n     * characters to lowercase.\n     */\n    open fun canonicalize(s: String): String = s\n  }\n\n  /** Tests integration between HttpUrl and the host platform's built-in URL classes, if any. */\n  open class Platform {\n    open fun test(\n      codePoint: Int,\n      codePointString: String,\n      encoding: Encoding,\n      component: Component,\n    ) {\n    }\n  }\n\n  companion object {\n    /** Arbitrary code point that's 2 bytes in UTF-8 and valid in IdnaMappingTable.txt. */\n    private const val UNICODE_2 = 0x1a5\n\n    /** Arbitrary code point that's 3 bytes in UTF-8 and valid in IdnaMappingTable.txt. */\n    private const val UNICODE_3 = 0x2202\n\n    /** Arbitrary code point that's 4 bytes in UTF-8 and valid in IdnaMappingTable.txt. */\n    private const val UNICODE_4 = 0x1d11e\n\n    /**\n     * Returns a new instance configured with a default encode set for the ASCII range. The specific\n     * rules vary per-component: for example, '?' may be identity-encoded in a fragment, but must be\n     * percent-encoded in a path.\n     *\n     * See https://url.spec.whatwg.org/#percent-encoded-bytes\n     */\n    fun newInstance(): UrlComponentEncodingTester =\n      UrlComponentEncodingTester()\n        .allAscii(Encoding.IDENTITY)\n        .nonPrintableAscii(Encoding.PERCENT)\n        .override(\n          Encoding.SKIP,\n          '\\t'.code,\n          '\\n'.code,\n          '\\u000c'.code,\n          '\\r'.code,\n        ).override(\n          Encoding.PERCENT,\n          ' '.code,\n          '\"'.code,\n          '#'.code,\n          '<'.code,\n          '>'.code,\n          '?'.code,\n          '`'.code,\n        ).override(\n          Encoding.PERCENT,\n          UNICODE_2,\n          UNICODE_3,\n          UNICODE_4,\n        )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/UrlComponentEncodingTesterJvm.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.fail\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okhttp3.UrlComponentEncodingTester.Component\n\nfun urlComponentEncodingTesterJvmPlatform(component: Component): UrlComponentEncodingTester.Platform =\n  when (component) {\n    Component.USER -> {\n      UrlComponentEncodingTesterJvmPlatform()\n        .escapeForUri('%'.code)\n    }\n\n    Component.PASSWORD -> {\n      UrlComponentEncodingTesterJvmPlatform()\n        .escapeForUri('%'.code)\n    }\n\n    Component.HOST -> {\n      UrlComponentEncodingTesterJvmPlatform()\n        .stripForUri(\n          '\\\"'.code,\n          '<'.code,\n          '>'.code,\n          '^'.code,\n          '`'.code,\n          '{'.code,\n          '|'.code,\n          '}'.code,\n        )\n    }\n\n    Component.PATH -> {\n      UrlComponentEncodingTesterJvmPlatform()\n        .escapeForUri(\n          '%'.code,\n          '['.code,\n          ']'.code,\n        )\n    }\n\n    Component.QUERY -> {\n      UrlComponentEncodingTesterJvmPlatform()\n        .escapeForUri(\n          '%'.code,\n          '\\\\'.code,\n          '^'.code,\n          '`'.code,\n          '{'.code,\n          '|'.code,\n          '}'.code,\n        )\n    }\n\n    Component.QUERY_VALUE -> {\n      UrlComponentEncodingTesterJvmPlatform()\n        .escapeForUri(\n          '%'.code,\n          '\\\\'.code,\n          '^'.code,\n          '`'.code,\n          '{'.code,\n          '|'.code,\n          '}'.code,\n        )\n    }\n\n    Component.FRAGMENT -> {\n      UrlComponentEncodingTesterJvmPlatform()\n        .escapeForUri(\n          '%'.code,\n          ' '.code,\n          '\"'.code,\n          '#'.code,\n          '<'.code,\n          '>'.code,\n          '\\\\'.code,\n          '^'.code,\n          '`'.code,\n          '{'.code,\n          '|'.code,\n          '}'.code,\n        )\n    }\n  }\n\nprivate class UrlComponentEncodingTesterJvmPlatform : UrlComponentEncodingTester.Platform() {\n  private val uriEscapedCodePoints = StringBuilder()\n  private val uriStrippedCodePoints = StringBuilder()\n\n  /**\n   * Configure code points to be escaped for conversion to `java.net.URI`. That class is more\n   * strict than the others.\n   */\n  fun escapeForUri(vararg codePoints: Int) =\n    apply {\n      uriEscapedCodePoints.append(String(*codePoints))\n    }\n\n  /**\n   * Configure code points to be stripped in conversion to `java.net.URI`. That class is more\n   * strict than the others.\n   */\n  fun stripForUri(vararg codePoints: Int) =\n    apply {\n      uriStrippedCodePoints.append(String(*codePoints))\n    }\n\n  override fun test(\n    codePoint: Int,\n    codePointString: String,\n    encoding: UrlComponentEncodingTester.Encoding,\n    component: Component,\n  ) {\n    testToUrl(codePoint, encoding, component)\n    testFromUrl(codePoint, encoding, component)\n    testUri(codePoint, codePointString, encoding, component)\n  }\n\n  private fun testToUrl(\n    codePoint: Int,\n    encoding: UrlComponentEncodingTester.Encoding,\n    component: Component,\n  ) {\n    val encoded = encoding.encode(codePoint)\n    val httpUrl = component.urlString(encoded).toHttpUrl()\n    val javaNetUrl = httpUrl.toUrl()\n    if (javaNetUrl.toString() != javaNetUrl.toString()) {\n      fail(\"Encoding $component $codePoint using $encoding\")\n    }\n  }\n\n  private fun testFromUrl(\n    codePoint: Int,\n    encoding: UrlComponentEncodingTester.Encoding,\n    component: Component,\n  ) {\n    val encoded = encoding.encode(codePoint)\n    val httpUrl = component.urlString(encoded).toHttpUrl()\n    val toAndFromJavaNetUrl = httpUrl.toUrl().toHttpUrlOrNull()\n    if (toAndFromJavaNetUrl != httpUrl) {\n      fail(\"Encoding $component $codePoint using $encoding\")\n    }\n  }\n\n  private fun testUri(\n    codePoint: Int,\n    codePointString: String,\n    encoding: UrlComponentEncodingTester.Encoding,\n    component: Component,\n  ) {\n    if (codePoint == '%'.code) return\n    val encoded = encoding.encode(codePoint)\n    val httpUrl = component.urlString(encoded).toHttpUrl()\n    val uri = httpUrl.toUri()\n    val toAndFromUri = uri.toHttpUrlOrNull()\n    val uriStripped = uriStrippedCodePoints.indexOf(codePointString) != -1\n    if (uriStripped) {\n      if (uri.toString() != component.urlString(\"\")) {\n        fail(\"Encoding $component $codePoint using $encoding\")\n      }\n      return\n    }\n\n    // If the URI has more escaping than the HttpURL, check that the decoded values still match.\n    val uriEscaped = uriEscapedCodePoints.indexOf(codePointString) != -1\n    if (uriEscaped) {\n      if (uri.toString() == httpUrl.toString()) {\n        fail(\"Encoding $component $codePoint using $encoding\")\n      }\n      if (component[toAndFromUri!!] != codePointString) {\n        fail(\"Encoding $component $codePoint using $encoding\")\n      }\n      return\n    }\n\n    // Check that the URI and HttpURL have the exact same escaping.\n    if (toAndFromUri != httpUrl) {\n      fail(\"Encoding $component $codePoint using $encoding\")\n    }\n    if (uri.toString() != httpUrl.toString()) {\n      fail(\"Encoding $component $codePoint using $encoding\")\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/WebPlatformToAsciiData.kt",
    "content": "/*\n * Copyright (C) 2023 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.decodeFromString\nimport kotlinx.serialization.json.Json\n\n/**\n * A test from the [Web Platform To ASCII](https://github.com/web-platform-tests/wpt/blob/master/url/resources/toascii.json).\n *\n * Each test is a line of the file `toascii.json`.\n */\n@Serializable\nclass WebPlatformToAsciiData {\n  var input: String? = null\n  var output: String? = null\n  var comment: String? = null\n\n  override fun toString() = \"input=$input output=$output\"\n\n  companion object {\n    fun load(): List<WebPlatformToAsciiData> {\n      val path = okHttpRoot / \"okhttp/src/jvmTest/resources/web-platform-test-toascii.json\"\n      return SYSTEM_FILE_SYSTEM.read(path) {\n        Json.decodeFromString<List<WebPlatformToAsciiData>>(readUtf8())\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/WebPlatformToAsciiTest.kt",
    "content": "/*\n * Copyright (C) 2023 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport kotlin.test.Test\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\n\n/** Runs the web platform ToAscii tests. */\nclass WebPlatformToAsciiTest {\n  @Suppress(\"ktlint:standard:max-line-length\")\n  val knownFailures =\n    setOf(\n      // OkHttp rejects empty labels.\n      \"x..xn--zca\",\n      \"x..ß\",\n      // OkHttp rejects labels longer than 63 code points, the web platform tests don't.\n      \"x01234567890123456789012345678901234567890123456789012345678901x.xn--zca\",\n      \"x01234567890123456789012345678901234567890123456789012345678901x.ß\",\n      \"x01234567890123456789012345678901234567890123456789012345678901x\",\n      \"x01234567890123456789012345678901234567890123456789012345678901†\",\n      // OkHttp rejects domain names longer than 253 code points, the web platform tests don't.\n      \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.x\",\n      \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.xn--zca\",\n      \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.ß\",\n      // OkHttp does not reject invalid Punycode.\n      \"xn--a\",\n      \"xn--a.ß\",\n      \"xn--a.xn--zca\",\n      \"xn--a-yoc\",\n      // OkHttp doesn't reject U+FFFD encoded in Punycode.\n      \"xn--zn7c.com\",\n      // OkHttp doesn't reject a U+200D. https://www.rfc-editor.org/rfc/rfc5892.html#appendix-A.2\n      \"xn--1ug.example\",\n      // OkHttp doesn't implement CheckJoiners.\n      \"\\u200D.example\",\n      // OkHttp doesn't implement CheckBidi.\n      \"يa\",\n    )\n\n  @Test\n  fun test() {\n    val list = WebPlatformToAsciiData.load()\n    val failures = mutableListOf<Throwable>()\n    for (entry in list) {\n      var failure: Throwable? = null\n      try {\n        testToAscii(entry.input!!, entry.output, entry.comment)\n      } catch (e: Throwable) {\n        failure = e\n      }\n\n      if (entry.input in knownFailures) {\n        if (failure == null) failures += AssertionError(\"known failure didn't fail: $entry\")\n      } else {\n        if (failure != null) failures += failure\n      }\n    }\n\n    if (failures.isNotEmpty()) {\n      for (failure in failures) {\n        println(failure)\n      }\n      throw failures.first()\n    }\n  }\n\n  private fun testToAscii(\n    input: String,\n    output: String?,\n    comment: String?,\n  ) {\n    val url = \"https://$input/\".toHttpUrlOrNull()\n    assertThat(url?.host, name = comment ?: input).isEqualTo(output)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/WebPlatformUrlTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport okhttp3.HttpUrl.Companion.defaultPort\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.HttpUrl.Companion.toHttpUrlOrNull\nimport okio.buffer\nimport okio.source\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ArgumentsSource\n\n/** Runs the web platform URL tests against Java URL models.  */\nclass WebPlatformUrlTest {\n  class TestDataParamProvider : SimpleProvider() {\n    override fun arguments() = ArrayList<Any>(loadTests())\n  }\n\n  /** Test how [HttpUrl] does against the web platform test suite.  */\n  @ArgumentsSource(TestDataParamProvider::class)\n  @ParameterizedTest\n  fun httpUrl(testData: WebPlatformUrlTestData) {\n    if (!testData.scheme.isEmpty() && !HTTP_URL_SCHEMES.contains(testData.scheme)) {\n      System.err.println(\"Ignoring unsupported scheme ${testData.scheme}\")\n      return\n    }\n\n    if (!testData.base!!.startsWith(\"https:\") &&\n      !testData.base!!.startsWith(\"http:\") &&\n      testData.base != \"about:blank\"\n    ) {\n      System.err.println(\"Ignoring unsupported base ${testData.base}\")\n      return\n    }\n\n    try {\n      testHttpUrl(testData)\n      if (KNOWN_FAILURES.contains(testData.toString())) {\n        System.err.println(\"Expected failure but was success: $testData\")\n      }\n    } catch (e: Throwable) {\n      if (KNOWN_FAILURES.contains(testData.toString())) {\n        System.err.println(\"Ignoring known failure: $testData\")\n        e.printStackTrace()\n      } else {\n        throw e\n      }\n    }\n  }\n\n  private fun testHttpUrl(testData: WebPlatformUrlTestData) {\n    val url =\n      when (testData.base) {\n        \"about:blank\" -> testData.input!!.toHttpUrlOrNull()\n        else -> testData.base!!.toHttpUrl().resolve(testData.input!!)\n      }\n\n    if (testData.expectParseFailure()) {\n      assertThat(url, \"Expected URL to fail parsing\").isNull()\n      return\n    }\n\n    assertThat(url, \"Expected URL to parse successfully, but was null\")\n      .isNotNull()\n    val effectivePort =\n      when {\n        url!!.port != defaultPort(url.scheme) -> Integer.toString(url.port)\n        else -> \"\"\n      }\n    val effectiveQuery =\n      when {\n        url.encodedQuery != null -> \"?\" + url.encodedQuery\n        else -> \"\"\n      }\n    val effectiveFragment =\n      when {\n        url.encodedFragment != null -> \"#\" + url.encodedFragment\n        else -> \"\"\n      }\n    val effectiveHost =\n      when {\n        url.host.contains(\":\") -> \"[\" + url.host + \"]\"\n        else -> url.host\n      }\n    assertThat(url.scheme, \"scheme\").isEqualTo(testData.scheme)\n    assertThat(effectiveHost, \"host\").isEqualTo(testData.host)\n    assertThat(effectivePort, \"port\").isEqualTo(testData.port)\n    assertThat(url.encodedPath, \"path\").isEqualTo(testData.path)\n    assertThat(effectiveQuery, \"query\").isEqualTo(testData.query)\n    assertThat(effectiveFragment, \"fragment\").isEqualTo(testData.fragment)\n  }\n\n  companion object {\n    private val HTTP_URL_SCHEMES = listOf(\"http\", \"https\")\n    private val KNOWN_FAILURES =\n      listOf(\n        \"Parsing: <http://example\\t.\\norg> against <http://example.org/foo/bar>\",\n        \"Parsing: <http://f:0/c> against <http://example.org/foo/bar>\",\n        \"Parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>\",\n        \"Parsing: <http://f:\\n/c> against <http://example.org/foo/bar>\",\n        \"Parsing: <http://f:999999/c> against <http://example.org/foo/bar>\",\n        \"Parsing: <http://192.0x00A80001> against <about:blank>\",\n        \"Parsing: <http://%30%78%63%30%2e%30%32%35%30.01> against <http://other.com/>\",\n        \"Parsing: <http://192.168.0.257> against <http://other.com/>\",\n        \"Parsing: <http://０Ｘｃ０．０２５０．０１> against <http://other.com/>\",\n      )\n\n    private fun loadTests(): List<WebPlatformUrlTestData> {\n      val resourceAsStream =\n        WebPlatformUrlTest::class.java.getResourceAsStream(\n          \"/web-platform-test-urltestdata.txt\",\n        )\n      val source = resourceAsStream.source().buffer()\n      return WebPlatformUrlTestData.load(source)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/WebPlatformUrlTestData.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okhttp3.internal.format\nimport okio.Buffer\nimport okio.BufferedSource\n\n/**\n * A test from the [Web Platform URL test suite](https://github.com/web-platform-tests/wpt/blob/master/url/resources/urltestdata.json).\n *\n * Each test is a line of the file `urltestdata.txt`. The format is informally specified by its\n * JavaScript parser `urltestparser.js` with which this class attempts to be compatible.\n *\n * Each line of the `urltestdata.txt` file specifies a test. Lines look like this:\n *\n * ```\n * http://example\\t.\\norg http://example.org/foo/bar s:http h:example.org p:/\n * ```\n */\nclass WebPlatformUrlTestData {\n  var input: String? = null\n  var base: String? = null\n  var scheme = \"\"\n  var username = \"\"\n  var password: String? = null\n  var host = \"\"\n  var port = \"\"\n  var path = \"\"\n  var query = \"\"\n  var fragment = \"\"\n\n  fun expectParseFailure() = scheme.isEmpty()\n\n  private operator fun set(\n    name: String,\n    value: String,\n  ) {\n    when (name) {\n      \"s\" -> scheme = value\n      \"u\" -> username = value\n      \"pass\" -> password = value\n      \"h\" -> host = value\n      \"port\" -> port = value\n      \"p\" -> path = value\n      \"q\" -> query = value\n      \"f\" -> fragment = value\n      else -> throw IllegalArgumentException(\"unexpected attribute: $value\")\n    }\n  }\n\n  override fun toString(): String = format(\"Parsing: <%s> against <%s>\", input!!, base!!)\n\n  companion object {\n    fun load(source: BufferedSource): List<WebPlatformUrlTestData> {\n      val list = mutableListOf<WebPlatformUrlTestData>()\n      while (true) {\n        val line = source.readUtf8Line() ?: break\n        if (line.isEmpty() || line.startsWith(\"#\")) continue\n\n        var i = 0\n        val parts = line.split(Regex(\" \")).toTypedArray()\n\n        val element = WebPlatformUrlTestData()\n        element.input = unescape(parts[i++])\n\n        val base = if (i < parts.size) parts[i++] else null\n        element.base =\n          when {\n            base == null || base.isEmpty() -> list[list.size - 1].base\n            else -> unescape(base)\n          }\n\n        while (i < parts.size) {\n          val piece = parts[i]\n          if (piece.startsWith(\"#\")) {\n            i++\n            continue\n          }\n          val nameAndValue = piece.split(Regex(\":\"), 2).toTypedArray()\n          element[nameAndValue[0]] = unescape(nameAndValue[1])\n          i++\n        }\n\n        list += element\n      }\n      return list\n    }\n\n    private fun unescape(s: String): String =\n      buildString {\n        val buffer = Buffer().writeUtf8(s)\n        while (!buffer.exhausted()) {\n          val c = buffer.readUtf8CodePoint()\n          if (c != '\\\\'.code) {\n            append(c.toChar())\n            continue\n          }\n          when (buffer.readUtf8CodePoint()) {\n            '\\\\'.code -> append('\\\\')\n            '#'.code -> append('#')\n            'n'.code -> append('\\n')\n            'r'.code -> append('\\r')\n            's'.code -> append(' ')\n            't'.code -> append('\\t')\n            'f'.code -> append('\\u000c')\n            'u'.code -> append(buffer.readUtf8(4).toInt(16).toChar())\n            else -> throw IllegalArgumentException(\"unexpected escape character in $s\")\n          }\n        }\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/WholeOperationTimeoutTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.HttpURLConnection\nimport java.time.Duration\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicReference\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.TestUtil.repeat\nimport okhttp3.testing.Flaky\nimport okio.BufferedSink\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Timeout(30)\n@Tag(\"Slow\")\nclass WholeOperationTimeoutTest {\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n  private val client = clientTestRule.newClient()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @Test\n  fun defaultConfigIsNoTimeout() {\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    assertThat(call.timeout().timeoutNanos()).isEqualTo(0)\n  }\n\n  @Test\n  fun configureClientDefault() {\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val timeoutClient =\n      client\n        .newBuilder()\n        .callTimeout(Duration.ofMillis(456))\n        .build()\n    val call = timeoutClient.newCall(request)\n    assertThat(call.timeout().timeoutNanos())\n      .isEqualTo(TimeUnit.MILLISECONDS.toNanos(456))\n  }\n\n  @Test\n  fun timeoutWritingRequest() {\n    server.enqueue(MockResponse())\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(sleepingRequestBody(500))\n        .build()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n      assertThat(call.isCanceled()).isTrue()\n    }\n  }\n\n  @Test\n  fun timeoutWritingRequestWithEnqueue() {\n    server.enqueue(MockResponse())\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(sleepingRequestBody(500))\n        .build()\n    val latch = CountDownLatch(1)\n    val exceptionRef = AtomicReference<Throwable>()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    call.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          exceptionRef.set(e)\n          latch.countDown()\n        }\n\n        @Throws(IOException::class)\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          response.close()\n          latch.countDown()\n        }\n      },\n    )\n    latch.await()\n    assertThat(call.isCanceled()).isTrue()\n    assertThat(exceptionRef.get()).isNotNull()\n  }\n\n  @Test\n  fun timeoutProcessing() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n      assertThat(call.isCanceled()).isTrue()\n    }\n  }\n\n  @Test\n  fun timeoutProcessingWithEnqueue() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val latch = CountDownLatch(1)\n    val exceptionRef = AtomicReference<Throwable>()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    call.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          exceptionRef.set(e)\n          latch.countDown()\n        }\n\n        @Throws(IOException::class)\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          response.close()\n          latch.countDown()\n        }\n      },\n    )\n    latch.await()\n    assertThat(call.isCanceled()).isTrue()\n    assertThat(exceptionRef.get()).isNotNull()\n  }\n\n  @Test\n  fun timeoutReadingResponse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(BIG_ENOUGH_BODY)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    val response = call.execute()\n    Thread.sleep(500)\n    assertFailsWith<IOException> {\n      response.body.source().readUtf8()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n      assertThat(call.isCanceled()).isTrue()\n    }\n  }\n\n  @Test\n  fun timeoutReadingResponseWithEnqueue() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(BIG_ENOUGH_BODY)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val latch = CountDownLatch(1)\n    val exceptionRef = AtomicReference<Throwable>()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    call.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          latch.countDown()\n        }\n\n        @Throws(IOException::class)\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          try {\n            Thread.sleep(500)\n          } catch (e: InterruptedException) {\n            throw AssertionError()\n          }\n          assertFailsWith<IOException> {\n            response.body.source().readUtf8()\n          }.also { expected ->\n            exceptionRef.set(expected)\n            latch.countDown()\n          }\n        }\n      },\n    )\n    latch.await()\n    assertThat(call.isCanceled()).isTrue()\n    assertThat(exceptionRef.get()).isNotNull()\n  }\n\n  @Test\n  fun singleTimeoutForAllFollowUpRequests() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .setHeader(\"Location\", \"/b\")\n        .headersDelay(100, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .setHeader(\"Location\", \"/c\")\n        .headersDelay(100, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .setHeader(\"Location\", \"/d\")\n        .headersDelay(100, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .setHeader(\"Location\", \"/e\")\n        .headersDelay(100, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .setHeader(\"Location\", \"/f\")\n        .headersDelay(100, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    server.enqueue(MockResponse())\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/a\"))\n        .build()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n      assertThat(call.isCanceled()).isTrue()\n    }\n  }\n\n  @Test\n  fun timeoutFollowingRedirectOnNewConnection() {\n    val otherServer = MockWebServer()\n    otherServer.start()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_MOVED_TEMP)\n        .setHeader(\"Location\", otherServer.url(\"/\"))\n        .build(),\n    )\n    otherServer.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val call = client.newCall(request)\n    call.timeout().timeout(250, TimeUnit.MILLISECONDS)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n      assertThat(call.isCanceled()).isTrue()\n    }\n  }\n\n  @Flaky\n  @Test\n  fun noTimeout() {\n    // Flaky https://github.com/square/okhttp/issues/5304\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(250, TimeUnit.MILLISECONDS)\n        .body(BIG_ENOUGH_BODY)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .post(sleepingRequestBody(250))\n        .build()\n    val call = client.newCall(request)\n    call.timeout().timeout(2000, TimeUnit.MILLISECONDS)\n    val response = call.execute()\n    Thread.sleep(250)\n    response.body.source().readUtf8()\n    response.close()\n    assertThat(call.isCanceled()).isFalse()\n  }\n\n  private fun sleepingRequestBody(sleepMillis: Int): RequestBody =\n    object : RequestBody() {\n      override fun contentType(): MediaType? = \"text/plain\".toMediaTypeOrNull()\n\n      @Throws(IOException::class)\n      override fun writeTo(sink: BufferedSink) {\n        try {\n          sink.writeUtf8(\"abc\")\n          sink.flush()\n          Thread.sleep(sleepMillis.toLong())\n          sink.writeUtf8(\"def\")\n        } catch (e: InterruptedException) {\n          throw InterruptedIOException()\n        }\n      }\n    }\n\n  companion object {\n    /** A large response body. Smaller bodies might successfully read after the socket is closed!  */\n    private val BIG_ENOUGH_BODY = repeat('a', 64 * 1024)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/DoubleInetAddressDns.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport java.net.InetAddress\nimport okhttp3.Dns\n\n/**\n * A network that always resolves two IP addresses per host. Use this when testing route selection\n * fallbacks to guarantee that a fallback address is available.\n */\nclass DoubleInetAddressDns : Dns {\n  override fun lookup(hostname: String): List<InetAddress> {\n    val addresses = Dns.SYSTEM.lookup(hostname)\n    return listOf(addresses[0], addresses[0])\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/HostnamesTest.kt",
    "content": "/*\n * Copyright (C) 2023 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport kotlin.test.Test\n\nclass HostnamesTest {\n  @Test\n  fun canonicalizeInetAddressNotMapped() {\n    val addressA = decodeIpv6(\"::1\")!!\n    assertThat(canonicalizeInetAddress(addressA)).isEqualTo(addressA)\n\n    val addressB = byteArrayOf(127, 0, 0, 1)\n    assertThat(canonicalizeInetAddress(addressB)).isEqualTo(addressB)\n\n    val addressC = byteArrayOf(192.toByte(), 168.toByte(), 0, 1)\n    assertThat(canonicalizeInetAddress(addressC)).isEqualTo(addressC)\n\n    val addressD = decodeIpv6(\"abcd:ef01:2345:6789:abcd:ef01:2345:6789\")!!\n    assertThat(canonicalizeInetAddress(addressD)).isEqualTo(addressD)\n\n    val addressE = decodeIpv6(\"2001:db8::1:0:0:1\")!!\n    assertThat(canonicalizeInetAddress(addressE)).isEqualTo(addressE)\n\n    val addressF = decodeIpv6(\"0:0:0:0:0:ffff:7f00:1\")!!\n    assertThat(canonicalizeInetAddress(addressF)).isEqualTo(addressB)\n\n    val addressG = decodeIpv6(\"0:0:0:0:0:ffff:c0a8:1\")!!\n    assertThat(canonicalizeInetAddress(addressG)).isEqualTo(addressC)\n  }\n\n  @Test\n  fun canonicalizeInetAddressMapped() {\n    val addressAIpv6 = decodeIpv6(\"0:0:0:0:0:ffff:7f00:1\")!!\n    val addressAIpv4 = byteArrayOf(127, 0, 0, 1)\n    assertThat(canonicalizeInetAddress(addressAIpv6)).isEqualTo(addressAIpv4)\n\n    val addressBIpv6 = decodeIpv6(\"0:0:0:0:0:ffff:c0a8:1\")!!\n    val addressBIpv4 = byteArrayOf(192.toByte(), 168.toByte(), 0, 1)\n    assertThat(canonicalizeInetAddress(addressBIpv6)).isEqualTo(addressBIpv4)\n  }\n\n  @Test\n  fun canonicalizeInetAddressIPv6RepresentationOfCompatibleIPV4() {\n    val addressAIpv6 = decodeIpv6(\"::192.168.0.1\")!!\n    assertThat(canonicalizeInetAddress(addressAIpv6)).isEqualTo(\n      ByteArray(12) +\n        byteArrayOf(\n          192.toByte(),\n          168.toByte(),\n          0,\n          1,\n        ),\n    )\n  }\n\n  @Test\n  fun canonicalizeInetAddressIPv6RepresentationOfMappedIPV4() {\n    val addressAIpv6 = decodeIpv6(\"::FFFF:192.168.0.1\")!!\n    assertThat(canonicalizeInetAddress(addressAIpv6)).isEqualTo(byteArrayOf(192.toByte(), 168.toByte(), 0, 1))\n  }\n\n  @Test\n  fun inet4AddressToAscii() {\n    assertThat(\n      inet4AddressToAscii(\n        byteArrayOf(0, 0, 0, 0),\n      ),\n    ).isEqualTo(\"0.0.0.0\")\n\n    assertThat(\n      inet4AddressToAscii(\n        byteArrayOf(1, 2, 3, 4),\n      ),\n    ).isEqualTo(\"1.2.3.4\")\n\n    assertThat(\n      inet4AddressToAscii(\n        byteArrayOf(127, 0, 0, 1),\n      ),\n    ).isEqualTo(\"127.0.0.1\")\n\n    assertThat(\n      inet4AddressToAscii(\n        byteArrayOf(192.toByte(), 168.toByte(), 0, 1),\n      ),\n    ).isEqualTo(\"192.168.0.1\")\n\n    assertThat(\n      inet4AddressToAscii(\n        byteArrayOf(252.toByte(), 253.toByte(), 254.toByte(), 255.toByte()),\n      ),\n    ).isEqualTo(\"252.253.254.255\")\n\n    assertThat(\n      inet4AddressToAscii(\n        byteArrayOf(255.toByte(), 255.toByte(), 255.toByte(), 255.toByte()),\n      ),\n    ).isEqualTo(\"255.255.255.255\")\n  }\n\n  private fun decodeIpv6(input: String): ByteArray? = decodeIpv6(input, 0, input.length)\n\n  @Test\n  fun testToCanonicalHost() {\n    // IPv4\n    assertThat(\"127.0.0.1\".toCanonicalHost()).isEqualTo(\"127.0.0.1\")\n    assertThat(\"1.2.3.4\".toCanonicalHost()).isEqualTo(\"1.2.3.4\")\n\n    // IPv6\n    assertThat(\"::1\".toCanonicalHost()).isEqualTo(\"::1\")\n    assertThat(\"2001:db8::1\".toCanonicalHost()).isEqualTo(\"2001:db8::1\")\n    assertThat(\"::ffff:192.168.0.1\".toCanonicalHost()).isEqualTo(\"192.168.0.1\")\n    assertThat(\n      \"FEDC:BA98:7654:3210:FEDC:BA98:7654:3210\".toCanonicalHost(),\n    ).isEqualTo(\n      \"fedc:ba98:7654:3210:fedc:ba98:7654:3210\",\n    )\n\n    assertThat(\n      \"1080:0:0:0:8:800:200C:417A\".toCanonicalHost(),\n    ).isEqualTo(\"1080::8:800:200c:417a\")\n\n    assertThat(\"1080::8:800:200C:417A\".toCanonicalHost()).isEqualTo(\"1080::8:800:200c:417a\")\n    assertThat(\"FF01::101\".toCanonicalHost()).isEqualTo(\"ff01::101\")\n    assertThat(\n      \"0:0:0:0:0:FFFF:129.144.52.38\".toCanonicalHost(),\n    ).isEqualTo(\"129.144.52.38\")\n\n    assertThat(\"::FFFF:129.144.52.38\".toCanonicalHost()).isEqualTo(\"129.144.52.38\")\n\n    // Hostnames\n    assertThat(\"WwW.GoOgLe.cOm\".toCanonicalHost()).isEqualTo(\"www.google.com\")\n    assertThat(\"localhost\".toCanonicalHost()).isEqualTo(\"localhost\")\n    assertThat(\"☃.net\".toCanonicalHost()).isEqualTo(\"xn--n3h.net\")\n\n    // IPv4 Compatible or Mapped addresses\n    assertThat(\"::192.168.0.1\".toCanonicalHost()).isEqualTo(\"::c0a8:1\")\n    assertThat(\"::FFFF:192.168.0.1\".toCanonicalHost()).isEqualTo(\"192.168.0.1\")\n    assertThat(\"0:0:0:0:0:0:13.1.68.3\".toCanonicalHost()).isEqualTo(\"::d01:4403\")\n    assertThat(\"::13.1.68.3\".toCanonicalHost()).isEqualTo(\"::d01:4403\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/RecordingAuthenticator.kt",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport java.net.Authenticator\nimport java.net.PasswordAuthentication\n\nclass RecordingAuthenticator(\n  private val authentication: PasswordAuthentication? =\n    PasswordAuthentication(\n      \"username\",\n      \"password\".toCharArray(),\n    ),\n) : Authenticator() {\n  val calls = mutableListOf<String>()\n\n  override fun getPasswordAuthentication(): PasswordAuthentication? {\n    calls.add(\n      \"host=$requestingHost port=$requestingPort site=${requestingSite.hostName} \" +\n        \"url=$requestingURL type=$requestorType prompt=$requestingPrompt \" +\n        \"protocol=$requestingProtocol scheme=$requestingScheme\",\n    )\n    return authentication\n  }\n\n  companion object {\n    /** base64(\"username:password\")  */\n    const val BASE_64_CREDENTIALS = \"dXNlcm5hbWU6cGFzc3dvcmQ=\"\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/TagsTest.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport java.util.concurrent.atomic.AtomicReference\nimport org.junit.jupiter.api.Test\n\nclass TagsTest {\n  @Test\n  fun emptyTags() {\n    val tags = EmptyTags\n    assertThat(tags[String::class]).isNull()\n  }\n\n  @Test\n  fun singleElement() {\n    val tags = EmptyTags.plus(String::class, \"hello\")\n    assertThat(tags[String::class]).isEqualTo(\"hello\")\n  }\n\n  @Test\n  fun multipleElements() {\n    val tags =\n      EmptyTags\n        .plus(String::class, \"hello\")\n        .plus(Integer::class, 5 as Integer)\n    assertThat(tags[String::class]).isEqualTo(\"hello\")\n    assertThat(tags[Integer::class]).isEqualTo(5)\n  }\n\n  /** The implementation retains no nodes from the original linked list. */\n  @Test\n  fun replaceFirstElement() {\n    val tags =\n      EmptyTags\n        .plus(String::class, \"a\")\n        .plus(Integer::class, 5 as Integer)\n        .plus(Boolean::class, true)\n        .plus(String::class, \"b\")\n    assertThat(tags[String::class]).isEqualTo(\"b\")\n    assertThat(tags.toString())\n      .isEqualTo(\"{class kotlin.Int=5, class kotlin.Boolean=true, class kotlin.String=b}\")\n  }\n\n  /** The implementation retains only the first node from the original linked list. */\n  @Test\n  fun replaceMiddleElement() {\n    val tags =\n      EmptyTags\n        .plus(Integer::class, 5 as Integer)\n        .plus(String::class, \"a\")\n        .plus(Boolean::class, true)\n        .plus(String::class, \"b\")\n    assertThat(tags[String::class]).isEqualTo(\"b\")\n    assertThat(tags.toString())\n      .isEqualTo(\"{class kotlin.Int=5, class kotlin.Boolean=true, class kotlin.String=b}\")\n  }\n\n  /** The implementation retains all but the first node from the original linked list. */\n  @Test\n  fun replaceLastElement() {\n    val tags =\n      EmptyTags\n        .plus(Integer::class, 5 as Integer)\n        .plus(Boolean::class, true)\n        .plus(String::class, \"a\")\n        .plus(String::class, \"b\")\n    assertThat(tags[String::class]).isEqualTo(\"b\")\n    assertThat(tags.toString())\n      .isEqualTo(\"{class kotlin.Int=5, class kotlin.Boolean=true, class kotlin.String=b}\")\n  }\n\n  /** The implementation retains no nodes from the original linked list. */\n  @Test\n  fun removeFirstElement() {\n    val tags =\n      EmptyTags\n        .plus(String::class, \"a\")\n        .plus(Integer::class, 5 as Integer)\n        .plus(Boolean::class, true)\n        .plus(String::class, null)\n    assertThat(tags[String::class]).isNull()\n    assertThat(tags.toString())\n      .isEqualTo(\"{class kotlin.Int=5, class kotlin.Boolean=true}\")\n  }\n\n  /** The implementation retains only the first node from the original linked list. */\n  @Test\n  fun removeMiddleElement() {\n    val tags =\n      EmptyTags\n        .plus(Integer::class, 5 as Integer)\n        .plus(String::class, \"a\")\n        .plus(Boolean::class, true)\n        .plus(String::class, null)\n    assertThat(tags[String::class]).isNull()\n    assertThat(tags.toString())\n      .isEqualTo(\"{class kotlin.Int=5, class kotlin.Boolean=true}\")\n  }\n\n  /** The implementation retains all but the first node from the original linked list. */\n  @Test\n  fun removeLastElement() {\n    val tags =\n      EmptyTags\n        .plus(Integer::class, 5 as Integer)\n        .plus(Boolean::class, true)\n        .plus(String::class, \"a\")\n        .plus(String::class, null)\n    assertThat(tags[String::class]).isNull()\n    assertThat(tags.toString())\n      .isEqualTo(\"{class kotlin.Int=5, class kotlin.Boolean=true}\")\n  }\n\n  @Test\n  fun removeUntilEmpty() {\n    val tags =\n      EmptyTags\n        .plus(Integer::class, 5 as Integer)\n        .plus(Boolean::class, true)\n        .plus(String::class, \"a\")\n        .plus(String::class, null)\n        .plus(Integer::class, null)\n        .plus(Boolean::class, null)\n    assertThat(tags).isEqualTo(EmptyTags)\n    assertThat(tags.toString()).isEqualTo(\"{}\")\n  }\n\n  @Test\n  fun removeAbsentFromEmpty() {\n    val tags = EmptyTags.plus(String::class, null)\n    assertThat(tags).isEqualTo(EmptyTags)\n    assertThat(tags.toString()).isEqualTo(\"{}\")\n  }\n\n  @Test\n  fun removeAbsentFromNonEmpty() {\n    val tags =\n      EmptyTags\n        .plus(String::class, \"a\")\n        .plus(Integer::class, null)\n    assertThat(tags[String::class]).isEqualTo(\"a\")\n    assertThat(tags.toString()).isEqualTo(\"{class kotlin.String=a}\")\n  }\n\n  @Test\n  fun computeIfAbsentWhenEmpty() {\n    val tags = EmptyTags\n    val atomicTags = AtomicReference<Tags>(tags)\n    assertThat(atomicTags.computeIfAbsent(String::class) { \"a\" }).isEqualTo(\"a\")\n    assertThat(atomicTags.get()[String::class]).isEqualTo(\"a\")\n  }\n\n  @Test\n  fun computeIfAbsentWhenPresent() {\n    val tags = EmptyTags.plus(String::class, \"a\")\n    val atomicTags = AtomicReference(tags)\n    assertThat(atomicTags.computeIfAbsent(String::class) { \"b\" }).isEqualTo(\"a\")\n    assertThat(atomicTags.get()[String::class]).isEqualTo(\"a\")\n  }\n\n  @Test\n  fun computeIfAbsentWhenDifferentKeyRaceLostDuringCompute() {\n    val tags = EmptyTags\n    val atomicTags = AtomicReference<Tags>(tags)\n    val result =\n      atomicTags.computeIfAbsent(String::class) {\n        // 'Race' by making another computeIfAbsent call. In practice this would be another thread.\n        assertThat(atomicTags.computeIfAbsent(Integer::class) { 5 as Integer }).isEqualTo(5)\n        \"a\"\n      }\n    assertThat(result).isEqualTo(\"a\")\n    assertThat(atomicTags.get()[String::class]).isEqualTo(\"a\")\n    assertThat(atomicTags.get()[Integer::class]).isEqualTo(5)\n  }\n\n  @Test\n  fun computeIfAbsentWhenSameKeyRaceLostDuringCompute() {\n    val tags = EmptyTags\n    val atomicTags = AtomicReference<Tags>(tags)\n    val result =\n      atomicTags.computeIfAbsent(String::class) {\n        // 'Race' by making another computeIfAbsent call. In practice this would be another thread.\n        assertThat(atomicTags.computeIfAbsent(String::class) { \"b\" }).isEqualTo(\"b\")\n        \"a\"\n      }\n    assertThat(result).isEqualTo(\"b\")\n    assertThat(atomicTags.get()[String::class]).isEqualTo(\"b\")\n  }\n\n  @Test\n  fun computeIfAbsentOnlyComputesOnceAfterRaceLost() {\n    var computeCount = 0\n    val tags = EmptyTags\n    val atomicTags = AtomicReference<Tags>(tags)\n    val result =\n      atomicTags.computeIfAbsent(String::class) {\n        computeCount++\n        // 'Race' by making another computeIfAbsent call. In practice this would be another thread.\n        assertThat(atomicTags.computeIfAbsent(Integer::class) { 5 as Integer }).isEqualTo(5)\n        \"a\"\n      }\n    assertThat(result).isEqualTo(\"a\")\n    assertThat(computeCount).isEqualTo(1)\n    assertThat(atomicTags.get()[Integer::class]).isEqualTo(5)\n    assertThat(atomicTags.get()[String::class]).isEqualTo(\"a\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/UtilTest.kt",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport assertk.assertThat\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isTrue\nimport java.net.InetAddress\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.util.concurrent.TimeUnit\nimport kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.nanoseconds\nimport okio.buffer\nimport okio.source\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\n\nclass UtilTest {\n  @Test\n  fun socketIsHealthy() {\n    val localhost = InetAddress.getLoopbackAddress()\n    val serverSocket = ServerSocket(0, 1, localhost)\n\n    val socket = Socket()\n    socket.connect(serverSocket.localSocketAddress)\n    val socketSource = socket.source().buffer()\n\n    assertThat(socket.isHealthy(socketSource)).isTrue()\n\n    serverSocket.close()\n    assertThat(socket.isHealthy(socketSource)).isFalse()\n  }\n\n  @Test\n  fun testDurationTimeUnit() {\n    assertThat(checkDuration(\"timeout\", 0, TimeUnit.MILLISECONDS)).isEqualTo(0)\n    assertThat(checkDuration(\"timeout\", 1, TimeUnit.MILLISECONDS)).isEqualTo(1)\n\n    assertThat(\n      assertThrows<IllegalStateException> {\n        checkDuration(\"timeout\", -1, TimeUnit.MILLISECONDS)\n      },\n    ).hasMessage(\"timeout < 0\")\n    assertThat(\n      assertThrows<IllegalArgumentException> {\n        checkDuration(\"timeout\", 1, TimeUnit.NANOSECONDS)\n      },\n    ).hasMessage(\"timeout too small\")\n    assertThat(\n      assertThrows<IllegalArgumentException> {\n        checkDuration(\n          \"timeout\",\n          1L + Int.MAX_VALUE.toLong(),\n          TimeUnit.MILLISECONDS,\n        )\n      },\n    ).hasMessage(\"timeout too large\")\n  }\n\n  @Test\n  fun testDurationDuration() {\n    assertThat(checkDuration(\"timeout\", 0.milliseconds)).isEqualTo(0)\n    assertThat(checkDuration(\"timeout\", 1.milliseconds)).isEqualTo(1)\n\n    assertThat(\n      assertThrows<IllegalStateException> {\n        checkDuration(\"timeout\", (-1).milliseconds)\n      },\n    ).hasMessage(\"timeout < 0\")\n    assertThat(\n      assertThrows<IllegalArgumentException> {\n        checkDuration(\"timeout\", 1.nanoseconds)\n      },\n    ).hasMessage(\"timeout too small\")\n    assertThat(\n      assertThrows<IllegalArgumentException> {\n        checkDuration(\n          \"timeout\",\n          (1L + Int.MAX_VALUE).milliseconds,\n        )\n      },\n    ).hasMessage(\"timeout too large\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/authenticator/JavaNetAuthenticatorTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.authenticator\n\nimport java.net.Authenticator\nimport java.net.InetAddress\nimport junit.framework.TestCase.assertNull\nimport okhttp3.FakeDns\nimport okhttp3.Protocol.HTTP_2\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.TestValueFactory\nimport okhttp3.internal.RecordingAuthenticator\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\n// Most tests from URLConnectionTest\nclass JavaNetAuthenticatorTest {\n  private var authenticator = JavaNetAuthenticator()\n  private val fakeDns = FakeDns()\n  private val recordingAuthenticator = RecordingAuthenticator()\n  private val factory =\n    TestValueFactory()\n      .apply {\n        dns = fakeDns\n      }\n\n  @BeforeEach\n  fun setup() {\n    Authenticator.setDefault(recordingAuthenticator)\n  }\n\n  @AfterEach\n  fun tearDown() {\n    Authenticator.setDefault(null)\n    factory.close()\n  }\n\n  @Test\n  fun testBasicAuth() {\n    fakeDns[\"server\"] = listOf(InetAddress.getLocalHost())\n\n    val route = factory.newRoute()\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://server/robots.txt\")\n        .build()\n    val response =\n      Response\n        .Builder()\n        .request(request)\n        .code(401)\n        .header(\"WWW-Authenticate\", \"Basic realm=\\\"User Visible Realm\\\"\")\n        .protocol(HTTP_2)\n        .message(\"Unauthorized\")\n        .build()\n    val authRequest = authenticator.authenticate(route, response)\n\n    assertEquals(\n      \"Basic ${RecordingAuthenticator.BASE_64_CREDENTIALS}\",\n      authRequest!!.header(\"Authorization\"),\n    )\n  }\n\n  @Test\n  fun noSupportForNonBasicAuth() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://server/robots.txt\")\n        .build()\n\n    val response =\n      Response\n        .Builder()\n        .request(request)\n        .code(401)\n        .header(\"WWW-Authenticate\", \"UnsupportedScheme realm=\\\"User Visible Realm\\\"\")\n        .protocol(HTTP_2)\n        .message(\"Unauthorized\")\n        .build()\n\n    val authRequest = authenticator.authenticate(null, response)\n    assertNull(authRequest)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/cache/DiskLruCacheTest.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.cache\n\nimport app.cash.burst.Burst\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNull\nimport assertk.assertions.isSameInstanceAs\nimport assertk.assertions.isTrue\nimport assertk.fail\nimport java.io.File\nimport java.io.FileNotFoundException\nimport java.io.IOException\nimport java.util.ArrayDeque\nimport kotlin.test.assertFailsWith\nimport okhttp3.TestUtil\nimport okhttp3.internal.cache.DiskLruCache.Editor\nimport okhttp3.internal.cache.DiskLruCache.Snapshot\nimport okhttp3.internal.concurrent.TaskFaker\nimport okhttp3.internal.io.FaultyFileSystem\nimport okio.FileSystem\nimport okio.Path\nimport okio.Path.Companion.toPath\nimport okio.Source\nimport okio.buffer\nimport okio.fakefilesystem.FakeFileSystem\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assumptions\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.io.TempDir\n\n@Timeout(60)\n@Tag(\"Slow\")\n@Burst\nclass DiskLruCacheTest(\n  subject: Subject = Subject.System,\n) {\n  private val baseFilesystem: FileSystem = subject.create()\n  private val filesystem = FaultyFileSystem(baseFilesystem)\n  private val windows = subject.windows\n\n  @TempDir\n  lateinit var cacheDirFile: File\n  private val cacheDir: Path\n    get() =\n      when (baseFilesystem) {\n        is FakeFileSystem -> \"/cache\".toPath()\n        else -> cacheDirFile.path.toPath()\n      }\n  private val appVersion = 100\n  private lateinit var journalFile: Path\n  private lateinit var journalBkpFile: Path\n  private val taskFaker = TaskFaker()\n  private val taskRunner = taskFaker.taskRunner\n  private lateinit var cache: DiskLruCache\n  private val toClose = ArrayDeque<DiskLruCache>()\n\n  private fun createNewCache() {\n    createNewCacheWithSize(Int.MAX_VALUE)\n  }\n\n  private fun createNewCacheWithSize(maxSize: Int) {\n    cache =\n      DiskLruCache(filesystem, cacheDir, appVersion, 2, maxSize.toLong(), taskRunner).also {\n        toClose.add(it)\n      }\n    synchronized(cache) { cache.initialize() }\n  }\n\n  @BeforeEach\n  fun setUp() {\n    if (filesystem.exists(cacheDir)) {\n      filesystem.deleteRecursively(cacheDir)\n    }\n    journalFile = cacheDir / DiskLruCache.JOURNAL_FILE\n    journalBkpFile = cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP\n    createNewCache()\n  }\n\n  @AfterEach fun tearDown() {\n    while (!toClose.isEmpty()) {\n      toClose.pop().close()\n    }\n    taskFaker.close()\n\n    (filesystem.delegate as? FakeFileSystem)?.checkNoOpenFiles()\n  }\n\n  @Test\n  fun emptyCache() {\n    cache.close()\n    assertJournalEquals()\n  }\n\n  @Test\n  fun recoverFromInitializationFailure() {\n    // Add an uncommitted entry. This will get detected on initialization, and the cache will\n    // attempt to delete the file. Do not explicitly close the cache here so the entry is left as\n    // incomplete.\n    val creator = cache.edit(\"k1\")!!\n    creator.newSink(0).buffer().use {\n      it.writeUtf8(\"Hello\")\n    }\n\n    // Simulate a severe Filesystem failure on the first initialization.\n    filesystem.setFaultyDelete(cacheDir / \"k1.0.tmp\", true)\n    filesystem.setFaultyDelete(cacheDir, true)\n    cache =\n      DiskLruCache(filesystem, cacheDir, appVersion, 2, Int.MAX_VALUE.toLong(), taskRunner).also {\n        toClose.add(it)\n      }\n    assertFailsWith<IOException> {\n      cache[\"k1\"]\n    }\n\n    // Now let it operate normally.\n    filesystem.setFaultyDelete(cacheDir / \"k1.0.tmp\", false)\n    filesystem.setFaultyDelete(cacheDir, false)\n    val snapshot = cache[\"k1\"]\n    assertThat(snapshot).isNull()\n  }\n\n  @Test\n  fun validateKey() {\n    var key = \"\"\n    assertFailsWith<IllegalArgumentException> {\n      key = \"has_space \"\n      cache.edit(key)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"keys must match regex [a-z0-9_-]{1,120}: \\\"$key\\\"\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      key = \"has_CR\\r\"\n      cache.edit(key)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"keys must match regex [a-z0-9_-]{1,120}: \\\"$key\\\"\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      key = \"has_LF\\n\"\n      cache.edit(key)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"keys must match regex [a-z0-9_-]{1,120}: \\\"$key\\\"\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      key = \"has_invalid/\"\n      cache.edit(key)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"keys must match regex [a-z0-9_-]{1,120}: \\\"$key\\\"\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      key = \"has_invalid\\u2603\"\n      cache.edit(key)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"keys must match regex [a-z0-9_-]{1,120}: \\\"$key\\\"\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      key = (\n        \"this_is_way_too_long_this_is_way_too_long_this_is_way_too_long_\" +\n          \"this_is_way_too_long_this_is_way_too_long_this_is_way_too_long\"\n      )\n      cache.edit(key)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"keys must match regex [a-z0-9_-]{1,120}: \\\"$key\\\"\")\n    }\n\n    // Test valid cases.\n\n    // Exactly 120.\n    key = (\n      \"0123456789012345678901234567890123456789012345678901234567890123456789\" +\n        \"01234567890123456789012345678901234567890123456789\"\n    )\n    cache.edit(key)!!.abort()\n    // Contains all valid characters.\n    key = \"abcdefghijklmnopqrstuvwxyz_0123456789\"\n    cache.edit(key)!!.abort()\n    // Contains dash.\n    key = \"-20384573948576\"\n    cache.edit(key)!!.abort()\n  }\n\n  @Test\n  fun writeAndReadEntry() {\n    val creator = cache.edit(\"k1\")!!\n    creator.setString(0, \"ABC\")\n    creator.setString(1, \"DE\")\n    assertThat(creator.newSource(0)).isNull()\n    assertThat(creator.newSource(1)).isNull()\n    creator.commit()\n    val snapshot = cache[\"k1\"]!!\n    snapshot.assertValue(0, \"ABC\")\n    snapshot.assertValue(1, \"DE\")\n  }\n\n  @Test\n  fun readAndWriteEntryAcrossCacheOpenAndClose() {\n    val creator = cache.edit(\"k1\")!!\n    creator.setString(0, \"A\")\n    creator.setString(1, \"B\")\n    creator.commit()\n    cache.close()\n    createNewCache()\n    val snapshot = cache[\"k1\"]!!\n    snapshot.assertValue(0, \"A\")\n    snapshot.assertValue(1, \"B\")\n    snapshot.close()\n  }\n\n  @Test\n  fun readAndWriteEntryWithoutProperClose() {\n    val creator = cache.edit(\"k1\")!!\n    creator.setString(0, \"A\")\n    creator.setString(1, \"B\")\n    creator.commit()\n\n    // Simulate a dirty close of 'cache' by opening the cache directory again.\n    createNewCache()\n    cache[\"k1\"]!!.use {\n      it.assertValue(0, \"A\")\n      it.assertValue(1, \"B\")\n    }\n  }\n\n  @Test\n  fun journalWithEditAndPublish() {\n    val creator = cache.edit(\"k1\")!!\n    assertJournalEquals(\"DIRTY k1\") // DIRTY must always be flushed.\n    creator.setString(0, \"AB\")\n    creator.setString(1, \"C\")\n    creator.commit()\n    cache.close()\n    assertJournalEquals(\"DIRTY k1\", \"CLEAN k1 2 1\")\n  }\n\n  @Test\n  fun revertedNewFileIsRemoveInJournal() {\n    val creator = cache.edit(\"k1\")!!\n    assertJournalEquals(\"DIRTY k1\") // DIRTY must always be flushed.\n    creator.setString(0, \"AB\")\n    creator.setString(1, \"C\")\n    creator.abort()\n    cache.close()\n    assertJournalEquals(\"DIRTY k1\", \"REMOVE k1\")\n  }\n\n  /** On Windows we have to wait until the edit is committed before we can delete its files. */\n  @Test\n  fun `unterminated edit is reverted on cache close`() {\n    val editor = cache.edit(\"k1\")!!\n    editor.setString(0, \"AB\")\n    editor.setString(1, \"C\")\n    cache.close()\n    val expected = if (windows) arrayOf(\"DIRTY k1\") else arrayOf(\"DIRTY k1\", \"REMOVE k1\")\n    assertJournalEquals(*expected)\n    editor.commit()\n    assertJournalEquals(*expected) // 'REMOVE k1' not written because journal is closed.\n  }\n\n  @Test\n  fun journalDoesNotIncludeReadOfYetUnpublishedValue() {\n    val creator = cache.edit(\"k1\")!!\n    assertThat(cache[\"k1\"]).isNull()\n    creator.setString(0, \"A\")\n    creator.setString(1, \"BC\")\n    creator.commit()\n    cache.close()\n    assertJournalEquals(\"DIRTY k1\", \"CLEAN k1 1 2\")\n  }\n\n  @Test\n  fun journalWithEditAndPublishAndRead() {\n    val k1Creator = cache.edit(\"k1\")!!\n    k1Creator.setString(0, \"AB\")\n    k1Creator.setString(1, \"C\")\n    k1Creator.commit()\n    val k2Creator = cache.edit(\"k2\")!!\n    k2Creator.setString(0, \"DEF\")\n    k2Creator.setString(1, \"G\")\n    k2Creator.commit()\n    val k1Snapshot = cache[\"k1\"]!!\n    k1Snapshot.close()\n    cache.close()\n    assertJournalEquals(\"DIRTY k1\", \"CLEAN k1 2 1\", \"DIRTY k2\", \"CLEAN k2 3 1\", \"READ k1\")\n  }\n\n  @Test\n  fun cannotOperateOnEditAfterPublish() {\n    val editor = cache.edit(\"k1\")!!\n    editor.setString(0, \"A\")\n    editor.setString(1, \"B\")\n    editor.commit()\n    editor.assertInoperable()\n  }\n\n  @Test\n  fun cannotOperateOnEditAfterRevert() {\n    val editor = cache.edit(\"k1\")!!\n    editor.setString(0, \"A\")\n    editor.setString(1, \"B\")\n    editor.abort()\n    editor.assertInoperable()\n  }\n\n  @Test\n  fun explicitRemoveAppliedToDiskImmediately() {\n    val editor = cache.edit(\"k1\")!!\n    editor.setString(0, \"ABC\")\n    editor.setString(1, \"B\")\n    editor.commit()\n    val k1 = getCleanFile(\"k1\", 0)\n    assertThat(readFile(k1)).isEqualTo(\"ABC\")\n    cache.remove(\"k1\")\n    assertThat(filesystem.exists(k1)).isFalse()\n  }\n\n  @Test\n  fun removePreventsActiveEditFromStoringAValue() {\n    set(\"a\", \"a\", \"a\")\n    val a = cache.edit(\"a\")!!\n    a.setString(0, \"a1\")\n    assertThat(cache.remove(\"a\")).isTrue()\n    a.setString(1, \"a2\")\n    a.commit()\n    assertAbsent(\"a\")\n  }\n\n  /**\n   * Each read sees a snapshot of the file at the time read was called. This means that two reads of\n   * the same key can see different data.\n   */\n  @Test\n  fun readAndWriteOverlapsMaintainConsistency() {\n    Assumptions.assumeFalse(windows) // Can't edit while a read is in progress.\n\n    val v1Creator = cache.edit(\"k1\")!!\n    v1Creator.setString(0, \"AAaa\")\n    v1Creator.setString(1, \"BBbb\")\n    v1Creator.commit()\n\n    cache[\"k1\"]!!.use { snapshot1 ->\n      val inV1 = snapshot1.getSource(0).buffer()\n      assertThat(inV1.readByte()).isEqualTo('A'.code.toByte())\n      assertThat(inV1.readByte()).isEqualTo('A'.code.toByte())\n\n      val v1Updater = cache.edit(\"k1\")!!\n      v1Updater.setString(0, \"CCcc\")\n      v1Updater.setString(1, \"DDdd\")\n      v1Updater.commit()\n\n      cache[\"k1\"]!!.use { snapshot2 ->\n        snapshot2.assertValue(0, \"CCcc\")\n        snapshot2.assertValue(1, \"DDdd\")\n      }\n\n      assertThat(inV1.readByte()).isEqualTo('a'.code.toByte())\n      assertThat(inV1.readByte()).isEqualTo('a'.code.toByte())\n      snapshot1.assertValue(1, \"BBbb\")\n    }\n  }\n\n  @Test\n  fun openWithDirtyKeyDeletesAllFilesForThatKey() {\n    cache.close()\n    val cleanFile0 = getCleanFile(\"k1\", 0)\n    val cleanFile1 = getCleanFile(\"k1\", 1)\n    val dirtyFile0 = getDirtyFile(\"k1\", 0)\n    val dirtyFile1 = getDirtyFile(\"k1\", 1)\n    writeFile(cleanFile0, \"A\")\n    writeFile(cleanFile1, \"B\")\n    writeFile(dirtyFile0, \"C\")\n    writeFile(dirtyFile1, \"D\")\n    createJournal(\"CLEAN k1 1 1\", \"DIRTY k1\")\n    createNewCache()\n    assertThat(filesystem.exists(cleanFile0)).isFalse()\n    assertThat(filesystem.exists(cleanFile1)).isFalse()\n    assertThat(filesystem.exists(dirtyFile0)).isFalse()\n    assertThat(filesystem.exists(dirtyFile1)).isFalse()\n    assertThat(cache[\"k1\"]).isNull()\n  }\n\n  @Test\n  fun openWithInvalidVersionClearsDirectory() {\n    cache.close()\n    generateSomeGarbageFiles()\n    createJournalWithHeader(DiskLruCache.MAGIC, \"0\", \"100\", \"2\", \"\")\n    createNewCache()\n    assertGarbageFilesAllDeleted()\n  }\n\n  @Test\n  fun openWithInvalidAppVersionClearsDirectory() {\n    cache.close()\n    generateSomeGarbageFiles()\n    createJournalWithHeader(DiskLruCache.MAGIC, \"1\", \"101\", \"2\", \"\")\n    createNewCache()\n    assertGarbageFilesAllDeleted()\n  }\n\n  @Test\n  fun openWithInvalidValueCountClearsDirectory() {\n    cache.close()\n    generateSomeGarbageFiles()\n    createJournalWithHeader(DiskLruCache.MAGIC, \"1\", \"100\", \"1\", \"\")\n    createNewCache()\n    assertGarbageFilesAllDeleted()\n  }\n\n  @Test\n  fun openWithInvalidBlankLineClearsDirectory() {\n    cache.close()\n    generateSomeGarbageFiles()\n    createJournalWithHeader(DiskLruCache.MAGIC, \"1\", \"100\", \"2\", \"x\")\n    createNewCache()\n    assertGarbageFilesAllDeleted()\n  }\n\n  @Test\n  fun openWithInvalidJournalLineClearsDirectory() {\n    cache.close()\n    generateSomeGarbageFiles()\n    createJournal(\"CLEAN k1 1 1\", \"BOGUS\")\n    createNewCache()\n    assertGarbageFilesAllDeleted()\n    assertThat(cache[\"k1\"]).isNull()\n  }\n\n  @Test\n  fun openWithInvalidFileSizeClearsDirectory() {\n    cache.close()\n    generateSomeGarbageFiles()\n    createJournal(\"CLEAN k1 0000x001 1\")\n    createNewCache()\n    assertGarbageFilesAllDeleted()\n    assertThat(cache[\"k1\"]).isNull()\n  }\n\n  @Test\n  fun openWithTruncatedLineDiscardsThatLine() {\n    cache.close()\n    writeFile(getCleanFile(\"k1\", 0), \"A\")\n    writeFile(getCleanFile(\"k1\", 1), \"B\")\n    filesystem.write(journalFile) {\n      writeUtf8(\n        // No trailing newline.\n        \"\"\"\n        |${DiskLruCache.MAGIC}\n        |${DiskLruCache.VERSION_1}\n        |100\n        |2\n        |\n        |CLEAN k1 1 1\n        \"\"\".trimMargin(),\n      )\n    }\n    createNewCache()\n    assertThat(cache[\"k1\"]).isNull()\n\n    // The journal is not corrupt when editing after a truncated line.\n    set(\"k1\", \"C\", \"D\")\n    cache.close()\n    createNewCache()\n    assertValue(\"k1\", \"C\", \"D\")\n  }\n\n  @Test\n  fun openWithTooManyFileSizesClearsDirectory() {\n    cache.close()\n    generateSomeGarbageFiles()\n    createJournal(\"CLEAN k1 1 1 1\")\n    createNewCache()\n    assertGarbageFilesAllDeleted()\n    assertThat(cache[\"k1\"]).isNull()\n  }\n\n  @Test\n  fun keyWithSpaceNotPermitted() {\n    assertFailsWith<IllegalArgumentException> {\n      cache.edit(\"my key\")\n    }\n  }\n\n  @Test\n  fun keyWithNewlineNotPermitted() {\n    assertFailsWith<IllegalArgumentException> {\n      cache.edit(\"my\\nkey\")\n    }\n  }\n\n  @Test\n  fun keyWithCarriageReturnNotPermitted() {\n    assertFailsWith<IllegalArgumentException> {\n      cache.edit(\"my\\rkey\")\n    }\n  }\n\n  @Test\n  fun createNewEntryWithTooFewValuesFails() {\n    val creator = cache.edit(\"k1\")!!\n    creator.setString(1, \"A\")\n    assertFailsWith<IllegalStateException> {\n      creator.commit()\n    }\n    assertThat(filesystem.exists(getCleanFile(\"k1\", 0))).isFalse()\n    assertThat(filesystem.exists(getCleanFile(\"k1\", 1))).isFalse()\n    assertThat(filesystem.exists(getDirtyFile(\"k1\", 0))).isFalse()\n    assertThat(filesystem.exists(getDirtyFile(\"k1\", 1))).isFalse()\n    assertThat(cache[\"k1\"]).isNull()\n    val creator2 = cache.edit(\"k1\")!!\n    creator2.setString(0, \"B\")\n    creator2.setString(1, \"C\")\n    creator2.commit()\n  }\n\n  @Test\n  fun revertWithTooFewValues() {\n    val creator = cache.edit(\"k1\")!!\n    creator.setString(1, \"A\")\n    creator.abort()\n    assertThat(filesystem.exists(getCleanFile(\"k1\", 0))).isFalse()\n    assertThat(filesystem.exists(getCleanFile(\"k1\", 1))).isFalse()\n    assertThat(filesystem.exists(getDirtyFile(\"k1\", 0))).isFalse()\n    assertThat(filesystem.exists(getDirtyFile(\"k1\", 1))).isFalse()\n    assertThat(cache[\"k1\"]).isNull()\n  }\n\n  @Test\n  fun updateExistingEntryWithTooFewValuesReusesPreviousValues() {\n    val creator = cache.edit(\"k1\")!!\n    creator.setString(0, \"A\")\n    creator.setString(1, \"B\")\n    creator.commit()\n    val updater = cache.edit(\"k1\")!!\n    updater.setString(0, \"C\")\n    updater.commit()\n    val snapshot = cache[\"k1\"]!!\n    snapshot.assertValue(0, \"C\")\n    snapshot.assertValue(1, \"B\")\n    snapshot.close()\n  }\n\n  @Test\n  fun growMaxSize() {\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"a\", \"a\", \"aaa\") // size 4\n    set(\"b\", \"bb\", \"bbbb\") // size 6\n    cache.maxSize = 20\n    set(\"c\", \"c\", \"c\") // size 12\n    assertThat(cache.size()).isEqualTo(12)\n  }\n\n  @Test\n  fun shrinkMaxSizeEvicts() {\n    cache.close()\n    createNewCacheWithSize(20)\n    set(\"a\", \"a\", \"aaa\") // size 4\n    set(\"b\", \"bb\", \"bbbb\") // size 6\n    set(\"c\", \"c\", \"c\") // size 12\n    cache.maxSize = 10\n    assertThat(taskFaker.isIdle()).isFalse()\n  }\n\n  @Test\n  fun evictOnInsert() {\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"a\", \"a\", \"aaa\") // size 4\n    set(\"b\", \"bb\", \"bbbb\") // size 6\n    assertThat(cache.size()).isEqualTo(10)\n\n    // Cause the size to grow to 12 should evict 'A'.\n    set(\"c\", \"c\", \"c\")\n    cache.flush()\n    assertThat(cache.size()).isEqualTo(8)\n    assertAbsent(\"a\")\n    assertValue(\"b\", \"bb\", \"bbbb\")\n    assertValue(\"c\", \"c\", \"c\")\n\n    // Causing the size to grow to 10 should evict nothing.\n    set(\"d\", \"d\", \"d\")\n    cache.flush()\n    assertThat(cache.size()).isEqualTo(10)\n    assertAbsent(\"a\")\n    assertValue(\"b\", \"bb\", \"bbbb\")\n    assertValue(\"c\", \"c\", \"c\")\n    assertValue(\"d\", \"d\", \"d\")\n\n    // Causing the size to grow to 18 should evict 'B' and 'C'.\n    set(\"e\", \"eeee\", \"eeee\")\n    cache.flush()\n    assertThat(cache.size()).isEqualTo(10)\n    assertAbsent(\"a\")\n    assertAbsent(\"b\")\n    assertAbsent(\"c\")\n    assertValue(\"d\", \"d\", \"d\")\n    assertValue(\"e\", \"eeee\", \"eeee\")\n  }\n\n  @Test\n  fun evictOnUpdate() {\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"a\", \"a\", \"aa\") // size 3\n    set(\"b\", \"b\", \"bb\") // size 3\n    set(\"c\", \"c\", \"cc\") // size 3\n    assertThat(cache.size()).isEqualTo(9)\n\n    // Causing the size to grow to 11 should evict 'A'.\n    set(\"b\", \"b\", \"bbbb\")\n    cache.flush()\n    assertThat(cache.size()).isEqualTo(8)\n    assertAbsent(\"a\")\n    assertValue(\"b\", \"b\", \"bbbb\")\n    assertValue(\"c\", \"c\", \"cc\")\n  }\n\n  @Test\n  fun evictionHonorsLruFromCurrentSession() {\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n    set(\"c\", \"c\", \"c\")\n    set(\"d\", \"d\", \"d\")\n    set(\"e\", \"e\", \"e\")\n    cache[\"b\"]!!.close() // 'B' is now least recently used.\n\n    // Causing the size to grow to 12 should evict 'A'.\n    set(\"f\", \"f\", \"f\")\n    // Causing the size to grow to 12 should evict 'C'.\n    set(\"g\", \"g\", \"g\")\n    cache.flush()\n    assertThat(cache.size()).isEqualTo(10)\n    assertAbsent(\"a\")\n    assertValue(\"b\", \"b\", \"b\")\n    assertAbsent(\"c\")\n    assertValue(\"d\", \"d\", \"d\")\n    assertValue(\"e\", \"e\", \"e\")\n    assertValue(\"f\", \"f\", \"f\")\n  }\n\n  @Test\n  fun evictionHonorsLruFromPreviousSession() {\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n    set(\"c\", \"c\", \"c\")\n    set(\"d\", \"d\", \"d\")\n    set(\"e\", \"e\", \"e\")\n    set(\"f\", \"f\", \"f\")\n    cache[\"b\"]!!.close() // 'B' is now least recently used.\n    assertThat(cache.size()).isEqualTo(12)\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"g\", \"g\", \"g\")\n    cache.flush()\n    assertThat(cache.size()).isEqualTo(10)\n    assertAbsent(\"a\")\n    assertValue(\"b\", \"b\", \"b\")\n    assertAbsent(\"c\")\n    assertValue(\"d\", \"d\", \"d\")\n    assertValue(\"e\", \"e\", \"e\")\n    assertValue(\"f\", \"f\", \"f\")\n    assertValue(\"g\", \"g\", \"g\")\n  }\n\n  @Test\n  fun cacheSingleEntryOfSizeGreaterThanMaxSize() {\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"a\", \"aaaaa\", \"aaaaaa\") // size=11\n    cache.flush()\n    assertAbsent(\"a\")\n  }\n\n  @Test\n  fun cacheSingleValueOfSizeGreaterThanMaxSize() {\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"a\", \"aaaaaaaaaaa\", \"a\") // size=12\n    cache.flush()\n    assertAbsent(\"a\")\n  }\n\n  @Test\n  fun constructorDoesNotAllowZeroCacheSize() {\n    assertFailsWith<IllegalArgumentException> {\n      DiskLruCache(filesystem, cacheDir, appVersion, 2, 0, taskRunner)\n    }\n  }\n\n  @Test\n  fun constructorDoesNotAllowZeroValuesPerEntry() {\n    assertFailsWith<IllegalArgumentException> {\n      DiskLruCache(filesystem, cacheDir, appVersion, 0, 10, taskRunner)\n    }\n  }\n\n  @Test\n  fun removeAbsentElement() {\n    cache.remove(\"a\")\n  }\n\n  @Test\n  fun readingTheSameStreamMultipleTimes() {\n    set(\"a\", \"a\", \"b\")\n    val snapshot = cache[\"a\"]!!\n    assertThat(snapshot.getSource(0)).isSameInstanceAs(snapshot.getSource(0))\n    snapshot.close()\n  }\n\n  @Test\n  fun rebuildJournalOnRepeatedReads() {\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n    while (taskFaker.isIdle()) {\n      assertValue(\"a\", \"a\", \"a\")\n      assertValue(\"b\", \"b\", \"b\")\n    }\n  }\n\n  @Test\n  fun rebuildJournalOnRepeatedEdits() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n    }\n    taskFaker.runNextTask()\n\n    // Check that a rebuilt journal behaves normally.\n    assertValue(\"a\", \"a\", \"a\")\n    assertValue(\"b\", \"b\", \"b\")\n  }\n\n  /** @see [Issue 28](https://github.com/JakeWharton/DiskLruCache/issues/28) */\n  @Test\n  fun rebuildJournalOnRepeatedReadsWithOpenAndClose() {\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n    while (taskFaker.isIdle()) {\n      assertValue(\"a\", \"a\", \"a\")\n      assertValue(\"b\", \"b\", \"b\")\n      cache.close()\n      createNewCache()\n    }\n  }\n\n  /** @see [Issue 28](https://github.com/JakeWharton/DiskLruCache/issues/28) */\n  @Test\n  fun rebuildJournalOnRepeatedEditsWithOpenAndClose() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n      cache.close()\n      createNewCache()\n    }\n  }\n\n  @Test\n  fun rebuildJournalFailurePreventsEditors() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n    }\n\n    // Cause the rebuild action to fail.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, true)\n    taskFaker.runNextTask()\n\n    // Don't allow edits under any circumstances.\n    assertThat(cache.edit(\"a\")).isNull()\n    assertThat(cache.edit(\"c\")).isNull()\n    cache[\"a\"]!!.use {\n      assertThat(it.edit()).isNull()\n    }\n  }\n\n  @Test\n  fun rebuildJournalFailureIsRetried() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n    }\n\n    // Cause the rebuild action to fail.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, true)\n    taskFaker.runNextTask()\n\n    // The rebuild is retried on cache hits and on cache edits.\n    val snapshot = cache[\"b\"]!!\n    snapshot.close()\n    assertThat(cache.edit(\"d\")).isNull()\n    assertThat(taskFaker.isIdle()).isFalse()\n\n    // On cache misses, no retry job is queued.\n    assertThat(cache[\"c\"]).isNull()\n    assertThat(taskFaker.isIdle()).isFalse()\n\n    // Let the rebuild complete successfully.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, false)\n    taskFaker.runNextTask()\n    assertJournalEquals(\"CLEAN a 1 1\", \"CLEAN b 1 1\")\n  }\n\n  @Test\n  fun rebuildJournalFailureWithInFlightEditors() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n    }\n    val commitEditor = cache.edit(\"c\")!!\n    val abortEditor = cache.edit(\"d\")!!\n    cache.edit(\"e\") // Grab an editor, but don't do anything with it.\n\n    // Cause the rebuild action to fail.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, true)\n    taskFaker.runNextTask()\n\n    // In-flight editors can commit and have their values retained.\n    commitEditor.setString(0, \"c\")\n    commitEditor.setString(1, \"c\")\n    commitEditor.commit()\n    assertValue(\"c\", \"c\", \"c\")\n    abortEditor.abort()\n\n    // Let the rebuild complete successfully.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, false)\n    taskFaker.runNextTask()\n    assertJournalEquals(\"CLEAN a 1 1\", \"CLEAN b 1 1\", \"DIRTY e\", \"CLEAN c 1 1\")\n  }\n\n  @Test\n  fun rebuildJournalFailureWithEditorsInFlightThenClose() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n    }\n    val commitEditor = cache.edit(\"c\")!!\n    val abortEditor = cache.edit(\"d\")!!\n    cache.edit(\"e\") // Grab an editor, but don't do anything with it.\n\n    // Cause the rebuild action to fail.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, true)\n    taskFaker.runNextTask()\n    commitEditor.setString(0, \"c\")\n    commitEditor.setString(1, \"c\")\n    commitEditor.commit()\n    assertValue(\"c\", \"c\", \"c\")\n    abortEditor.abort()\n    cache.close()\n    createNewCache()\n\n    // Although 'c' successfully committed above, the journal wasn't available to issue a CLEAN op.\n    // Because the last state of 'c' was DIRTY before the journal failed, it should be removed\n    // entirely on a subsequent open.\n    assertThat(cache.size()).isEqualTo(4)\n    assertAbsent(\"c\")\n    assertAbsent(\"d\")\n    assertAbsent(\"e\")\n  }\n\n  @Test\n  fun rebuildJournalFailureAllowsRemovals() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n    }\n\n    // Cause the rebuild action to fail.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, true)\n    taskFaker.runNextTask()\n    assertThat(cache.remove(\"a\")).isTrue()\n    assertAbsent(\"a\")\n\n    // Let the rebuild complete successfully.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, false)\n    taskFaker.runNextTask()\n    assertJournalEquals(\"CLEAN b 1 1\")\n  }\n\n  @Test\n  fun rebuildJournalFailureWithRemovalThenClose() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n    }\n\n    // Cause the rebuild action to fail.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, true)\n    taskFaker.runNextTask()\n    assertThat(cache.remove(\"a\")).isTrue()\n    assertAbsent(\"a\")\n    cache.close()\n    createNewCache()\n\n    // The journal will have no record that 'a' was removed. It will have an entry for 'a', but when\n    // it tries to read the cache files, it will find they were deleted. Once it encounters an entry\n    // with missing cache files, it should remove it from the cache entirely.\n    assertThat(cache.size()).isEqualTo(4)\n    assertThat(cache[\"a\"]).isNull()\n    assertThat(cache.size()).isEqualTo(2)\n  }\n\n  @Test\n  fun rebuildJournalFailureAllowsEvictAll() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"a\", \"a\")\n      set(\"b\", \"b\", \"b\")\n    }\n\n    // Cause the rebuild action to fail.\n    filesystem.setFaultyRename(cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP, true)\n    taskFaker.runNextTask()\n    cache.evictAll()\n    assertThat(cache.size()).isEqualTo(0)\n    assertAbsent(\"a\")\n    assertAbsent(\"b\")\n    cache.close()\n    createNewCache()\n\n    // The journal has no record that 'a' and 'b' were removed. It will have an entry for both, but\n    // when it tries to read the cache files for either entry, it will discover the cache files are\n    // missing and remove the entries from the cache.\n    assertThat(cache.size()).isEqualTo(4)\n    assertThat(cache[\"a\"]).isNull()\n    assertThat(cache[\"b\"]).isNull()\n    assertThat(cache.size()).isEqualTo(0)\n  }\n\n  @Test\n  fun rebuildJournalFailureWithCacheTrim() {\n    while (taskFaker.isIdle()) {\n      set(\"a\", \"aa\", \"aa\")\n      set(\"b\", \"bb\", \"bb\")\n    }\n\n    // Cause the rebuild action to fail.\n    filesystem.setFaultyRename(\n      cacheDir / DiskLruCache.JOURNAL_FILE_BACKUP,\n      true,\n    )\n    taskFaker.runNextTask()\n\n    // Trigger a job to trim the cache.\n    cache.maxSize = 4\n    taskFaker.runNextTask()\n    assertAbsent(\"a\")\n    assertValue(\"b\", \"bb\", \"bb\")\n  }\n\n  @Test\n  fun restoreBackupFile() {\n    val creator = cache.edit(\"k1\")!!\n    creator.setString(0, \"ABC\")\n    creator.setString(1, \"DE\")\n    creator.commit()\n    cache.close()\n    filesystem.atomicMove(journalFile, journalBkpFile)\n    assertThat(filesystem.exists(journalFile)).isFalse()\n    createNewCache()\n    val snapshot = cache[\"k1\"]!!\n    snapshot.assertValue(0, \"ABC\")\n    snapshot.assertValue(1, \"DE\")\n    assertThat(filesystem.exists(journalBkpFile)).isFalse()\n    assertThat(filesystem.exists(journalFile)).isTrue()\n  }\n\n  @Test\n  fun journalFileIsPreferredOverBackupFile() {\n    var creator = cache.edit(\"k1\")!!\n    creator.setString(0, \"ABC\")\n    creator.setString(1, \"DE\")\n    creator.commit()\n    cache.flush()\n    filesystem.copy(journalFile, journalBkpFile)\n    creator = cache.edit(\"k2\")!!\n    creator.setString(0, \"F\")\n    creator.setString(1, \"GH\")\n    creator.commit()\n    cache.close()\n    assertThat(filesystem.exists(journalFile)).isTrue()\n    assertThat(filesystem.exists(journalBkpFile)).isTrue()\n    createNewCache()\n    val snapshotA = cache[\"k1\"]!!\n    snapshotA.assertValue(0, \"ABC\")\n    snapshotA.assertValue(1, \"DE\")\n    val snapshotB = cache[\"k2\"]!!\n    snapshotB.assertValue(0, \"F\")\n    snapshotB.assertValue(1, \"GH\")\n    assertThat(filesystem.exists(journalBkpFile)).isFalse()\n    assertThat(filesystem.exists(journalFile)).isTrue()\n  }\n\n  @Test\n  fun openCreatesDirectoryIfNecessary() {\n    cache.close()\n    val dir = (cacheDir / \"testOpenCreatesDirectoryIfNecessary\").also { filesystem.createDirectories(it) }\n    cache =\n      DiskLruCache(filesystem, dir, appVersion, 2, Int.MAX_VALUE.toLong(), taskRunner).also {\n        toClose.add(it)\n      }\n    set(\"a\", \"a\", \"a\")\n    assertThat(filesystem.exists(dir / \"a.0\")).isTrue()\n    assertThat(filesystem.exists(dir / \"a.1\")).isTrue()\n    assertThat(filesystem.exists(dir / \"journal\")).isTrue()\n  }\n\n  @Test\n  fun fileDeletedExternally() {\n    set(\"a\", \"a\", \"a\")\n    filesystem.delete(getCleanFile(\"a\", 1))\n    assertThat(cache[\"a\"]).isNull()\n    assertThat(cache.size()).isEqualTo(0)\n  }\n\n  @Test\n  fun editSameVersion() {\n    set(\"a\", \"a\", \"a\")\n    val snapshot = cache[\"a\"]!!\n    snapshot.close()\n    val editor = snapshot.edit()!!\n    editor.setString(1, \"a2\")\n    editor.commit()\n    assertValue(\"a\", \"a\", \"a2\")\n  }\n\n  @Test\n  fun editSnapshotAfterChangeAborted() {\n    set(\"a\", \"a\", \"a\")\n    val snapshot = cache[\"a\"]!!\n    snapshot.close()\n    val toAbort = snapshot.edit()!!\n    toAbort.setString(0, \"b\")\n    toAbort.abort()\n    val editor = snapshot.edit()!!\n    editor.setString(1, \"a2\")\n    editor.commit()\n    assertValue(\"a\", \"a\", \"a2\")\n  }\n\n  @Test\n  fun editSnapshotAfterChangeCommitted() {\n    set(\"a\", \"a\", \"a\")\n    val snapshot = cache[\"a\"]!!\n    snapshot.close()\n    val toAbort = snapshot.edit()!!\n    toAbort.setString(0, \"b\")\n    toAbort.commit()\n    assertThat(snapshot.edit()).isNull()\n  }\n\n  @Test\n  fun editSinceEvicted() {\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"a\", \"aa\", \"aaa\") // size 5\n    cache[\"a\"]!!.use {\n      set(\"b\", \"bb\", \"bbb\") // size 5\n      set(\"c\", \"cc\", \"ccc\") // size 5; will evict 'A'\n      cache.flush()\n      assertThat(it.edit()).isNull()\n    }\n  }\n\n  @Test\n  fun editSinceEvictedAndRecreated() {\n    cache.close()\n    createNewCacheWithSize(10)\n    set(\"a\", \"aa\", \"aaa\") // size 5\n    val snapshot = cache[\"a\"]!!\n    snapshot.close()\n    set(\"b\", \"bb\", \"bbb\") // size 5\n    set(\"c\", \"cc\", \"ccc\") // size 5; will evict 'A'\n    set(\"a\", \"a\", \"aaaa\") // size 5; will evict 'B'\n    cache.flush()\n    assertThat(snapshot.edit()).isNull()\n  }\n\n  /** @see [Issue 2](https://github.com/JakeWharton/DiskLruCache/issues/2) */\n  @Test\n  fun aggressiveClearingHandlesWrite() {\n    Assumptions.assumeFalse(windows) // Can't deleteContents while the journal is open.\n\n    filesystem.deleteRecursively(cacheDir)\n    set(\"a\", \"a\", \"a\")\n    assertValue(\"a\", \"a\", \"a\")\n  }\n\n  /** @see [Issue 2](https://github.com/JakeWharton/DiskLruCache/issues/2) */\n  @Test\n  fun aggressiveClearingHandlesEdit() {\n    Assumptions.assumeFalse(windows) // Can't deleteContents while the journal is open.\n\n    set(\"a\", \"a\", \"a\")\n    val a = cache.edit(\"a\")!!\n    filesystem.deleteRecursively(cacheDir)\n    a.setString(1, \"a2\")\n    a.commit()\n  }\n\n  @Test\n  fun removeHandlesMissingFile() {\n    set(\"a\", \"a\", \"a\")\n    filesystem.delete(getCleanFile(\"a\", 0))\n    cache.remove(\"a\")\n  }\n\n  /** @see [Issue 2](https://github.com/JakeWharton/DiskLruCache/issues/2) */\n  @Test\n  fun aggressiveClearingHandlesPartialEdit() {\n    Assumptions.assumeFalse(windows) // Can't deleteContents while the journal is open.\n\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n    val a = cache.edit(\"a\")!!\n    a.setString(0, \"a1\")\n    filesystem.deleteRecursively(cacheDir)\n    a.setString(1, \"a2\")\n    a.commit()\n    assertThat(cache[\"a\"]).isNull()\n  }\n\n  /** @see [Issue 2](https://github.com/JakeWharton/DiskLruCache/issues/2) */\n  @Test\n  fun aggressiveClearingHandlesRead() {\n    Assumptions.assumeFalse(windows) // Can't deleteContents while the journal is open.\n\n    filesystem.deleteRecursively(cacheDir)\n    assertThat(cache[\"a\"]).isNull()\n  }\n\n  /**\n   * We had a long-lived bug where [DiskLruCache.trimToSize] could infinite loop if entries\n   * being edited required deletion for the operation to complete.\n   */\n  @Test\n  fun trimToSizeWithActiveEdit() {\n    val expectedByteCount = if (windows) 10L else 0L\n    val afterRemoveFileContents = if (windows) \"a1234\" else null\n\n    set(\"a\", \"a1234\", \"a1234\")\n    val a = cache.edit(\"a\")!!\n    a.setString(0, \"a123\")\n    cache.maxSize = 8 // Smaller than the sum of active edits!\n    cache.flush() // Force trimToSize().\n    assertThat(cache.size()).isEqualTo(expectedByteCount)\n    assertThat(readFileOrNull(getCleanFile(\"a\", 0))).isEqualTo(afterRemoveFileContents)\n    assertThat(readFileOrNull(getCleanFile(\"a\", 1))).isEqualTo(afterRemoveFileContents)\n\n    // After the edit is completed, its entry is still gone.\n    a.setString(1, \"a1\")\n    a.commit()\n    assertAbsent(\"a\")\n    assertThat(cache.size()).isEqualTo(0)\n  }\n\n  @Test\n  fun evictAll() {\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n    cache.evictAll()\n    assertThat(cache.size()).isEqualTo(0)\n    assertAbsent(\"a\")\n    assertAbsent(\"b\")\n  }\n\n  @Test\n  fun evictAllWithPartialCreate() {\n    val a = cache.edit(\"a\")!!\n    a.setString(0, \"a1\")\n    a.setString(1, \"a2\")\n    cache.evictAll()\n    assertThat(cache.size()).isEqualTo(0)\n    a.commit()\n    assertAbsent(\"a\")\n  }\n\n  @Test\n  fun evictAllWithPartialEditDoesNotStoreAValue() {\n    val expectedByteCount = if (windows) 2L else 0L\n\n    set(\"a\", \"a\", \"a\")\n    val a = cache.edit(\"a\")!!\n    a.setString(0, \"a1\")\n    a.setString(1, \"a2\")\n    cache.evictAll()\n    assertThat(cache.size()).isEqualTo(expectedByteCount)\n    a.commit()\n    assertAbsent(\"a\")\n  }\n\n  @Test\n  fun evictAllDoesntInterruptPartialRead() {\n    val expectedByteCount = if (windows) 2L else 0L\n    val afterRemoveFileContents = if (windows) \"a\" else null\n\n    set(\"a\", \"a\", \"a\")\n    cache[\"a\"]!!.use {\n      it.assertValue(0, \"a\")\n      cache.evictAll()\n      assertThat(cache.size()).isEqualTo(expectedByteCount)\n      assertThat(readFileOrNull(getCleanFile(\"a\", 0))).isEqualTo(afterRemoveFileContents)\n      assertThat(readFileOrNull(getCleanFile(\"a\", 1))).isEqualTo(afterRemoveFileContents)\n      it.assertValue(1, \"a\")\n    }\n    assertThat(cache.size()).isEqualTo(0L)\n  }\n\n  @Test\n  fun editSnapshotAfterEvictAllReturnsNullDueToStaleValue() {\n    val expectedByteCount = if (windows) 2L else 0L\n    val afterRemoveFileContents = if (windows) \"a\" else null\n\n    set(\"a\", \"a\", \"a\")\n    cache[\"a\"]!!.use {\n      cache.evictAll()\n      assertThat(cache.size()).isEqualTo(expectedByteCount)\n      assertThat(readFileOrNull(getCleanFile(\"a\", 0))).isEqualTo(afterRemoveFileContents)\n      assertThat(readFileOrNull(getCleanFile(\"a\", 1))).isEqualTo(afterRemoveFileContents)\n      assertThat(it.edit()).isNull()\n    }\n    assertThat(cache.size()).isEqualTo(0L)\n  }\n\n  @Test\n  fun iterator() {\n    set(\"a\", \"a1\", \"a2\")\n    set(\"b\", \"b1\", \"b2\")\n    set(\"c\", \"c1\", \"c2\")\n    val iterator = cache.snapshots()\n    assertThat(iterator.hasNext()).isTrue()\n    iterator.next().use {\n      assertThat(it.key()).isEqualTo(\"a\")\n      it.assertValue(0, \"a1\")\n      it.assertValue(1, \"a2\")\n    }\n    assertThat(iterator.hasNext()).isTrue()\n    iterator.next().use {\n      assertThat(it.key()).isEqualTo(\"b\")\n      it.assertValue(0, \"b1\")\n      it.assertValue(1, \"b2\")\n    }\n    assertThat(iterator.hasNext()).isTrue()\n    iterator.next().use {\n      assertThat(it.key()).isEqualTo(\"c\")\n      it.assertValue(0, \"c1\")\n      it.assertValue(1, \"c2\")\n    }\n    assertThat(iterator.hasNext()).isFalse()\n    assertFailsWith<NoSuchElementException> {\n      iterator.next()\n    }\n  }\n\n  @Test\n  fun iteratorElementsAddedDuringIterationAreOmitted() {\n    set(\"a\", \"a1\", \"a2\")\n    set(\"b\", \"b1\", \"b2\")\n    val iterator = cache.snapshots()\n    iterator.next().use { a ->\n      assertThat(a.key()).isEqualTo(\"a\")\n    }\n    set(\"c\", \"c1\", \"c2\")\n    iterator.next().use { b ->\n      assertThat(b.key()).isEqualTo(\"b\")\n    }\n    assertThat(iterator.hasNext()).isFalse()\n  }\n\n  @Test\n  fun iteratorElementsUpdatedDuringIterationAreUpdated() {\n    set(\"a\", \"a1\", \"a2\")\n    set(\"b\", \"b1\", \"b2\")\n    val iterator = cache.snapshots()\n    iterator.next().use {\n      assertThat(it.key()).isEqualTo(\"a\")\n    }\n    set(\"b\", \"b3\", \"b4\")\n    iterator.next().use {\n      assertThat(it.key()).isEqualTo(\"b\")\n      it.assertValue(0, \"b3\")\n      it.assertValue(1, \"b4\")\n    }\n  }\n\n  @Test\n  fun iteratorElementsRemovedDuringIterationAreOmitted() {\n    set(\"a\", \"a1\", \"a2\")\n    set(\"b\", \"b1\", \"b2\")\n    val iterator = cache.snapshots()\n    cache.remove(\"b\")\n    iterator.next().use {\n      assertThat(it.key()).isEqualTo(\"a\")\n    }\n    assertThat(iterator.hasNext()).isFalse()\n  }\n\n  @Test\n  fun iteratorRemove() {\n    set(\"a\", \"a1\", \"a2\")\n    val iterator = cache.snapshots()\n    val a = iterator.next()\n    a.close()\n    iterator.remove()\n    assertThat(cache[\"a\"]).isNull()\n  }\n\n  @Test\n  fun iteratorRemoveBeforeNext() {\n    set(\"a\", \"a1\", \"a2\")\n    val iterator = cache.snapshots()\n    assertFailsWith<IllegalStateException> {\n      iterator.remove()\n    }\n  }\n\n  @Test\n  fun iteratorRemoveOncePerCallToNext() {\n    set(\"a\", \"a1\", \"a2\")\n    val iterator = cache.snapshots()\n    iterator.next().use {\n      iterator.remove()\n    }\n    assertFailsWith<IllegalStateException> {\n      iterator.remove()\n    }\n  }\n\n  @Test\n  fun cacheClosedTruncatesIterator() {\n    set(\"a\", \"a1\", \"a2\")\n    val iterator = cache.snapshots()\n    cache.close()\n    assertThat(iterator.hasNext()).isFalse()\n  }\n\n  @Test\n  fun isClosed_uninitializedCache() {\n    // Create an uninitialized cache.\n    cache =\n      DiskLruCache(filesystem, cacheDir, appVersion, 2, Int.MAX_VALUE.toLong(), taskRunner).also {\n        toClose.add(it)\n      }\n    assertThat(cache.isClosed()).isFalse()\n    cache.close()\n    assertThat(cache.isClosed()).isTrue()\n  }\n\n  @Test\n  fun journalWriteFailsDuringEdit() {\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n\n    // We can't begin the edit if writing 'DIRTY' fails.\n    filesystem.setFaultyWrite(journalFile, true)\n    assertThat(cache.edit(\"c\")).isNull()\n\n    // Once the journal has a failure, subsequent writes aren't permitted.\n    filesystem.setFaultyWrite(journalFile, false)\n    assertThat(cache.edit(\"d\")).isNull()\n\n    // Confirm that the fault didn't corrupt entries stored before the fault was introduced.\n    cache.close()\n    cache =\n      DiskLruCache(filesystem, cacheDir, appVersion, 2, Int.MAX_VALUE.toLong(), taskRunner).also {\n        toClose.add(it)\n      }\n    assertValue(\"a\", \"a\", \"a\")\n    assertValue(\"b\", \"b\", \"b\")\n    assertAbsent(\"c\")\n    assertAbsent(\"d\")\n  }\n\n  /**\n   * We had a bug where the cache was left in an inconsistent state after a journal write failed.\n   * https://github.com/square/okhttp/issues/1211\n   */\n  @Test\n  fun journalWriteFailsDuringEditorCommit() {\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n\n    // Create an entry that fails to write to the journal during commit.\n    val editor = cache.edit(\"c\")!!\n    editor.setString(0, \"c\")\n    editor.setString(1, \"c\")\n    filesystem.setFaultyWrite(journalFile, true)\n    editor.commit()\n\n    // Once the journal has a failure, subsequent writes aren't permitted.\n    filesystem.setFaultyWrite(journalFile, false)\n    assertThat(cache.edit(\"d\")).isNull()\n\n    // Confirm that the fault didn't corrupt entries stored before the fault was introduced.\n    cache.close()\n    cache =\n      DiskLruCache(filesystem, cacheDir, appVersion, 2, Int.MAX_VALUE.toLong(), taskRunner).also {\n        toClose.add(it)\n      }\n    assertValue(\"a\", \"a\", \"a\")\n    assertValue(\"b\", \"b\", \"b\")\n    assertAbsent(\"c\")\n    assertAbsent(\"d\")\n  }\n\n  @Test\n  fun journalWriteFailsDuringEditorAbort() {\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n\n    // Create an entry that fails to write to the journal during abort.\n    val editor = cache.edit(\"c\")!!\n    editor.setString(0, \"c\")\n    editor.setString(1, \"c\")\n    filesystem.setFaultyWrite(journalFile, true)\n    editor.abort()\n\n    // Once the journal has a failure, subsequent writes aren't permitted.\n    filesystem.setFaultyWrite(journalFile, false)\n    assertThat(cache.edit(\"d\")).isNull()\n\n    // Confirm that the fault didn't corrupt entries stored before the fault was introduced.\n    cache.close()\n    cache =\n      DiskLruCache(filesystem, cacheDir, appVersion, 2, Int.MAX_VALUE.toLong(), taskRunner).also {\n        toClose.add(it)\n      }\n    assertValue(\"a\", \"a\", \"a\")\n    assertValue(\"b\", \"b\", \"b\")\n    assertAbsent(\"c\")\n    assertAbsent(\"d\")\n  }\n\n  @Test\n  fun journalWriteFailsDuringRemove() {\n    set(\"a\", \"a\", \"a\")\n    set(\"b\", \"b\", \"b\")\n\n    // Remove, but the journal write will fail.\n    filesystem.setFaultyWrite(journalFile, true)\n    assertThat(cache.remove(\"a\")).isTrue()\n\n    // Confirm that the entry was still removed.\n    filesystem.setFaultyWrite(journalFile, false)\n    cache.close()\n    cache =\n      DiskLruCache(filesystem, cacheDir, appVersion, 2, Int.MAX_VALUE.toLong(), taskRunner).also {\n        toClose.add(it)\n      }\n    assertAbsent(\"a\")\n    assertValue(\"b\", \"b\", \"b\")\n  }\n\n  @Test\n  fun cleanupTrimFailurePreventsNewEditors() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aa\")\n    set(\"b\", \"bb\", \"bbb\")\n\n    // Cause the cache trim job to fail.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", true)\n    taskFaker.runNextTask()\n\n    // Confirm that edits are prevented after a cache trim failure.\n    assertThat(cache.edit(\"a\")).isNull()\n    assertThat(cache.edit(\"b\")).isNull()\n    assertThat(cache.edit(\"c\")).isNull()\n\n    // Allow the test to clean up.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", false)\n  }\n\n  @Test\n  fun cleanupTrimFailureRetriedOnEditors() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aa\")\n    set(\"b\", \"bb\", \"bbb\")\n\n    // Cause the cache trim job to fail.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", true)\n    taskFaker.runNextTask()\n\n    // An edit should now add a job to clean up if the most recent trim failed.\n    assertThat(cache.edit(\"b\")).isNull()\n    taskFaker.runNextTask()\n\n    // Confirm a successful cache trim now allows edits.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", false)\n    assertThat(cache.edit(\"c\")).isNull()\n    taskFaker.runNextTask()\n    set(\"c\", \"cc\", \"cc\")\n    assertValue(\"c\", \"cc\", \"cc\")\n  }\n\n  @Test\n  fun cleanupTrimFailureWithInFlightEditor() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aaa\")\n    set(\"b\", \"bb\", \"bb\")\n    val inFlightEditor = cache.edit(\"c\")!!\n\n    // Cause the cache trim job to fail.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", true)\n    taskFaker.runNextTask()\n\n    // The in-flight editor can still write after a trim failure.\n    inFlightEditor.setString(0, \"cc\")\n    inFlightEditor.setString(1, \"cc\")\n    inFlightEditor.commit()\n\n    // Confirm the committed values are present after a successful cache trim.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", false)\n    taskFaker.runNextTask()\n    assertValue(\"c\", \"cc\", \"cc\")\n  }\n\n  @Test\n  fun cleanupTrimFailureAllowsSnapshotReads() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aa\")\n    set(\"b\", \"bb\", \"bbb\")\n\n    // Cause the cache trim job to fail.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", true)\n    taskFaker.runNextTask()\n\n    // Confirm we still allow snapshot reads after a trim failure.\n    assertValue(\"a\", \"aa\", \"aa\")\n    assertValue(\"b\", \"bb\", \"bbb\")\n\n    // Allow the test to clean up.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", false)\n  }\n\n  @Test\n  fun cleanupTrimFailurePreventsSnapshotWrites() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aa\")\n    set(\"b\", \"bb\", \"bbb\")\n\n    // Cause the cache trim job to fail.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", true)\n    taskFaker.runNextTask()\n\n    // Confirm snapshot writes are prevented after a trim failure.\n    cache[\"a\"]!!.use {\n      assertThat(it.edit()).isNull()\n    }\n    cache[\"b\"]!!.use {\n      assertThat(it.edit()).isNull()\n    }\n\n    // Allow the test to clean up.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", false)\n  }\n\n  @Test\n  fun evictAllAfterCleanupTrimFailure() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aa\")\n    set(\"b\", \"bb\", \"bbb\")\n\n    // Cause the cache trim job to fail.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", true)\n    taskFaker.runNextTask()\n\n    // Confirm we prevent edits after a trim failure.\n    assertThat(cache.edit(\"c\")).isNull()\n\n    // A successful eviction should allow new writes.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", false)\n    cache.evictAll()\n    set(\"c\", \"cc\", \"cc\")\n    assertValue(\"c\", \"cc\", \"cc\")\n  }\n\n  @Test\n  fun manualRemovalAfterCleanupTrimFailure() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aa\")\n    set(\"b\", \"bb\", \"bbb\")\n\n    // Cause the cache trim job to fail.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", true)\n    taskFaker.runNextTask()\n\n    // Confirm we prevent edits after a trim failure.\n    assertThat(cache.edit(\"c\")).isNull()\n\n    // A successful removal which trims the cache should allow new writes.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", false)\n    cache.remove(\"a\")\n    set(\"c\", \"cc\", \"cc\")\n    assertValue(\"c\", \"cc\", \"cc\")\n  }\n\n  @Test\n  fun flushingAfterCleanupTrimFailure() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aa\")\n    set(\"b\", \"bb\", \"bbb\")\n\n    // Cause the cache trim job to fail.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", true)\n    taskFaker.runNextTask()\n\n    // Confirm we prevent edits after a trim failure.\n    assertThat(cache.edit(\"c\")).isNull()\n\n    // A successful flush trims the cache and should allow new writes.\n    filesystem.setFaultyDelete(cacheDir / \"a.0\", false)\n    cache.flush()\n    set(\"c\", \"cc\", \"cc\")\n    assertValue(\"c\", \"cc\", \"cc\")\n  }\n\n  @Test\n  fun cleanupTrimFailureWithPartialSnapshot() {\n    cache.maxSize = 8\n    taskFaker.runNextTask()\n    set(\"a\", \"aa\", \"aa\")\n    set(\"b\", \"bb\", \"bbb\")\n\n    // Cause the cache trim to fail on the second value leaving a partial snapshot.\n    filesystem.setFaultyDelete(cacheDir / \"a.1\", true)\n    taskFaker.runNextTask()\n\n    // Confirm the partial snapshot is not returned.\n    assertThat(cache[\"a\"]).isNull()\n\n    // Confirm we prevent edits after a trim failure.\n    assertThat(cache.edit(\"a\")).isNull()\n\n    // Confirm the partial snapshot is not returned after a successful trim.\n    filesystem.setFaultyDelete(cacheDir / \"a.1\", false)\n    taskFaker.runNextTask()\n    assertThat(cache[\"a\"]).isNull()\n  }\n\n  @Test\n  fun noSizeCorruptionAfterCreatorDetached() {\n    Assumptions.assumeFalse(windows) // Windows can't have two concurrent editors.\n\n    // Create an editor for k1. Detach it by clearing the cache.\n    val editor = cache.edit(\"k1\")!!\n    editor.setString(0, \"a\")\n    editor.setString(1, \"a\")\n    cache.evictAll()\n\n    // Create a new value in its place.\n    set(\"k1\", \"bb\", \"bb\")\n    assertThat(cache.size()).isEqualTo(4)\n\n    // Committing the detached editor should not change the cache's size.\n    editor.commit()\n    assertThat(cache.size()).isEqualTo(4)\n    assertValue(\"k1\", \"bb\", \"bb\")\n  }\n\n  @Test\n  fun noSizeCorruptionAfterEditorDetached() {\n    Assumptions.assumeFalse(windows) // Windows can't have two concurrent editors.\n\n    set(\"k1\", \"a\", \"a\")\n\n    // Create an editor for k1. Detach it by clearing the cache.\n    val editor = cache.edit(\"k1\")!!\n    editor.setString(0, \"bb\")\n    editor.setString(1, \"bb\")\n    cache.evictAll()\n\n    // Create a new value in its place.\n    set(\"k1\", \"ccc\", \"ccc\")\n    assertThat(cache.size()).isEqualTo(6)\n\n    // Committing the detached editor should not change the cache's size.\n    editor.commit()\n    assertThat(cache.size()).isEqualTo(6)\n    assertValue(\"k1\", \"ccc\", \"ccc\")\n  }\n\n  @Test\n  fun noNewSourceAfterEditorDetached() {\n    set(\"k1\", \"a\", \"a\")\n    val editor = cache.edit(\"k1\")!!\n    cache.evictAll()\n    assertThat(editor.newSource(0)).isNull()\n  }\n\n  @Test\n  fun `edit discarded after editor detached`() {\n    set(\"k1\", \"a\", \"a\")\n\n    // Create an editor, then detach it.\n    val editor = cache.edit(\"k1\")!!\n    editor.newSink(0).buffer().use { sink ->\n      cache.evictAll()\n\n      // Complete the original edit. It goes into a black hole.\n      sink.writeUtf8(\"bb\")\n    }\n    assertThat(cache[\"k1\"]).isNull()\n  }\n\n  @Test\n  fun `edit discarded after editor detached with concurrent write`() {\n    Assumptions.assumeFalse(windows) // Windows can't have two concurrent editors.\n\n    set(\"k1\", \"a\", \"a\")\n\n    // Create an editor, then detach it.\n    val editor = cache.edit(\"k1\")!!\n    editor.newSink(0).buffer().use { sink ->\n      cache.evictAll()\n\n      // Create another value in its place.\n      set(\"k1\", \"ccc\", \"ccc\")\n\n      // Complete the original edit. It goes into a black hole.\n      sink.writeUtf8(\"bb\")\n    }\n    assertValue(\"k1\", \"ccc\", \"ccc\")\n  }\n\n  @Test\n  fun abortAfterDetach() {\n    set(\"k1\", \"a\", \"a\")\n    val editor = cache.edit(\"k1\")!!\n    cache.evictAll()\n    editor.abort()\n    assertThat(cache.size()).isEqualTo(0)\n    assertAbsent(\"k1\")\n  }\n\n  @Test\n  fun dontRemoveUnfinishedEntryWhenCreatingSnapshot() {\n    val creator = cache.edit(\"k1\")!!\n    creator.setString(0, \"ABC\")\n    creator.setString(1, \"DE\")\n    assertThat(creator.newSource(0)).isNull()\n    assertThat(creator.newSource(1)).isNull()\n    val snapshotWhileEditing = cache.snapshots()\n    assertThat(snapshotWhileEditing.hasNext()).isFalse() // entry still is being created/edited\n    creator.commit()\n    val snapshotAfterCommit = cache.snapshots()\n    assertThat(snapshotAfterCommit.hasNext(), \"Entry has been removed during creation.\").isTrue()\n    snapshotAfterCommit.next().close()\n  }\n\n  @Test\n  fun `Windows cannot read while writing`() {\n    Assumptions.assumeTrue(windows)\n\n    set(\"k1\", \"a\", \"a\")\n    val editor = cache.edit(\"k1\")!!\n    assertThat(cache[\"k1\"]).isNull()\n    editor.commit()\n  }\n\n  @Test\n  fun `Windows cannot write while reading`() {\n    Assumptions.assumeTrue(windows)\n\n    set(\"k1\", \"a\", \"a\")\n    val snapshot = cache[\"k1\"]!!\n    assertThat(cache.edit(\"k1\")).isNull()\n    snapshot.close()\n  }\n\n  @Test\n  fun `can read while reading`() {\n    set(\"k1\", \"a\", \"a\")\n    cache[\"k1\"]!!.use { snapshot1 ->\n      snapshot1.assertValue(0, \"a\")\n      cache[\"k1\"]!!.use { snapshot2 ->\n        snapshot2.assertValue(0, \"a\")\n        snapshot1.assertValue(1, \"a\")\n        snapshot2.assertValue(1, \"a\")\n      }\n    }\n  }\n\n  @Test\n  fun `remove while reading creates zombie that is removed when read finishes`() {\n    val afterRemoveFileContents = if (windows) \"a\" else null\n\n    set(\"k1\", \"a\", \"a\")\n    cache[\"k1\"]!!.use { snapshot1 ->\n      cache.remove(\"k1\")\n\n      // On Windows files still exist with open with 2 open sources.\n      assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isEqualTo(afterRemoveFileContents)\n      assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n\n      // On Windows files still exist with open with 1 open source.\n      snapshot1.assertValue(0, \"a\")\n      assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isEqualTo(afterRemoveFileContents)\n      assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n\n      // On all platforms files are deleted when all sources are closed.\n      snapshot1.assertValue(1, \"a\")\n      assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isNull()\n      assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n    }\n  }\n\n  @Test\n  fun `remove while writing creates zombie that is removed when write finishes`() {\n    val afterRemoveFileContents = if (windows) \"a\" else null\n\n    set(\"k1\", \"a\", \"a\")\n    val editor = cache.edit(\"k1\")!!\n    cache.remove(\"k1\")\n    assertThat(cache[\"k1\"]).isNull()\n\n    // On Windows files still exist while being edited.\n    assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isEqualTo(afterRemoveFileContents)\n    assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n\n    // On all platforms files are deleted when the edit completes.\n    editor.commit()\n    assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isNull()\n    assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n  }\n\n  @Test\n  fun `Windows cannot read zombie entry`() {\n    Assumptions.assumeTrue(windows)\n\n    set(\"k1\", \"a\", \"a\")\n    cache[\"k1\"]!!.use {\n      cache.remove(\"k1\")\n      assertThat(cache[\"k1\"]).isNull()\n    }\n  }\n\n  @Test\n  fun `Windows cannot write zombie entry`() {\n    Assumptions.assumeTrue(windows)\n\n    set(\"k1\", \"a\", \"a\")\n    cache[\"k1\"]!!.use {\n      cache.remove(\"k1\")\n      assertThat(cache.edit(\"k1\")).isNull()\n    }\n  }\n\n  @Test\n  fun `removed entry absent when iterating`() {\n    set(\"k1\", \"a\", \"a\")\n    cache[\"k1\"]!!.use {\n      cache.remove(\"k1\")\n      val snapshots = cache.snapshots()\n      assertThat(snapshots.hasNext()).isFalse()\n    }\n  }\n\n  @Test\n  fun `close with zombie read`() {\n    val afterRemoveFileContents = if (windows) \"a\" else null\n\n    set(\"k1\", \"a\", \"a\")\n    cache[\"k1\"]!!.use {\n      cache.remove(\"k1\")\n\n      // After we close the cache the files continue to exist!\n      cache.close()\n      assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isEqualTo(afterRemoveFileContents)\n      assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n\n      // But they disappear when the sources are closed.\n      it.assertValue(0, \"a\")\n      it.assertValue(1, \"a\")\n      assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isNull()\n      assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n    }\n  }\n\n  @Test\n  fun `close with zombie write`() {\n    val afterRemoveCleanFileContents = if (windows) \"a\" else null\n    val afterRemoveDirtyFileContents = if (windows) \"\" else null\n\n    set(\"k1\", \"a\", \"a\")\n    val editor = cache.edit(\"k1\")!!\n    val sink0 = editor.newSink(0)\n    cache.remove(\"k1\")\n\n    // After we close the cache the files continue to exist!\n    cache.close()\n    assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isEqualTo(afterRemoveCleanFileContents)\n    assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isEqualTo(afterRemoveDirtyFileContents)\n\n    // But they disappear when the edit completes.\n    sink0.close()\n    editor.commit()\n    assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isNull()\n    assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n  }\n\n  @Test\n  fun `close with completed zombie write`() {\n    val afterRemoveCleanFileContents = if (windows) \"a\" else null\n    val afterRemoveDirtyFileContents = if (windows) \"b\" else null\n\n    set(\"k1\", \"a\", \"a\")\n    val editor = cache.edit(\"k1\")!!\n    editor.setString(0, \"b\")\n    cache.remove(\"k1\")\n\n    // After we close the cache the files continue to exist!\n    cache.close()\n    assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isEqualTo(afterRemoveCleanFileContents)\n    assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isEqualTo(afterRemoveDirtyFileContents)\n\n    // But they disappear when the edit completes.\n    editor.commit()\n    assertThat(readFileOrNull(getCleanFile(\"k1\", 0))).isNull()\n    assertThat(readFileOrNull(getDirtyFile(\"k1\", 0))).isNull()\n  }\n\n  private fun assertJournalEquals(vararg expectedBodyLines: String) {\n    assertThat(readJournalLines()).isEqualTo(\n      listOf(DiskLruCache.MAGIC, DiskLruCache.VERSION_1, \"100\", \"2\", \"\") + expectedBodyLines,\n    )\n  }\n\n  private fun createJournal(vararg bodyLines: String) {\n    createJournalWithHeader(\n      DiskLruCache.MAGIC,\n      DiskLruCache.VERSION_1,\n      \"100\",\n      \"2\",\n      \"\",\n      *bodyLines,\n    )\n  }\n\n  private fun createJournalWithHeader(\n    magic: String,\n    version: String,\n    appVersion: String,\n    valueCount: String,\n    blank: String,\n    vararg bodyLines: String,\n  ) {\n    filesystem.write(journalFile) {\n      writeUtf8(\n        \"\"\"\n        |$magic\n        |$version\n        |$appVersion\n        |$valueCount\n        |$blank\n        |\n        \"\"\".trimMargin(),\n      )\n      for (line in bodyLines) {\n        writeUtf8(line)\n        writeUtf8(\"\\n\")\n      }\n    }\n  }\n\n  private fun readJournalLines(): List<String> {\n    val result = mutableListOf<String>()\n    filesystem.read(journalFile) {\n      while (true) {\n        val line = readUtf8Line() ?: break\n        result.add(line)\n      }\n    }\n    return result\n  }\n\n  private fun getCleanFile(\n    key: String,\n    index: Int,\n  ) = cacheDir / \"$key.$index\"\n\n  private fun getDirtyFile(\n    key: String,\n    index: Int,\n  ) = cacheDir / \"$key.$index.tmp\"\n\n  private fun readFile(file: Path): String =\n    filesystem.read(file) {\n      readUtf8()\n    }\n\n  private fun readFileOrNull(file: Path): String? =\n    try {\n      filesystem.read(file) {\n        readUtf8()\n      }\n    } catch (_: FileNotFoundException) {\n      null\n    }\n\n  fun writeFile(\n    file: Path,\n    content: String,\n  ) {\n    file.parent?.let {\n      filesystem.createDirectories(it)\n    }\n    filesystem.write(file) {\n      writeUtf8(content)\n    }\n  }\n\n  private fun generateSomeGarbageFiles() {\n    val dir1 = cacheDir / \"dir1\"\n    val dir2 = dir1 / \"dir2\"\n    writeFile(getCleanFile(\"g1\", 0), \"A\")\n    writeFile(getCleanFile(\"g1\", 1), \"B\")\n    writeFile(getCleanFile(\"g2\", 0), \"C\")\n    writeFile(getCleanFile(\"g2\", 1), \"D\")\n    writeFile(getCleanFile(\"g2\", 1), \"D\")\n    writeFile(cacheDir / \"otherFile0\", \"E\")\n    writeFile(dir2 / \"otherFile1\", \"F\")\n  }\n\n  private fun assertGarbageFilesAllDeleted() {\n    assertThat(filesystem.exists(getCleanFile(\"g1\", 0))).isFalse()\n    assertThat(filesystem.exists(getCleanFile(\"g1\", 1))).isFalse()\n    assertThat(filesystem.exists(getCleanFile(\"g2\", 0))).isFalse()\n    assertThat(filesystem.exists(getCleanFile(\"g2\", 1))).isFalse()\n    assertThat(filesystem.exists(cacheDir / \"otherFile0\")).isFalse()\n    assertThat(filesystem.exists(cacheDir / \"dir1\")).isFalse()\n  }\n\n  private operator fun set(\n    key: String,\n    value0: String,\n    value1: String,\n  ) {\n    val editor = cache.edit(key)!!\n    editor.setString(0, value0)\n    editor.setString(1, value1)\n    editor.commit()\n  }\n\n  private fun assertAbsent(key: String) {\n    val snapshot = cache[key]\n    if (snapshot != null) {\n      snapshot.close()\n      fail(\"\")\n    }\n    assertThat(filesystem.exists(getCleanFile(key, 0))).isFalse()\n    assertThat(filesystem.exists(getCleanFile(key, 1))).isFalse()\n    assertThat(filesystem.exists(getDirtyFile(key, 0))).isFalse()\n    assertThat(filesystem.exists(getDirtyFile(key, 1))).isFalse()\n  }\n\n  private fun assertValue(\n    key: String,\n    value0: String,\n    value1: String,\n  ) {\n    cache[key]!!.use {\n      it.assertValue(0, value0)\n      it.assertValue(1, value1)\n      assertThat(filesystem.exists(getCleanFile(key, 0))).isTrue()\n      assertThat(filesystem.exists(getCleanFile(key, 1))).isTrue()\n    }\n  }\n\n  private fun Snapshot.assertValue(\n    index: Int,\n    value: String,\n  ) {\n    getSource(index).use { source ->\n      assertThat(sourceAsString(source)).isEqualTo(value)\n      assertThat(getLength(index)).isEqualTo(value.length.toLong())\n    }\n  }\n\n  private fun sourceAsString(source: Source) = source.buffer().readUtf8()\n\n  private fun Editor.assertInoperable() {\n    assertFailsWith<IllegalStateException> {\n      setString(0, \"A\")\n    }\n    assertFailsWith<IllegalStateException> {\n      newSource(0)\n    }\n    assertFailsWith<IllegalStateException> {\n      newSink(0)\n    }\n    assertFailsWith<IllegalStateException> {\n      commit()\n    }\n    assertFailsWith<IllegalStateException> {\n      abort()\n    }\n  }\n\n  private fun Editor.setString(\n    index: Int,\n    value: String,\n  ) {\n    newSink(index).buffer().use { writer ->\n      writer.writeUtf8(value)\n    }\n  }\n\n  enum class Subject {\n    System {\n      override fun create() = FileSystem.SYSTEM\n\n      override val windows: Boolean\n        get() = TestUtil.windows\n    },\n\n    FakeUnix {\n      override fun create() = FakeFileSystem().apply { emulateUnix() }\n\n      override val windows: Boolean\n        get() = false\n    },\n\n    FakeWindows {\n      override fun create() = FakeFileSystem().apply { emulateWindows() }\n\n      override val windows: Boolean\n        get() = true\n    }, ;\n\n    abstract fun create(): FileSystem\n\n    abstract val windows: Boolean\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/concurrent/TaskLoggerTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.concurrent\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport org.junit.jupiter.api.Test\n\nclass TaskLoggerTest {\n  @Suppress(\"ktlint\")\n  @Test fun formatTime() {\n    assertThat(formatDuration(-3_499_999_999L)).isEqualTo(\" -3 s \")\n    assertThat(formatDuration(-3_000_000_000L)).isEqualTo(\" -3 s \")\n    assertThat(formatDuration(-2_500_000_000L)).isEqualTo(\" -3 s \")\n    assertThat(formatDuration(-2_499_999_999L)).isEqualTo(\" -2 s \")\n    assertThat(formatDuration(-1_500_000_000L)).isEqualTo(\" -2 s \")\n    assertThat(formatDuration(-1_499_999_999L)).isEqualTo(\" -1 s \")\n    assertThat(formatDuration(-1_000_000_000L)).isEqualTo(\" -1 s \")\n    assertThat(formatDuration(  -999_500_000L)).isEqualTo(\" -1 s \")\n    assertThat(formatDuration(  -999_499_999L)).isEqualTo(\"-999 ms\")\n    assertThat(formatDuration(  -998_500_000L)).isEqualTo(\"-999 ms\")\n    assertThat(formatDuration(  -998_499_999L)).isEqualTo(\"-998 ms\")\n    assertThat(formatDuration(    -1_499_999L)).isEqualTo(\" -1 ms\")\n    assertThat(formatDuration(      -999_500L)).isEqualTo(\" -1 ms\")\n    assertThat(formatDuration(      -999_499L)).isEqualTo(\"-999 µs\")\n    assertThat(formatDuration(      -998_500L)).isEqualTo(\"-999 µs\")\n    assertThat(formatDuration(        -1_500L)).isEqualTo(\" -2 µs\")\n    assertThat(formatDuration(        -1_499L)).isEqualTo(\" -1 µs\")\n    assertThat(formatDuration(        -1_000L)).isEqualTo(\" -1 µs\")\n    assertThat(formatDuration(          -999L)).isEqualTo(\" -1 µs\")\n    assertThat(formatDuration(          -500L)).isEqualTo(\" -1 µs\")\n    assertThat(formatDuration(          -499L)).isEqualTo(\"  0 µs\")\n\n    assertThat(formatDuration( 3_499_999_999L)).isEqualTo(\"  3 s \")\n    assertThat(formatDuration( 3_000_000_000L)).isEqualTo(\"  3 s \")\n    assertThat(formatDuration( 2_500_000_000L)).isEqualTo(\"  3 s \")\n    assertThat(formatDuration( 2_499_999_999L)).isEqualTo(\"  2 s \")\n    assertThat(formatDuration( 1_500_000_000L)).isEqualTo(\"  2 s \")\n    assertThat(formatDuration( 1_499_999_999L)).isEqualTo(\"  1 s \")\n    assertThat(formatDuration( 1_000_000_000L)).isEqualTo(\"  1 s \")\n    assertThat(formatDuration(   999_500_000L)).isEqualTo(\"  1 s \")\n    assertThat(formatDuration(   999_499_999L)).isEqualTo(\"999 ms\")\n    assertThat(formatDuration(   998_500_000L)).isEqualTo(\"999 ms\")\n    assertThat(formatDuration(   998_499_999L)).isEqualTo(\"998 ms\")\n    assertThat(formatDuration(     1_499_999L)).isEqualTo(\"  1 ms\")\n    assertThat(formatDuration(       999_500L)).isEqualTo(\"  1 ms\")\n    assertThat(formatDuration(       999_499L)).isEqualTo(\"999 µs\")\n    assertThat(formatDuration(       998_500L)).isEqualTo(\"999 µs\")\n    assertThat(formatDuration(         1_500L)).isEqualTo(\"  2 µs\")\n    assertThat(formatDuration(         1_499L)).isEqualTo(\"  1 µs\")\n    assertThat(formatDuration(         1_000L)).isEqualTo(\"  1 µs\")\n    assertThat(formatDuration(           999L)).isEqualTo(\"  1 µs\")\n    assertThat(formatDuration(           500L)).isEqualTo(\"  1 µs\")\n    assertThat(formatDuration(           499L)).isEqualTo(\"  0 µs\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/concurrent/TaskRunnerRealBackendTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.concurrent\n\nimport assertk.assertThat\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isTrue\nimport java.lang.Thread.UncaughtExceptionHandler\nimport java.util.concurrent.LinkedBlockingDeque\nimport java.util.concurrent.ThreadFactory\nimport java.util.concurrent.TimeUnit\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\n\n/**\n * Integration test to confirm that [TaskRunner] works with a real backend. Business logic is all\n * exercised by [TaskRunnerTest].\n *\n * This test is doing real sleeping with tolerances of 250 ms. Hopefully that's enough for even the\n * busiest of CI servers.\n */\n@Tag(\"Slowish\")\nclass TaskRunnerRealBackendTest {\n  private val log = LinkedBlockingDeque<String>()\n\n  private val loggingUncaughtExceptionHandler =\n    UncaughtExceptionHandler { _, throwable ->\n      log.put(\"uncaught exception: $throwable\")\n    }\n\n  private val threadFactory =\n    ThreadFactory { runnable ->\n      Thread(runnable, \"TaskRunnerRealBackendTest\").apply {\n        isDaemon = true\n        uncaughtExceptionHandler = loggingUncaughtExceptionHandler\n      }\n    }\n\n  private val backend = TaskRunner.RealBackend(threadFactory)\n  private val taskRunner = TaskRunner(backend)\n  private val queue = taskRunner.newQueue()\n\n  @AfterEach fun tearDown() {\n    backend.shutdown()\n  }\n\n  @Test fun test() {\n    val t1 = System.nanoTime() / 1e6\n\n    val delays = mutableListOf(TimeUnit.MILLISECONDS.toNanos(1000), -1L)\n    queue.schedule(\"task\", TimeUnit.MILLISECONDS.toNanos(750)) {\n      log.put(\"runOnce delays.size=${delays.size}\")\n      return@schedule delays.removeAt(0)\n    }\n\n    assertThat(log.take()).isEqualTo(\"runOnce delays.size=2\")\n    val t2 = System.nanoTime() / 1e6 - t1\n    assertThat(t2).isCloseTo(750.0, 250.0)\n\n    assertThat(log.take()).isEqualTo(\"runOnce delays.size=1\")\n    val t3 = System.nanoTime() / 1e6 - t1\n    assertThat(t3).isCloseTo(1750.0, 250.0)\n  }\n\n  @Test fun taskFailsWithUncheckedException() {\n    queue.schedule(\"task\", TimeUnit.MILLISECONDS.toNanos(100)) {\n      log.put(\"failing task running\")\n      throw RuntimeException(\"boom!\")\n    }\n\n    queue.schedule(\"task\", TimeUnit.MILLISECONDS.toNanos(200)) {\n      log.put(\"normal task running\")\n      return@schedule -1L\n    }\n\n    queue.idleLatch().await(500, TimeUnit.MILLISECONDS)\n\n    assertThat(log.take()).isEqualTo(\"failing task running\")\n    assertThat(log.take()).isEqualTo(\"uncaught exception: java.lang.RuntimeException: boom!\")\n    assertThat(log.take()).isEqualTo(\"normal task running\")\n    assertThat(log).isEmpty()\n  }\n\n  @Test fun idleLatchAfterShutdown() {\n    queue.schedule(\"task\") {\n      Thread.sleep(250)\n      backend.shutdown()\n      return@schedule -1L\n    }\n\n    assertThat(queue.idleLatch().await(500L, TimeUnit.MILLISECONDS)).isTrue()\n    assertThat(queue.idleLatch().count).isEqualTo(0)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/concurrent/TaskRunnerTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.concurrent\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.containsExactlyInAnyOrder\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isSameInstanceAs\nimport java.util.concurrent.RejectedExecutionException\nimport kotlin.test.assertFailsWith\nimport okhttp3.TestLogHandler\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass TaskRunnerTest {\n  private val taskFaker = TaskFaker()\n\n  @RegisterExtension @JvmField\n  val testLogHandler = TestLogHandler(taskFaker.logger)\n\n  private val taskRunner = taskFaker.taskRunner\n  private val log = mutableListOf<String>()\n  private val redQueue = taskRunner.newQueue()\n  private val blueQueue = taskRunner.newQueue()\n  private val greenQueue = taskRunner.newQueue()\n\n  @AfterEach\n  internal fun tearDown() {\n    taskFaker.close()\n  }\n\n  @Test fun executeDelayed() {\n    redQueue.execute(\"task\", 100.µs) {\n      log += \"run@${taskFaker.nanoTime}\"\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).containsExactly()\n\n    taskFaker.advanceUntil(99.µs)\n    assertThat(log).containsExactly()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  @Test fun executeRepeated() {\n    val delays = mutableListOf(50.µs, 150.µs, -1L)\n    redQueue.schedule(\"task\", 100.µs) {\n      log += \"run@${taskFaker.nanoTime}\"\n      return@schedule delays.removeAt(0)\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).containsExactly()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.advanceUntil(150.µs)\n    assertThat(log).containsExactly(\"run@100000\", \"run@150000\")\n\n    taskFaker.advanceUntil(299.µs)\n    assertThat(log).containsExactly(\"run@100000\", \"run@150000\")\n\n    taskFaker.advanceUntil(300.µs)\n    assertThat(log).containsExactly(\"run@100000\", \"run@150000\", \"run@300000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n      \"FINE: Q10000 run again after  50 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n      \"FINE: Q10000 run again after 150 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  /** Repeat with a delay of 200 but schedule with a delay of 50. The schedule wins. */\n  @Test fun executeScheduledEarlierReplacesRepeatedLater() {\n    val task =\n      object : Task(\"task\") {\n        val schedules = mutableListOf(50.µs)\n        val delays = mutableListOf(200.µs, -1)\n\n        override fun runOnce(): Long {\n          log += \"run@${taskFaker.nanoTime}\"\n          if (schedules.isNotEmpty()) {\n            redQueue.schedule(this, schedules.removeAt(0))\n          }\n          return delays.removeAt(0)\n        }\n      }\n    redQueue.schedule(task, 100.µs)\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.advanceUntil(150.µs)\n    assertThat(log).containsExactly(\"run@100000\", \"run@150000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 scheduled after  50 µs: task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n      \"FINE: Q10000 already scheduled     : task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  /** Schedule with a delay of 200 but repeat with a delay of 50. The repeat wins. */\n  @Test fun executeRepeatedEarlierReplacesScheduledLater() {\n    val task =\n      object : Task(\"task\") {\n        val schedules = mutableListOf(200.µs)\n        val delays = mutableListOf(50.µs, -1L)\n\n        override fun runOnce(): Long {\n          log += \"run@${taskFaker.nanoTime}\"\n          if (schedules.isNotEmpty()) {\n            redQueue.schedule(this, schedules.removeAt(0))\n          }\n          return delays.removeAt(0)\n        }\n      }\n    redQueue.schedule(task, 100.µs)\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.advanceUntil(150.µs)\n    assertThat(log).containsExactly(\"run@100000\", \"run@150000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 scheduled after 200 µs: task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n      \"FINE: Q10000 run again after  50 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  @Test fun cancelReturnsTruePreventsNextExecution() {\n    redQueue.execute(\"task\", 100.µs) {\n      log += \"run@${taskFaker.nanoTime}\"\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    redQueue.cancelAll()\n\n    taskFaker.advanceUntil(99.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 canceled              : task\",\n    )\n  }\n\n  @Test fun cancelReturnsFalseDoesNotCancel() {\n    redQueue.schedule(\n      object : Task(\"task\", cancelable = false) {\n        override fun runOnce(): Long {\n          log += \"run@${taskFaker.nanoTime}\"\n          return -1L\n        }\n      },\n      100.µs,\n    )\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    redQueue.cancelAll()\n\n    taskFaker.advanceUntil(99.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  @Test fun cancelWhileExecutingPreventsRepeat() {\n    redQueue.schedule(\"task\", 100.µs) {\n      log += \"run@${taskFaker.nanoTime}\"\n      redQueue.cancelAll()\n      return@schedule 100.µs\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  @Test fun cancelWhileExecutingDoesNothingIfTaskDoesNotRepeat() {\n    redQueue.execute(\"task\", 100.µs) {\n      log += \"run@${taskFaker.nanoTime}\"\n      redQueue.cancelAll()\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  @Test fun cancelWhileExecutingDoesNotStopUncancelableTask() {\n    redQueue.schedule(\n      object : Task(\"task\", cancelable = false) {\n        val delays = mutableListOf(50.µs, -1L)\n\n        override fun runOnce(): Long {\n          log += \"run@${taskFaker.nanoTime}\"\n          redQueue.cancelAll()\n          return delays.removeAt(0)\n        }\n      },\n      100.µs,\n    )\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.advanceUntil(150.µs)\n    assertThat(log).containsExactly(\"run@100000\", \"run@150000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n      \"FINE: Q10000 run again after  50 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  @Test fun interruptingCoordinatorAttemptsToCancelsAndSucceeds() {\n    redQueue.execute(\"task\", 100.µs) {\n      log += \"run@${taskFaker.nanoTime}\"\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.interruptCoordinatorThread()\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 canceled              : task\",\n    )\n  }\n\n  @Test fun interruptingCoordinatorAttemptsToCancelsAndFails() {\n    redQueue.schedule(\n      object : Task(\"task\", cancelable = false) {\n        override fun runOnce(): Long {\n          log += \"run@${taskFaker.nanoTime}\"\n          return -1L\n        }\n      },\n      100.µs,\n    )\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.interruptCoordinatorThread()\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  /** Inspect how many runnables have been enqueued. If none then we're truly sequential. */\n  @Test fun singleQueueIsSerial() {\n    redQueue.execute(\"task one\", 100.µs) {\n      log += \"one:run@${taskFaker.nanoTime} parallel=${taskFaker.isParallel}\"\n    }\n\n    redQueue.execute(\"task two\", 100.µs) {\n      log += \"two:run@${taskFaker.nanoTime} parallel=${taskFaker.isParallel}\"\n    }\n\n    redQueue.execute(\"task three\", 100.µs) {\n      log += \"three:run@${taskFaker.nanoTime} parallel=${taskFaker.isParallel}\"\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\n      \"one:run@100000 parallel=false\",\n      \"two:run@100000 parallel=false\",\n      \"three:run@100000 parallel=false\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task one\",\n      \"FINE: Q10000 scheduled after 100 µs: task two\",\n      \"FINE: Q10000 scheduled after 100 µs: task three\",\n      \"FINE: Q10000 starting              : task one\",\n      \"FINE: Q10000 finished run in   0 µs: task one\",\n      \"FINE: Q10000 starting              : task two\",\n      \"FINE: Q10000 finished run in   0 µs: task two\",\n      \"FINE: Q10000 starting              : task three\",\n      \"FINE: Q10000 finished run in   0 µs: task three\",\n    )\n  }\n\n  /** Inspect how many runnables have been enqueued. If non-zero then we're truly parallel. */\n  @Test fun differentQueuesAreParallel() {\n    redQueue.execute(\"task one\", 100.µs) {\n      log += \"one:run@${taskFaker.nanoTime} parallel=${taskFaker.isParallel}\"\n    }\n\n    blueQueue.execute(\"task two\", 100.µs) {\n      log += \"two:run@${taskFaker.nanoTime} parallel=${taskFaker.isParallel}\"\n    }\n\n    greenQueue.execute(\"task three\", 100.µs) {\n      log += \"three:run@${taskFaker.nanoTime} parallel=${taskFaker.isParallel}\"\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactlyInAnyOrder(\n      \"one:run@100000 parallel=true\",\n      \"two:run@100000 parallel=true\",\n      \"three:run@100000 parallel=true\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactlyInAnyOrder(\n      \"FINE: Q10000 scheduled after 100 µs: task one\",\n      \"FINE: Q10001 scheduled after 100 µs: task two\",\n      \"FINE: Q10002 scheduled after 100 µs: task three\",\n      \"FINE: Q10000 starting              : task one\",\n      \"FINE: Q10000 finished run in   0 µs: task one\",\n      \"FINE: Q10001 starting              : task two\",\n      \"FINE: Q10001 finished run in   0 µs: task two\",\n      \"FINE: Q10002 starting              : task three\",\n      \"FINE: Q10002 finished run in   0 µs: task three\",\n    )\n  }\n\n  /** Test the introspection method [TaskQueue.scheduledTasks]. */\n  @Test fun scheduledTasks() {\n    redQueue.execute(\"task one\", 100.µs) {\n      // Do nothing.\n    }\n\n    redQueue.execute(\"task two\", 200.µs) {\n      // Do nothing.\n    }\n\n    assertThat(redQueue.scheduledTasks.toString()).isEqualTo(\"[task one, task two]\")\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task one\",\n      \"FINE: Q10000 scheduled after 200 µs: task two\",\n    )\n  }\n\n  /**\n   * We don't track the active task in scheduled tasks. This behavior might be a mistake, but it's\n   * cumbersome to implement properly because the active task might be a cancel.\n   */\n  @Test fun scheduledTasksDoesNotIncludeRunningTask() {\n    val task =\n      object : Task(\"task one\") {\n        val schedules = mutableListOf(200.µs)\n\n        override fun runOnce(): Long {\n          if (schedules.isNotEmpty()) {\n            redQueue.schedule(this, schedules.removeAt(0)) // Add it at the end also.\n          }\n          log += \"scheduledTasks=${redQueue.scheduledTasks}\"\n          return -1L\n        }\n      }\n    redQueue.schedule(task, 100.µs)\n\n    redQueue.execute(\"task two\", 200.µs) {\n      log += \"scheduledTasks=${redQueue.scheduledTasks}\"\n    }\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\n      \"scheduledTasks=[task two, task one]\",\n    )\n\n    taskFaker.advanceUntil(200.µs)\n    assertThat(log).containsExactly(\n      \"scheduledTasks=[task two, task one]\",\n      \"scheduledTasks=[task one]\",\n    )\n\n    taskFaker.advanceUntil(300.µs)\n    assertThat(log).containsExactly(\n      \"scheduledTasks=[task two, task one]\",\n      \"scheduledTasks=[task one]\",\n      \"scheduledTasks=[]\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task one\",\n      \"FINE: Q10000 scheduled after 200 µs: task two\",\n      \"FINE: Q10000 starting              : task one\",\n      \"FINE: Q10000 scheduled after 200 µs: task one\",\n      \"FINE: Q10000 finished run in   0 µs: task one\",\n      \"FINE: Q10000 starting              : task two\",\n      \"FINE: Q10000 finished run in   0 µs: task two\",\n      \"FINE: Q10000 starting              : task one\",\n      \"FINE: Q10000 finished run in   0 µs: task one\",\n    )\n  }\n\n  /**\n   * The runner doesn't hold references to its queues! Otherwise we'd need a mechanism to clean them\n   * up when they're no longer needed and that's annoying. Instead the task runner only tracks which\n   * queues have work scheduled.\n   */\n  @Test fun activeQueuesContainsOnlyQueuesWithScheduledTasks() {\n    redQueue.execute(\"task one\", 100.µs) {\n      // Do nothing.\n    }\n\n    blueQueue.execute(\"task two\", 200.µs) {\n      // Do nothing.\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(taskRunner.activeQueues()).containsExactly(redQueue, blueQueue)\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(taskRunner.activeQueues()).containsExactly(blueQueue)\n\n    taskFaker.advanceUntil(200.µs)\n    assertThat(taskRunner.activeQueues()).isEmpty()\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task one\",\n      \"FINE: Q10001 scheduled after 200 µs: task two\",\n      \"FINE: Q10000 starting              : task one\",\n      \"FINE: Q10000 finished run in   0 µs: task one\",\n      \"FINE: Q10001 starting              : task two\",\n      \"FINE: Q10001 finished run in   0 µs: task two\",\n    )\n  }\n\n  @Test fun taskNameIsUsedForThreadNameWhenRunning() {\n    redQueue.execute(\"lucky task\") {\n      log += \"run threadName:${Thread.currentThread().name}\"\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).containsExactly(\"run threadName:lucky task\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after   0 µs: lucky task\",\n      \"FINE: Q10000 starting              : lucky task\",\n      \"FINE: Q10000 finished run in   0 µs: lucky task\",\n    )\n  }\n\n  @Test fun shutdownSuccessfullyCancelsScheduledTasks() {\n    redQueue.execute(\"task\", 100.µs) {\n      log += \"run@${taskFaker.nanoTime}\"\n    }\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    redQueue.shutdown()\n\n    taskFaker.advanceUntil(99.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 canceled              : task\",\n    )\n  }\n\n  @Test fun shutdownFailsToCancelsScheduledTasks() {\n    redQueue.schedule(\n      object : Task(\"task\", false) {\n        override fun runOnce(): Long {\n          log += \"run@${taskFaker.nanoTime}\"\n          return 50.µs\n        }\n      },\n      100.µs,\n    )\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).isEmpty()\n\n    redQueue.shutdown()\n\n    taskFaker.advanceUntil(99.µs)\n    assertThat(log).isEmpty()\n\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"run@100000\")\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 scheduled after 100 µs: task\",\n      \"FINE: Q10000 starting              : task\",\n      \"FINE: Q10000 finished run in   0 µs: task\",\n    )\n  }\n\n  @Test fun scheduleDiscardsTaskWhenShutdown() {\n    redQueue.shutdown()\n\n    redQueue.execute(\"task\", 100.µs) {\n      // Do nothing.\n    }\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 schedule canceled (queue is shutdown): task\",\n    )\n  }\n\n  @Test fun scheduleThrowsWhenShutdown() {\n    redQueue.shutdown()\n\n    assertFailsWith<RejectedExecutionException> {\n      redQueue.schedule(\n        object : Task(\"task\", cancelable = false) {\n          override fun runOnce(): Long = -1L\n        },\n        100.µs,\n      )\n    }\n\n    taskFaker.assertNoMoreTasks()\n\n    assertThat(testLogHandler.takeAll()).containsExactly(\n      \"FINE: Q10000 schedule failed (queue is shutdown): task\",\n    )\n  }\n\n  @Test fun idleLatch() {\n    redQueue.execute(\"task\") {\n      log += \"run@${taskFaker.nanoTime}\"\n    }\n\n    val idleLatch = redQueue.idleLatch()\n    assertThat(idleLatch.count).isEqualTo(1)\n\n    taskFaker.advanceUntil(0.µs)\n    assertThat(log).containsExactly(\"run@0\")\n\n    assertThat(idleLatch.count).isEqualTo(0)\n  }\n\n  @Test fun multipleCallsToIdleLatchReturnSameInstance() {\n    redQueue.execute(\"task\") {\n      log += \"run@${taskFaker.nanoTime}\"\n    }\n\n    val idleLatch1 = redQueue.idleLatch()\n    val idleLatch2 = redQueue.idleLatch()\n    assertThat(idleLatch2).isSameInstanceAs(idleLatch1)\n  }\n\n  @Test fun cancelAllWhenEmptyDoesNotStartWorkerThread() {\n    redQueue.execute(\"red task\", 100.µs) {\n      error(\"expected to be canceled\")\n    }\n    assertThat(taskFaker.executeCallCount).isEqualTo(1)\n\n    blueQueue.execute(\"task\", 100.µs) {\n      error(\"expected to be canceled\")\n    }\n    assertThat(taskFaker.executeCallCount).isEqualTo(1)\n\n    redQueue.cancelAll()\n    assertThat(taskFaker.executeCallCount).isEqualTo(1)\n\n    blueQueue.cancelAll()\n    assertThat(taskFaker.executeCallCount).isEqualTo(1)\n  }\n\n  @Test fun noMoreThanOneWorkerThreadWaitingToStartAtATime() {\n    // Enqueueing the red task starts a thread because the head of the queue changed.\n    redQueue.execute(\"red task\") {\n      log += \"red:starting@${taskFaker.nanoTime}\"\n      taskFaker.sleep(100.µs)\n      log += \"red:finishing@${taskFaker.nanoTime}\"\n    }\n    assertThat(taskFaker.executeCallCount).isEqualTo(1)\n\n    // Enqueueing the blue task doesn't start a thread because the red one is still starting.\n    blueQueue.execute(\"blue task\") {\n      log += \"blue:starting@${taskFaker.nanoTime}\"\n      taskFaker.sleep(100.µs)\n      log += \"blue:finishing@${taskFaker.nanoTime}\"\n    }\n    assertThat(taskFaker.executeCallCount).isEqualTo(1)\n\n    // Running the red task starts another thread, so the two can run in parallel.\n    taskFaker.runNextTask()\n    assertThat(log).containsExactly(\"red:starting@0\")\n    assertThat(taskFaker.executeCallCount).isEqualTo(2)\n\n    // Next the blue task starts.\n    taskFaker.runNextTask()\n    assertThat(log).containsExactly(\n      \"red:starting@0\",\n      \"blue:starting@0\",\n    )\n    assertThat(taskFaker.executeCallCount).isEqualTo(2)\n\n    // Advance time until the tasks complete.\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\n      \"red:starting@0\",\n      \"blue:starting@0\",\n      \"red:finishing@100000\",\n      \"blue:finishing@100000\",\n    )\n    taskFaker.assertNoMoreTasks()\n    assertThat(taskFaker.executeCallCount).isEqualTo(2)\n  }\n\n  @Test fun onlyOneCoordinatorWaitingToStartFutureTasks() {\n    // Enqueueing the red task starts a coordinator thread.\n    redQueue.execute(\"red task\", 100.µs) {\n      log += \"red:run@${taskFaker.nanoTime}\"\n    }\n    assertThat(taskFaker.executeCallCount).isEqualTo(1)\n\n    // Enqueueing the blue task doesn't need a 2nd coordinator yet.\n    blueQueue.execute(\"blue task\", 200.µs) {\n      log += \"blue:run@${taskFaker.nanoTime}\"\n    }\n    assertThat(taskFaker.executeCallCount).isEqualTo(1)\n\n    // Nothing to do.\n    taskFaker.runTasks()\n    assertThat(log).isEmpty()\n\n    // At 100.µs, the coordinator runs the red task and starts a thread for the new coordinator.\n    taskFaker.advanceUntil(100.µs)\n    assertThat(log).containsExactly(\"red:run@100000\")\n    assertThat(taskFaker.executeCallCount).isEqualTo(2)\n\n    // At 200.µs, the blue task runs.\n    taskFaker.advanceUntil(200.µs)\n    assertThat(log).containsExactly(\"red:run@100000\", \"blue:run@200000\")\n    assertThat(taskFaker.executeCallCount).isEqualTo(2)\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  private val Int.µs: Long\n    get() = this * 1_000L\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/ConnectionPoolTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport assertk.assertThat\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNotEmpty\nimport assertk.assertions.isTrue\nimport okhttp3.ConnectionPool\nimport okhttp3.FakeRoutePlanner\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.TestUtil.awaitGarbageCollection\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.TaskRunner.RealBackend\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.http2.MockHttp2Peer\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\n\nclass ConnectionPoolTest {\n  private val routePlanner = FakeRoutePlanner()\n  private val factory = routePlanner.factory\n  private val peer = MockHttp2Peer()\n\n  /** The fake task runner prevents the cleanup runnable from being started.  */\n  private val addressA = factory.newAddress(\"a\")\n  private val routeA1 = factory.newRoute(addressA)\n  private val addressB = factory.newAddress(\"b\")\n  private val routeB1 = factory.newRoute(addressB)\n  private val addressC = factory.newAddress(\"c\")\n  private val routeC1 = factory.newRoute(addressC)\n\n  @AfterEach fun tearDown() {\n    factory.close()\n    peer.close()\n  }\n\n  @Test fun connectionsEvictedWhenIdleLongEnough() {\n    val pool = factory.newConnectionPool()\n    val c1 = factory.newConnection(pool, routeA1, 50L)\n\n    // Running at time 50, the pool returns that nothing can be evicted until time 150.\n    assertThat(pool.closeConnections(50L)).isEqualTo(100L)\n    assertThat(pool.connectionCount()).isEqualTo(1)\n    assertThat(c1.socket().isClosed).isFalse()\n\n    // Running at time 60, the pool returns that nothing can be evicted until time 150.\n    assertThat(pool.closeConnections(60L)).isEqualTo(90L)\n    assertThat(pool.connectionCount()).isEqualTo(1)\n    assertThat(c1.socket().isClosed).isFalse()\n\n    // Running at time 149, the pool returns that nothing can be evicted until time 150.\n    assertThat(pool.closeConnections(149L)).isEqualTo(1L)\n    assertThat(pool.connectionCount()).isEqualTo(1)\n    assertThat(c1.socket().isClosed).isFalse()\n\n    // Running at time 150, the pool evicts.\n    assertThat(pool.closeConnections(150L)).isEqualTo(0)\n    assertThat(pool.connectionCount()).isEqualTo(0)\n    assertThat(c1.socket().isClosed).isTrue()\n\n    // Running again, the pool reports that no further runs are necessary.\n    assertThat(pool.closeConnections(150L)).isEqualTo(-1)\n    assertThat(pool.connectionCount()).isEqualTo(0)\n    assertThat(c1.socket().isClosed).isTrue()\n  }\n\n  @Test fun inUseConnectionsNotEvicted() {\n    val pool = factory.newConnectionPool()\n    val poolApi = ConnectionPool(pool)\n    val c1 = factory.newConnection(pool, routeA1, 50L)\n    val client =\n      OkHttpClient\n        .Builder()\n        .connectionPool(poolApi)\n        .build()\n    val call = client.newCall(Request(addressA.url)) as RealCall\n    call.enterNetworkInterceptorExchange(call.request(), true, factory.newChain(call))\n    c1.withLock { call.acquireConnectionNoEvents(c1) }\n\n    // Running at time 50, the pool returns that nothing can be evicted until time 150.\n    assertThat(pool.closeConnections(50L)).isEqualTo(100L)\n    assertThat(pool.connectionCount()).isEqualTo(1)\n    assertThat(c1.socket().isClosed).isFalse()\n\n    // Running at time 60, the pool returns that nothing can be evicted until time 160.\n    assertThat(pool.closeConnections(60L)).isEqualTo(100L)\n    assertThat(pool.connectionCount()).isEqualTo(1)\n    assertThat(c1.socket().isClosed).isFalse()\n\n    // Running at time 160, the pool returns that nothing can be evicted until time 260.\n    assertThat(pool.closeConnections(160L)).isEqualTo(100L)\n    assertThat(pool.connectionCount()).isEqualTo(1)\n    assertThat(c1.socket().isClosed).isFalse()\n  }\n\n  @Test fun cleanupPrioritizesEarliestEviction() {\n    val pool = factory.newConnectionPool()\n    val c1 = factory.newConnection(pool, routeA1, 75L)\n    val c2 = factory.newConnection(pool, routeB1, 50L)\n\n    // Running at time 75, the pool returns that nothing can be evicted until time 150.\n    assertThat(pool.closeConnections(75L)).isEqualTo(75L)\n    assertThat(pool.connectionCount()).isEqualTo(2)\n\n    // Running at time 149, the pool returns that nothing can be evicted until time 150.\n    assertThat(pool.closeConnections(149L)).isEqualTo(1L)\n    assertThat(pool.connectionCount()).isEqualTo(2)\n\n    // Running at time 150, the pool evicts c2.\n    assertThat(pool.closeConnections(150L)).isEqualTo(0L)\n    assertThat(pool.connectionCount()).isEqualTo(1)\n    assertThat(c1.socket().isClosed).isFalse()\n    assertThat(c2.socket().isClosed).isTrue()\n\n    // Running at time 150, the pool returns that nothing can be evicted until time 175.\n    assertThat(pool.closeConnections(150L)).isEqualTo(25L)\n    assertThat(pool.connectionCount()).isEqualTo(1)\n\n    // Running at time 175, the pool evicts c1.\n    assertThat(pool.closeConnections(175L)).isEqualTo(0L)\n    assertThat(pool.connectionCount()).isEqualTo(0)\n    assertThat(c1.socket().isClosed).isTrue()\n    assertThat(c2.socket().isClosed).isTrue()\n  }\n\n  @Test fun oldestConnectionsEvictedIfIdleLimitExceeded() {\n    val pool =\n      factory.newConnectionPool(\n        maxIdleConnections = 2,\n      )\n    val c1 = factory.newConnection(pool, routeA1, 50L)\n    val c2 = factory.newConnection(pool, routeB1, 75L)\n\n    // With 2 connections, there's no need to evict until the connections time out.\n    assertThat(pool.closeConnections(100L)).isEqualTo(50L)\n    assertThat(pool.connectionCount()).isEqualTo(2)\n    assertThat(c1.socket().isClosed).isFalse()\n    assertThat(c2.socket().isClosed).isFalse()\n\n    // Add a third connection\n    val c3 = factory.newConnection(pool, routeC1, 75L)\n\n    // The third connection bounces the first.\n    assertThat(pool.closeConnections(100L)).isEqualTo(0L)\n    assertThat(pool.connectionCount()).isEqualTo(2)\n    assertThat(c1.socket().isClosed).isTrue()\n    assertThat(c2.socket().isClosed).isFalse()\n    assertThat(c3.socket().isClosed).isFalse()\n  }\n\n  @Test fun leakedAllocation() {\n    val pool = factory.newConnectionPool()\n    val poolApi = ConnectionPool(pool)\n    val c1 = factory.newConnection(pool, routeA1, 0L)\n    allocateAndLeakAllocation(poolApi, c1)\n    awaitGarbageCollection()\n    assertThat(pool.closeConnections(100L)).isEqualTo(0L)\n    assertThat(c1.calls).isEmpty()\n\n    // Can't allocate once a leak has been detected.\n    assertThat(c1.noNewExchanges).isTrue()\n  }\n\n  @Test fun interruptStopsThread() {\n    val taskRunnerThreads = mutableListOf<Thread>()\n    val taskRunner =\n      TaskRunner(\n        RealBackend { runnable ->\n          Thread(runnable, \"interruptStopsThread TaskRunner\")\n            .also { taskRunnerThreads += it }\n        },\n      )\n\n    val pool =\n      factory.newConnectionPool(\n        taskRunner = taskRunner,\n        maxIdleConnections = 2,\n      )\n    factory.newConnection(pool, routeA1)\n    // Racy causing flaky tests\n    // assertThat(taskRunner.activeQueues()).isNotEmpty()\n    assertThat(taskRunnerThreads).isNotEmpty()\n    Thread.sleep(100)\n    for (t in taskRunnerThreads) {\n      t.interrupt()\n    }\n    Thread.sleep(100)\n    assertThat(taskRunner.activeQueues()).isEmpty()\n  }\n\n  /** Use a helper method so there's no hidden reference remaining on the stack.  */\n  private fun allocateAndLeakAllocation(\n    pool: ConnectionPool,\n    connection: RealConnection,\n  ) {\n    val client =\n      OkHttpClient\n        .Builder()\n        .connectionPool(pool)\n        .build()\n    val call = client.newCall(Request(connection.route().address.url)) as RealCall\n    call.enterNetworkInterceptorExchange(call.request(), true, factory.newChain(call))\n    connection.withLock { call.acquireConnectionNoEvents(connection) }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/FastFallbackExchangeFinderTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport java.net.UnknownServiceException\nimport kotlin.test.assertFailsWith\nimport okhttp3.FakeRoutePlanner\nimport okhttp3.FakeRoutePlanner.ConnectState.TLS_CONNECTED\nimport okhttp3.internal.concurrent.TaskFaker\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\n\n/**\n * Unit test for [FastFallbackExchangeFinder] implementation details.\n *\n * This test uses [TaskFaker] to deterministically test racy code. Each function in this test has\n * the same structure:\n *\n *  * prepare a set of plans, each with a predictable connect delay\n *  * attempt to find a connection\n *  * step through time, asserting that the expected side effects are performed.\n */\ninternal class FastFallbackExchangeFinderTest {\n  private val taskFaker = TaskFaker()\n  private val taskRunner = taskFaker.taskRunner\n\n  /**\n   * Note that we don't use the same [TaskFaker] for this factory. That way off-topic tasks like\n   * connection pool maintenance tasks don't add noise to route planning tests.\n   */\n  private val routePlanner = FakeRoutePlanner(taskFaker = taskFaker)\n  private val finder = FastFallbackExchangeFinder(routePlanner, taskRunner)\n\n  @AfterEach\n  fun tearDown() {\n    taskFaker.close()\n    routePlanner.close()\n  }\n\n  @Test\n  fun takeConnectedConnection() {\n    val plan0 = routePlanner.addPlan()\n    plan0.connectState = TLS_CONNECTED\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan0.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun takeConnectingConnection() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 240.ms\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan0.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(240.ms)\n    assertEvents(\n      \"plan 0 TCP connected\",\n      \"plan 0 TLS connecting...\",\n      \"plan 0 TLS connected\",\n    )\n  }\n\n  @Test\n  fun firstPlanConnectedBeforeSecondPlan() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 260.ms\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectDelayNanos = 20.ms // Connect at time = 270 ms.\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan0.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(260.ms)\n    assertEvents(\n      \"plan 0 TCP connected\",\n      \"plan 1 cancel\",\n      \"plan 0 TLS connecting...\",\n      \"plan 0 TLS connected\",\n    )\n\n    taskFaker.advanceUntil(270.ms)\n    assertEvents(\n      \"plan 1 TCP connect canceled\",\n    )\n  }\n\n  @Test\n  fun secondPlanAlreadyConnected() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 260.ms\n    val plan1 = routePlanner.addPlan()\n    plan1.connectState = TLS_CONNECTED\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan1.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 0 cancel\",\n    )\n\n    taskFaker.advanceUntil(260.ms)\n    assertEvents(\n      \"plan 0 TCP connect canceled\",\n    )\n  }\n\n  @Test\n  fun secondPlanConnectedBeforeFirstPlan() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 270.ms\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectDelayNanos = 10.ms // Connect at time = 260 ms.\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan1.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(260.ms)\n    assertEvents(\n      \"plan 1 TCP connected\",\n      \"plan 0 cancel\",\n      \"plan 1 TLS connecting...\",\n      \"plan 1 TLS connected\",\n    )\n\n    taskFaker.advanceUntil(270.ms)\n    assertEvents(\n      \"plan 0 TCP connect canceled\",\n    )\n  }\n\n  @Test\n  fun thirdPlanAlreadyConnected() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 520.ms\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectDelayNanos = 260.ms // Connect completes at 510 ms.\n    val plan2 = routePlanner.addPlan()\n    plan2.connectState = TLS_CONNECTED\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan2.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(500.ms)\n    assertEvents(\n      \"take plan 2\",\n      \"plan 0 cancel\",\n      \"plan 1 cancel\",\n    )\n\n    taskFaker.advanceUntil(510.ms)\n    assertEvents(\n      \"plan 1 TCP connect canceled\",\n    )\n\n    taskFaker.advanceUntil(520.ms)\n    assertEvents(\n      \"plan 0 TCP connect canceled\",\n    )\n  }\n\n  @Test\n  fun takeMultipleConnections() {\n    val plan0 = routePlanner.addPlan()\n    plan0.connectState = TLS_CONNECTED\n    val plan1 = routePlanner.addPlan()\n    plan1.connectState = TLS_CONNECTED\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan0.connection)\n      val result1 = finder.find()\n      assertThat(result1).isEqualTo(plan1.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"take plan 1\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun takeMultipleConnectionsReturnsRaceLoser() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 270.ms\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectDelayNanos = 10.ms // Connect at time = 260 ms.\n    val plan2 = plan0.createRetry()\n    plan2.tcpConnectDelayNanos = 20.ms // Connect at time = 280 ms.\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan1.connection)\n      val result1 = finder.find()\n      assertThat(result1).isEqualTo(plan2.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(260.ms)\n    assertEvents(\n      \"plan 1 TCP connected\",\n      \"plan 0 cancel\",\n      \"plan 1 TLS connecting...\",\n      \"plan 1 TLS connected\",\n      \"plan 2 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(270.ms)\n    assertEvents(\n      \"plan 0 TCP connect canceled\",\n    )\n\n    taskFaker.advanceUntil(280.ms)\n    assertEvents(\n      \"plan 2 TCP connected\",\n      \"plan 2 TLS connecting...\",\n      \"plan 2 TLS connected\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun firstConnectionFailsAndNoOthersExist() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectThrowable = IOException(\"boom!\")\n\n    taskRunner.newQueue().execute(\"connect\") {\n      assertFailsWith<IOException> {\n        finder.find()\n      }.also { expected ->\n        assertThat(expected).hasMessage(\"boom!\")\n      }\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n      \"plan 0 TCP connect failed\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun firstConnectionFailsToConnectAndSecondSucceeds() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectThrowable = IOException(\"boom!\")\n    val plan1 = routePlanner.addPlan()\n    plan1.connectState = TLS_CONNECTED\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan1.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n      \"plan 0 TCP connect failed\",\n      \"take plan 1\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun firstConnectionFailsToConnectAndSecondFailureIsSuppressedException() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectThrowable = IOException(\"boom 0!\")\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectThrowable = IOException(\"boom 1!\")\n\n    taskRunner.newQueue().execute(\"connect\") {\n      assertFailsWith<IOException> {\n        finder.find()\n      }.also { expected ->\n        assertThat(expected).hasMessage(\"boom 0!\")\n        assertThat(expected.suppressed.single()).hasMessage(\"boom 1!\")\n      }\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n      \"plan 0 TCP connect failed\",\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n      \"plan 1 TCP connect failed\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun firstConnectionCrashesWithUncheckedException() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectThrowable = IllegalStateException(\"boom!\")\n    routePlanner.addPlan() // This plan should not be used.\n\n    taskRunner.newQueue().execute(\"connect\") {\n      assertFailsWith<IllegalStateException> {\n        finder.find()\n      }.also { expected ->\n        assertThat(expected).hasMessage(\"boom!\")\n      }\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n      \"plan 0 TCP connect failed\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun routePlannerPlanThrowsOnOnlyPlan() {\n    val plan0 = routePlanner.addPlan()\n    plan0.planningThrowable = UnknownServiceException(\"boom!\")\n\n    taskRunner.newQueue().execute(\"connect\") {\n      assertFailsWith<UnknownServiceException> {\n        finder.find()\n      }.also { expected ->\n        assertThat(expected).hasMessage(\"boom!\")\n      }\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun recoversAfterFirstPlanCallThrows() {\n    val plan0 = routePlanner.addPlan()\n    plan0.planningThrowable = UnknownServiceException(\"boom!\")\n    val plan1 = routePlanner.addPlan()\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan1.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n      \"plan 1 TCP connected\",\n      \"plan 1 TLS connecting...\",\n      \"plan 1 TLS connected\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun retryConnectionThatLostTcpRaceAfterWinnersTlsFails() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 270.ms\n\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectDelayNanos = 10.ms // TCP connect at time = 260 ms.\n    plan1.tlsConnectThrowable = IOException(\"boom!\")\n\n    val plan2 = plan0.createRetry()\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan2.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(260.ms)\n    assertEvents(\n      \"plan 1 TCP connected\",\n      \"plan 0 cancel\",\n      \"plan 1 TLS connecting...\",\n      \"plan 1 TLS connect failed\",\n      \"plan 2 TCP connecting...\",\n      \"plan 2 TCP connected\",\n      \"plan 2 TLS connecting...\",\n      \"plan 2 TLS connected\",\n    )\n\n    taskFaker.advanceUntil(270.ms)\n    assertEvents(\n      \"plan 0 TCP connect canceled\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun losingPlanDoesNotConnectTls() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 270.ms\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectDelayNanos = 10.ms // Connect at time = 260 ms.\n    plan1.tlsConnectDelayNanos = 20.ms // Connect at time = 280 ms.\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan0.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(260.ms)\n    assertEvents(\n      \"plan 1 TCP connected\",\n      \"plan 0 cancel\",\n      \"plan 1 TLS connecting...\",\n    )\n\n    taskFaker.advanceUntil(270.ms)\n    assertEvents(\n      \"plan 0 TCP connect canceled\",\n    )\n\n    taskFaker.advanceUntil(280.ms)\n    assertEvents(\n      \"plan 1 TLS connected\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun tcpConnectFollowUpPlanUsed() {\n    val plan0 = routePlanner.addPlan()\n    val plan1 = plan0.createConnectTcpNextPlan()\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan1.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n      \"plan 0 needs follow-up\",\n      \"plan 1 TCP connecting...\",\n      \"plan 1 TCP connected\",\n      \"plan 1 TLS connecting...\",\n      \"plan 1 TLS connected\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  @Test\n  fun tlsConnectFollowUpPlanUsed() {\n    val plan0 = routePlanner.addPlan()\n    val plan1 = plan0.createConnectTlsNextPlan()\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan1.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n      \"plan 0 TCP connected\",\n      \"plan 0 TLS connecting...\",\n      \"plan 0 needs follow-up\",\n      \"plan 1 TCP connecting...\",\n      \"plan 1 TCP connected\",\n      \"plan 1 TLS connecting...\",\n      \"plan 1 TLS connected\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  /**\n   * This test performs two races:\n   *\n   *  * The first race is between plan0 and plan1, with a 250 ms head start for plan0.\n   *  * The second race is between plan2 and plan3, with a 250 ms head start for plan2.\n   *\n   * We get plan0 and plan1 from the route planner.\n   * We get plan2 as a follow-up to plan1, typically retry the same IP but different TLS.\n   * We get plan3 as a retry of plan0, which was canceled when it lost the race.\n   *\n   * This test confirms that we prefer to do the TLS follow-up (plan2) before the TCP retry (plan3).\n   * It also confirms we enforce the 250 ms delay in each race.\n   */\n  @Test\n  fun tcpConnectionsRaceAfterTlsFails() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 280.ms\n\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectDelayNanos = 10.ms // Connect at time = 260 ms.\n    plan1.tlsConnectDelayNanos = 10.ms // Connect at time = 270 ms.\n    plan1.tlsConnectThrowable = IOException(\"boom!\")\n\n    val plan2 = plan1.createConnectTlsNextPlan()\n    plan2.tcpConnectDelayNanos = 270.ms // Connect at time = 540 ms.\n\n    val plan3 = plan0.createRetry()\n    plan3.tcpConnectDelayNanos = 10.ms // Connect at time = 530 ms.\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan3.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(260.ms)\n    assertEvents(\n      \"plan 1 TCP connected\",\n      \"plan 0 cancel\",\n      \"plan 1 TLS connecting...\",\n    )\n\n    taskFaker.advanceUntil(270.ms)\n    assertEvents(\n      \"plan 1 TLS connect failed\",\n      \"plan 2 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(280.ms)\n    assertEvents(\n      \"plan 0 TCP connect canceled\",\n    )\n\n    taskFaker.advanceUntil(520.ms)\n    assertEvents(\n      \"plan 3 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(530.ms)\n    assertEvents(\n      \"plan 3 TCP connected\",\n      \"plan 2 cancel\",\n      \"plan 3 TLS connecting...\",\n      \"plan 3 TLS connected\",\n    )\n\n    taskFaker.advanceUntil(540.ms)\n    assertEvents(\n      \"plan 2 TCP connect canceled\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  /**\n   * This test puts several connections in flight that all fail at approximately the same time. It\n   * confirms the fast fallback implements these invariants:\n   *\n   *  * if there's no TCP connect in flight, start one.\n   *  * don't start a new TCP connect within 250 ms of the previous TCP connect.\n   */\n  @Test\n  fun minimumDelayEnforcedBetweenConnects() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 510.ms\n    plan0.tcpConnectThrowable = IOException(\"boom!\")\n    val plan1 = routePlanner.addPlan()\n    plan1.tcpConnectDelayNanos = 270.ms // Connect fail at time = 520 ms.\n    plan1.tcpConnectThrowable = IOException(\"boom!\")\n    val plan2 = routePlanner.addPlan()\n    plan2.tcpConnectDelayNanos = 30.ms // Connect fail at time = 530 ms.\n    plan2.tcpConnectThrowable = IOException(\"boom!\")\n    val plan3 = routePlanner.addPlan()\n    plan3.tcpConnectDelayNanos = 270.ms // Connect at time 800 ms.\n    val plan4 = routePlanner.addPlan()\n    plan4.tcpConnectDelayNanos = 10.ms // Connect at time 790 ms.\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan4.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 1 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(500.ms)\n    assertEvents(\n      \"take plan 2\",\n      \"plan 2 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(510.ms)\n    assertEvents(\n      \"plan 0 TCP connect failed\",\n    )\n\n    taskFaker.advanceUntil(520.ms)\n    assertEvents(\n      \"plan 1 TCP connect failed\",\n    )\n\n    taskFaker.advanceUntil(530.ms)\n    assertEvents(\n      \"plan 2 TCP connect failed\",\n      \"take plan 3\",\n      \"plan 3 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(780.ms)\n    assertEvents(\n      \"take plan 4\",\n      \"plan 4 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(790.ms)\n    assertEvents(\n      \"plan 4 TCP connected\",\n      \"plan 3 cancel\",\n      \"plan 4 TLS connecting...\",\n      \"plan 4 TLS connected\",\n    )\n\n    taskFaker.advanceUntil(800.ms)\n    assertEvents(\n      \"plan 3 TCP connect canceled\",\n    )\n\n    taskFaker.assertNoMoreTasks()\n  }\n\n  /**\n   * This test causes two connections to become available simultaneously, one from a TCP connect and\n   * one from the pool. We must take the pooled connection because by taking it from the pool, we've\n   * fully acquired it.\n   *\n   * This test yields threads to force the decision of plan1 to be deliberate and not lucky. In\n   * particular, we set up this sequence of events:\n   *\n   *  1. take plan 0\n   *  3. plan 0 connects\n   *  4. finish taking plan 1\n   *\n   * https://github.com/square/okhttp/issues/7152\n   */\n  @Test\n  fun reusePlanAndNewConnectRace() {\n    val plan0 = routePlanner.addPlan()\n    plan0.tcpConnectDelayNanos = 250.ms\n    plan0.yieldBeforeTcpConnectReturns = true // Yield so we get a chance to take plan1...\n    val plan1 = routePlanner.addPlan()\n    plan1.connectState = TLS_CONNECTED\n    plan1.yieldBeforePlanReturns = true // ... but let plan 0 connect before we act upon it.\n\n    taskRunner.newQueue().execute(\"connect\") {\n      val result0 = finder.find()\n      assertThat(result0).isEqualTo(plan0.connection)\n    }\n\n    taskFaker.runTasks()\n    assertEvents(\n      \"take plan 0\",\n      \"plan 0 TCP connecting...\",\n    )\n\n    taskFaker.advanceUntil(250.ms)\n    assertEvents(\n      \"take plan 1\",\n      \"plan 0 cancel\",\n      \"plan 0 TCP connect canceled\",\n    )\n  }\n\n  private fun assertEvents(vararg expected: String) {\n    val actual = generateSequence { routePlanner.events.poll() }.toList()\n    assertThat(actual).containsExactly(*expected)\n  }\n\n  private val Int.ms: Long\n    get() = this * 1_000_000L\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/InetAddressOrderTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.internal.connection\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.net.Inet4Address\nimport java.net.Inet6Address\nimport org.junit.jupiter.api.Test\n\n@Suppress(\"ktlint:standard:property-naming\")\nclass InetAddressOrderTest {\n  val ipv4_10_0_0_6 = Inet4Address.getByName(\"10.0.0.6\")\n  val ipv4_10_0_0_1 = Inet4Address.getByName(\"10.0.0.1\")\n  val ipv4_10_0_0_4 = Inet4Address.getByName(\"10.0.0.4\")\n  val ipv6_ab = Inet6Address.getByName(\"::ac\")\n  val ipv6_fc = Inet6Address.getByName(\"::fc\")\n\n  @Test fun prioritiseIpv6Example() {\n    val result =\n      reorderForHappyEyeballs(\n        listOf(\n          ipv4_10_0_0_6,\n          ipv4_10_0_0_1,\n          ipv4_10_0_0_4,\n          ipv6_ab,\n          ipv6_fc,\n        ),\n      )\n\n    assertThat(result).isEqualTo(\n      listOf(ipv6_ab, ipv4_10_0_0_6, ipv6_fc, ipv4_10_0_0_1, ipv4_10_0_0_4),\n    )\n  }\n\n  @Test fun ipv6Only() {\n    val result = reorderForHappyEyeballs(listOf(ipv6_ab, ipv6_fc))\n\n    assertThat(result).isEqualTo(\n      listOf(ipv6_ab, ipv6_fc),\n    )\n  }\n\n  @Test fun ipv4Only() {\n    val result =\n      reorderForHappyEyeballs(\n        listOf(\n          ipv4_10_0_0_6,\n          ipv4_10_0_0_1,\n          ipv4_10_0_0_4,\n        ),\n      )\n\n    assertThat(result).isEqualTo(\n      listOf(ipv4_10_0_0_6, ipv4_10_0_0_1, ipv4_10_0_0_4),\n    )\n  }\n\n  @Test fun singleIpv6() {\n    val result = reorderForHappyEyeballs(listOf(ipv6_ab))\n\n    assertThat(result).isEqualTo(\n      listOf(ipv6_ab),\n    )\n  }\n\n  @Test fun singleIpv4() {\n    val result = reorderForHappyEyeballs(listOf(ipv4_10_0_0_6))\n\n    assertThat(result).isEqualTo(\n      listOf(ipv4_10_0_0_6),\n    )\n  }\n\n  @Test fun prioritiseIpv6() {\n    val result = reorderForHappyEyeballs(listOf(ipv4_10_0_0_6, ipv6_ab))\n\n    assertThat(result).isEqualTo(\n      listOf(ipv6_ab, ipv4_10_0_0_6),\n    )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/RetryConnectionTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactlyInAnyOrder\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.security.cert.CertificateException\nimport javax.net.ssl.SSLHandshakeException\nimport javax.net.ssl.SSLSocket\nimport okhttp3.ConnectionSpec\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.TestValueFactory\nimport okhttp3.TlsVersion\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass RetryConnectionTest {\n  private val factory = TestValueFactory()\n  private val handshakeCertificates = localhost()\n  private val retryableException = SSLHandshakeException(\"Simulated handshake exception\")\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private var client = clientTestRule.newClient()\n\n  @AfterEach internal fun tearDown() {\n    factory.close()\n  }\n\n  @Test fun nonRetryableIOException() {\n    val exception = IOException(\"Non-handshake exception\")\n    assertThat(retryTlsHandshake(exception)).isFalse()\n  }\n\n  @Test fun nonRetryableSSLHandshakeException() {\n    val exception =\n      SSLHandshakeException(\"Certificate handshake exception\").apply {\n        initCause(CertificateException())\n      }\n    assertThat(retryTlsHandshake(exception)).isFalse()\n  }\n\n  @Test fun retryableSSLHandshakeException() {\n    assertThat(retryTlsHandshake(retryableException)).isTrue()\n  }\n\n  @Test fun someFallbacksSupported() {\n    val sslV3 =\n      ConnectionSpec\n        .Builder(ConnectionSpec.MODERN_TLS)\n        .tlsVersions(TlsVersion.SSL_3_0)\n        .build()\n    val routePlanner = factory.newRoutePlanner(client)\n    val route = factory.newRoute()\n    val connectionSpecs = listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, sslV3)\n    val enabledSocketTlsVersions =\n      arrayOf(\n        TlsVersion.TLS_1_2,\n        TlsVersion.TLS_1_1,\n        TlsVersion.TLS_1_0,\n      )\n    var socket = createSocketWithEnabledProtocols(*enabledSocketTlsVersions)\n\n    // MODERN_TLS is used here.\n    val attempt0 =\n      routePlanner\n        .planConnectToRoute(route)\n        .planWithCurrentOrInitialConnectionSpec(connectionSpecs, socket)\n    assertThat(attempt0.isTlsFallback).isFalse()\n    connectionSpecs[attempt0.connectionSpecIndex].apply(socket, attempt0.isTlsFallback)\n    assertEnabledProtocols(socket, TlsVersion.TLS_1_2)\n    val attempt1 = attempt0.nextConnectionSpec(connectionSpecs, socket)\n    assertThat(attempt1).isNotNull()\n    assertThat(attempt1!!.isTlsFallback).isTrue()\n    socket.close()\n\n    // COMPATIBLE_TLS is used here.\n    socket = createSocketWithEnabledProtocols(*enabledSocketTlsVersions)\n    connectionSpecs[attempt1.connectionSpecIndex].apply(socket, attempt1.isTlsFallback)\n    assertEnabledProtocols(socket, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)\n    val attempt2 = attempt1.nextConnectionSpec(connectionSpecs, socket)\n    assertThat(attempt2).isNull()\n    socket.close()\n\n    // sslV3 is not used because SSLv3 is not enabled on the socket.\n  }\n\n  private fun createSocketWithEnabledProtocols(vararg tlsVersions: TlsVersion): SSLSocket =\n    (handshakeCertificates.sslSocketFactory().createSocket() as SSLSocket).apply {\n      enabledProtocols = javaNames(*tlsVersions)\n    }\n\n  private fun assertEnabledProtocols(\n    socket: SSLSocket,\n    vararg required: TlsVersion,\n  ) {\n    assertThat(socket.enabledProtocols.toList()).containsExactlyInAnyOrder(*javaNames(*required))\n  }\n\n  private fun javaNames(vararg tlsVersions: TlsVersion) = tlsVersions.map { it.javaName }.toTypedArray()\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/RouteSelectorTest.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.connection\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isSameInstanceAs\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.SocketAddress\nimport java.net.URI\nimport java.net.UnknownHostException\nimport kotlin.test.assertFailsWith\nimport okhttp3.Address\nimport okhttp3.FakeDns\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Request\nimport okhttp3.Route\nimport okhttp3.TestValueFactory\nimport okhttp3.internal.connection.RouteSelector.Companion.socketHost\nimport okhttp3.internal.http.RecordingProxySelector\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass RouteSelectorTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private val dns = FakeDns()\n  private val proxySelector = RecordingProxySelector()\n  private val uriHost = \"hosta\"\n  private val uriPort = 1003\n  private val factory =\n    TestValueFactory().apply {\n      this.dns = this@RouteSelectorTest.dns\n      this.proxySelector = this@RouteSelectorTest.proxySelector\n      this.uriHost = this@RouteSelectorTest.uriHost\n      this.uriPort = this@RouteSelectorTest.uriPort\n    }\n\n  private lateinit var call: RealCall\n  private val routeDatabase = RouteDatabase()\n\n  @BeforeEach fun setUp() {\n    call =\n      clientTestRule.newClient().newCall(\n        Request\n          .Builder()\n          .url(\"https://$uriHost:$uriPort/\")\n          .build(),\n      ) as RealCall\n  }\n\n  @AfterEach fun tearDown() {\n    factory.close()\n  }\n\n  @Test fun singleRoute() {\n    val address = factory.newAddress()\n    val routeSelector = newRouteSelector(address)\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[uriHost] = dns.allocate(1)\n    val selection = routeSelector.next()\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(uriHost, 0), uriPort)\n    dns.assertRequests(uriHost)\n    assertThat(selection.hasNext()).isFalse()\n    assertFailsWith<NoSuchElementException> {\n      selection.next()\n    }\n    assertThat(routeSelector.hasNext()).isFalse()\n    assertFailsWith<NoSuchElementException> {\n      routeSelector.next()\n    }\n  }\n\n  @Test fun singleRouteReturnsFailedRoute() {\n    val address = factory.newAddress()\n    var routeSelector = newRouteSelector(address)\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[uriHost] = dns.allocate(1)\n    var selection = routeSelector.next()\n    val route = selection.next()\n    routeDatabase.failed(route)\n    routeSelector = newRouteSelector(address)\n    selection = routeSelector.next()\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(uriHost, 0), uriPort)\n    assertThat(selection.hasNext()).isFalse()\n    assertFailsWith<NoSuchElementException> {\n      selection.next()\n    }\n    assertThat(routeSelector.hasNext()).isFalse()\n    assertFailsWith<NoSuchElementException> {\n      routeSelector.next()\n    }\n  }\n\n  @Test fun explicitProxyTriesThatProxysAddressesOnly() {\n    val address =\n      factory.newAddress(\n        proxy = proxyA,\n      )\n    val routeSelector = newRouteSelector(address)\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[PROXY_A_HOST] = dns.allocate(2)\n    val selection = routeSelector.next()\n    assertRoute(selection.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 0), PROXY_A_PORT)\n    assertRoute(selection.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 1), PROXY_A_PORT)\n    assertThat(selection.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n    dns.assertRequests(PROXY_A_HOST)\n    proxySelector.assertRequests() // No proxy selector requests!\n  }\n\n  @Test fun explicitDirectProxy() {\n    val address =\n      factory.newAddress(\n        proxy = Proxy.NO_PROXY,\n      )\n    val routeSelector = newRouteSelector(address)\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[uriHost] = dns.allocate(2)\n    val selection = routeSelector.next()\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(uriHost, 0), uriPort)\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(uriHost, 1), uriPort)\n    assertThat(selection.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n    dns.assertRequests(uriHost)\n    proxySelector.assertRequests() // No proxy selector requests!\n  }\n\n  /**\n   * Don't call through to the proxy selector if we don't have a host name.\n   * https://github.com/square/okhttp/issues/5770\n   */\n  @Test fun proxySelectorNotCalledForNullHost() {\n    // The string '>' is okay in a hostname in HttpUrl, which does very light hostname validation.\n    // It is not okay in URI, and so it's stripped and we get a URI with a null host.\n    val bogusHostname = \">\"\n    val address =\n      factory.newAddress(\n        uriHost = bogusHostname,\n        uriPort = uriPort,\n      )\n    val routeSelector = newRouteSelector(address)\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[bogusHostname] = dns.allocate(1)\n    val selection = routeSelector.next()\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(bogusHostname, 0), uriPort)\n    assertThat(selection.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n    dns.assertRequests(bogusHostname)\n    proxySelector.assertRequests() // No proxy selector requests!\n  }\n\n  @Test fun proxySelectorReturnsNull() {\n    val nullProxySelector: ProxySelector =\n      object : ProxySelector() {\n        override fun select(uri: URI): List<Proxy>? {\n          assertThat(uri.host).isEqualTo(uriHost)\n          return null\n        }\n\n        override fun connectFailed(\n          uri: URI,\n          socketAddress: SocketAddress,\n          e: IOException,\n        ): Unit = throw AssertionError()\n      }\n\n    val address =\n      factory.newAddress(\n        proxySelector = nullProxySelector,\n      )\n    val routeSelector = newRouteSelector(address)\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[uriHost] = dns.allocate(1)\n    val selection = routeSelector.next()\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(uriHost, 0), uriPort)\n    dns.assertRequests(uriHost)\n    assertThat(selection.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n  }\n\n  @Test fun proxySelectorReturnsNoProxies() {\n    val address = factory.newAddress()\n    val routeSelector = newRouteSelector(address)\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[uriHost] = dns.allocate(2)\n    val selection = routeSelector.next()\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(uriHost, 0), uriPort)\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(uriHost, 1), uriPort)\n    assertThat(selection.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n    dns.assertRequests(uriHost)\n    proxySelector.assertRequests(address.url.toUri())\n  }\n\n  @Test fun proxySelectorReturnsMultipleProxies() {\n    val address = factory.newAddress()\n    proxySelector.proxies.add(proxyA)\n    proxySelector.proxies.add(proxyB)\n    val routeSelector = newRouteSelector(address)\n    proxySelector.assertRequests(address.url.toUri())\n\n    // First try the IP addresses of the first proxy, in sequence.\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[PROXY_A_HOST] = dns.allocate(2)\n    val selection1 = routeSelector.next()\n    assertRoute(selection1.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 0), PROXY_A_PORT)\n    assertRoute(selection1.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 1), PROXY_A_PORT)\n    dns.assertRequests(PROXY_A_HOST)\n    assertThat(selection1.hasNext()).isFalse()\n\n    // Next try the IP address of the second proxy.\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[PROXY_B_HOST] = dns.allocate(1)\n    val selection2 = routeSelector.next()\n    assertRoute(selection2.next(), address, proxyB, dns.lookup(PROXY_B_HOST, 0), PROXY_B_PORT)\n    dns.assertRequests(PROXY_B_HOST)\n    assertThat(selection2.hasNext()).isFalse()\n\n    // No more proxies to try.\n    assertThat(routeSelector.hasNext()).isFalse()\n  }\n\n  @Test fun proxySelectorDirectConnectionsAreSkipped() {\n    val address = factory.newAddress()\n    proxySelector.proxies.add(Proxy.NO_PROXY)\n    val routeSelector = newRouteSelector(address)\n    proxySelector.assertRequests(address.url.toUri())\n\n    // Only the origin server will be attempted.\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[uriHost] = dns.allocate(1)\n    val selection = routeSelector.next()\n    assertRoute(selection.next(), address, Proxy.NO_PROXY, dns.lookup(uriHost, 0), uriPort)\n    dns.assertRequests(uriHost)\n    assertThat(selection.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n  }\n\n  @Test fun proxyDnsFailureContinuesToNextProxy() {\n    val address = factory.newAddress()\n    proxySelector.proxies.add(proxyA)\n    proxySelector.proxies.add(proxyB)\n    proxySelector.proxies.add(proxyA)\n    val routeSelector = newRouteSelector(address)\n    proxySelector.assertRequests(address.url.toUri())\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[PROXY_A_HOST] = dns.allocate(1)\n    val selection1 = routeSelector.next()\n    assertRoute(selection1.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 0), PROXY_A_PORT)\n    dns.assertRequests(PROXY_A_HOST)\n    assertThat(selection1.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns.clear(PROXY_B_HOST)\n    assertFailsWith<UnknownHostException> {\n      routeSelector.next()\n    }\n    dns.assertRequests(PROXY_B_HOST)\n    assertThat(routeSelector.hasNext()).isTrue()\n    dns[PROXY_A_HOST] = dns.allocate(1)\n    val selection2 = routeSelector.next()\n    assertRoute(selection2.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 0), PROXY_A_PORT)\n    dns.assertRequests(PROXY_A_HOST)\n    assertThat(selection2.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n  }\n\n  @Test fun multipleProxiesMultipleInetAddressesMultipleConfigurations() {\n    val address = factory.newHttpsAddress()\n    proxySelector.proxies.add(proxyA)\n    proxySelector.proxies.add(proxyB)\n    val routeSelector = newRouteSelector(address)\n\n    // Proxy A\n    dns[PROXY_A_HOST] = dns.allocate(2)\n    val selection1 = routeSelector.next()\n    assertRoute(selection1.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 0), PROXY_A_PORT)\n    dns.assertRequests(PROXY_A_HOST)\n    assertRoute(selection1.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 1), PROXY_A_PORT)\n    assertThat(selection1.hasNext()).isFalse()\n\n    // Proxy B\n    dns[PROXY_B_HOST] = dns.allocate(2)\n    val selection2 = routeSelector.next()\n    assertRoute(selection2.next(), address, proxyB, dns.lookup(PROXY_B_HOST, 0), PROXY_B_PORT)\n    dns.assertRequests(PROXY_B_HOST)\n    assertRoute(selection2.next(), address, proxyB, dns.lookup(PROXY_B_HOST, 1), PROXY_B_PORT)\n    assertThat(selection2.hasNext()).isFalse()\n\n    // No more proxies to attempt.\n    assertThat(routeSelector.hasNext()).isFalse()\n  }\n\n  @Test fun failedRouteWithSingleProxy() {\n    val address = factory.newHttpsAddress()\n    var routeSelector = newRouteSelector(address)\n    val numberOfAddresses = 2\n    dns[uriHost] = dns.allocate(numberOfAddresses)\n\n    // Extract the regular sequence of routes from selector.\n    val selection1 = routeSelector.next()\n    val regularRoutes = selection1.routes\n\n    // Check that we do indeed have more than one route.\n    assertThat(regularRoutes.size).isEqualTo(numberOfAddresses)\n    // Add first regular route as failed.\n    routeDatabase.failed(regularRoutes[0])\n    // Reset selector\n    routeSelector = newRouteSelector(address)\n\n    // The first selection prioritizes the non-failed routes.\n    val selection2 = routeSelector.next()\n    assertThat(selection2.next()).isEqualTo(regularRoutes[1])\n    assertThat(selection2.hasNext()).isFalse()\n\n    // The second selection will contain all failed routes.\n    val selection3 = routeSelector.next()\n    assertThat(selection3.next()).isEqualTo(regularRoutes[0])\n    assertThat(selection3.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n  }\n\n  @Test fun failedRouteWithMultipleProxies() {\n    val address = factory.newHttpsAddress()\n    proxySelector.proxies.add(proxyA)\n    proxySelector.proxies.add(proxyB)\n    var routeSelector = newRouteSelector(address)\n    dns[PROXY_A_HOST] = dns.allocate(1)\n    dns[PROXY_B_HOST] = dns.allocate(1)\n\n    // Mark the ProxyA route as failed.\n    val selection = routeSelector.next()\n    dns.assertRequests(PROXY_A_HOST)\n    val route = selection.next()\n    assertRoute(route, address, proxyA, dns.lookup(PROXY_A_HOST, 0), PROXY_A_PORT)\n    routeDatabase.failed(route)\n    routeSelector = newRouteSelector(address)\n\n    // Confirm we enumerate both proxies, giving preference to the route from ProxyB.\n    val selection2 = routeSelector.next()\n    dns.assertRequests(PROXY_A_HOST, PROXY_B_HOST)\n    assertRoute(selection2.next(), address, proxyB, dns.lookup(PROXY_B_HOST, 0), PROXY_B_PORT)\n    assertThat(selection2.hasNext()).isFalse()\n\n    // Confirm the last selection contains the postponed route from ProxyA.\n    val selection3 = routeSelector.next()\n    dns.assertRequests()\n    assertRoute(selection3.next(), address, proxyA, dns.lookup(PROXY_A_HOST, 0), PROXY_A_PORT)\n    assertThat(selection3.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n  }\n\n  @Test fun queryForAllSelectedRoutes() {\n    val address = factory.newAddress()\n    val routeSelector = newRouteSelector(address)\n    dns[uriHost] = dns.allocate(2)\n    val selection = routeSelector.next()\n    dns.assertRequests(uriHost)\n    val routes = selection.routes\n    assertRoute(routes[0], address, Proxy.NO_PROXY, dns.lookup(uriHost, 0), uriPort)\n    assertRoute(routes[1], address, Proxy.NO_PROXY, dns.lookup(uriHost, 1), uriPort)\n    assertThat(selection.next()).isSameInstanceAs(routes[0])\n    assertThat(selection.next()).isSameInstanceAs(routes[1])\n    assertThat(selection.hasNext()).isFalse()\n    assertThat(routeSelector.hasNext()).isFalse()\n  }\n\n  @Test fun addressesNotSortedWhenFastFallbackIsOff() {\n    val address =\n      factory.newAddress(\n        proxy = Proxy.NO_PROXY,\n      )\n    val routeSelector =\n      newRouteSelector(\n        address = address,\n        fastFallback = false,\n      )\n    assertThat(routeSelector.hasNext()).isTrue()\n    val (ipv4_1, ipv4_2) = dns.allocate(2)\n    val (ipv6_1, ipv6_2) = dns.allocateIpv6(2)\n    dns[uriHost] = listOf(ipv4_1, ipv4_2, ipv6_1, ipv6_2)\n\n    val selection = routeSelector.next()\n    assertThat(selection.routes.map { it.socketAddress.address }).containsExactly(\n      ipv4_1,\n      ipv4_2,\n      ipv6_1,\n      ipv6_2,\n    )\n  }\n\n  @Test fun addressesSortedWhenFastFallbackIsOn() {\n    val address =\n      factory.newAddress(\n        proxy = Proxy.NO_PROXY,\n      )\n    val routeSelector =\n      newRouteSelector(\n        address = address,\n        fastFallback = true,\n      )\n    assertThat(routeSelector.hasNext()).isTrue()\n    val (ipv4_1, ipv4_2) = dns.allocate(2)\n    val (ipv6_1, ipv6_2) = dns.allocateIpv6(2)\n    dns[uriHost] = listOf(ipv4_1, ipv4_2, ipv6_1, ipv6_2)\n\n    val selection = routeSelector.next()\n    assertThat(selection.routes.map { it.socketAddress.address }).containsExactly(\n      ipv6_1,\n      ipv4_1,\n      ipv6_2,\n      ipv4_2,\n    )\n  }\n\n  @Test fun getHostString() {\n    // Name proxy specification.\n    var socketAddress = InetSocketAddress.createUnresolved(\"host\", 1234)\n    assertThat(socketAddress.socketHost).isEqualTo(\"host\")\n    socketAddress = InetSocketAddress.createUnresolved(\"127.0.0.1\", 1234)\n    assertThat(socketAddress.socketHost).isEqualTo(\"127.0.0.1\")\n\n    // InetAddress proxy specification.\n    socketAddress = InetSocketAddress(InetAddress.getByName(\"localhost\"), 1234)\n    assertThat(socketAddress.socketHost).isEqualTo(\"127.0.0.1\")\n    socketAddress = InetSocketAddress(InetAddress.getByAddress(byteArrayOf(127, 0, 0, 1)), 1234)\n    assertThat(socketAddress.socketHost).isEqualTo(\"127.0.0.1\")\n    socketAddress =\n      InetSocketAddress(\n        InetAddress.getByAddress(\"foobar\", byteArrayOf(127, 0, 0, 1)),\n        1234,\n      )\n    assertThat(socketAddress.socketHost).isEqualTo(\"127.0.0.1\")\n  }\n\n  @Test fun routeToString() {\n    val ipv4Address =\n      InetAddress.getByAddress(\n        byteArrayOf(1, 2, 3, 4),\n      )\n    assertThat(\n      Route(\n        factory.newAddress(uriHost = \"1.2.3.4\", uriPort = 1003),\n        Proxy.NO_PROXY,\n        InetSocketAddress(ipv4Address, 1003),\n      ).toString(),\n    ).isEqualTo(\"1.2.3.4:1003\")\n    assertThat(\n      Route(\n        factory.newAddress(uriHost = \"example.com\", uriPort = 1003),\n        Proxy.NO_PROXY,\n        InetSocketAddress(ipv4Address, 1003),\n      ).toString(),\n    ).isEqualTo(\"example.com at 1.2.3.4:1003\")\n    assertThat(\n      Route(\n        factory.newAddress(uriHost = \"example.com\", uriPort = 1003),\n        Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(\"proxy.example.com\", 1003)),\n        InetSocketAddress(ipv4Address, 1003),\n      ).toString(),\n    ).isEqualTo(\"example.com via proxy 1.2.3.4:1003\")\n    assertThat(\n      Route(\n        factory.newAddress(uriHost = \"example.com\", uriPort = 1003),\n        Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(\"proxy.example.com\", 1003)),\n        InetSocketAddress(ipv4Address, 5678),\n      ).toString(),\n    ).isEqualTo(\"example.com:1003 via proxy 1.2.3.4:5678\")\n  }\n\n  @Test fun routeToStringIpv6() {\n    val ipv6Address =\n      InetAddress.getByAddress(\n        byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),\n      )\n    assertThat(\n      Route(\n        factory.newAddress(uriHost = \"::1\", uriPort = 1003),\n        Proxy.NO_PROXY,\n        InetSocketAddress(ipv6Address, uriPort),\n      ).toString(),\n    ).isEqualTo(\"[::1]:1003\")\n    assertThat(\n      Route(\n        factory.newAddress(uriHost = \"example.com\", uriPort = 1003),\n        Proxy.NO_PROXY,\n        InetSocketAddress(ipv6Address, uriPort),\n      ).toString(),\n    ).isEqualTo(\"example.com at [::1]:1003\")\n    assertThat(\n      Route(\n        factory.newAddress(uriHost = \"example.com\", uriPort = 1003),\n        Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(\"proxy.example.com\", 1003)),\n        InetSocketAddress(ipv6Address, 5678),\n      ).toString(),\n    ).isEqualTo(\"example.com:1003 via proxy [::1]:5678\")\n    assertThat(\n      Route(\n        factory.newAddress(uriHost = \"::2\", uriPort = 1003),\n        Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(\"proxy.example.com\", 1003)),\n        InetSocketAddress(ipv6Address, 5678),\n      ).toString(),\n    ).isEqualTo(\"[::2]:1003 via proxy [::1]:5678\")\n  }\n\n  private fun assertRoute(\n    route: Route,\n    address: Address,\n    proxy: Proxy,\n    socketAddress: InetAddress,\n    socketPort: Int,\n  ) {\n    assertThat(route.address).isEqualTo(address)\n    assertThat(route.proxy).isEqualTo(proxy)\n    assertThat(route.socketAddress.address).isEqualTo(socketAddress)\n    assertThat(route.socketAddress.port).isEqualTo(socketPort)\n  }\n\n  private fun newRouteSelector(\n    address: Address,\n    routeDatabase: RouteDatabase = this.routeDatabase,\n    fastFallback: Boolean = false,\n    call: RealCall = this.call,\n  ): RouteSelector =\n    RouteSelector(\n      address = address,\n      routeDatabase = routeDatabase,\n      fastFallback = fastFallback,\n      call = call,\n    )\n\n  companion object {\n    private const val PROXY_A_PORT = 1001\n    private const val PROXY_A_HOST = \"proxya\"\n    private val proxyA =\n      Proxy(\n        Proxy.Type.HTTP,\n        InetSocketAddress.createUnresolved(PROXY_A_HOST, PROXY_A_PORT),\n      )\n\n    private const val PROXY_B_PORT = 1002\n    private const val PROXY_B_HOST = \"proxyb\"\n    private val proxyB =\n      Proxy(\n        Proxy.Type.HTTP,\n        InetSocketAddress.createUnresolved(PROXY_B_HOST, PROXY_B_PORT),\n      )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http/CancelTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport app.cash.burst.Burst\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.doesNotContain\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.startsWith\nimport assertk.fail\nimport java.io.IOException\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit.MILLISECONDS\nimport javax.net.ServerSocketFactory\nimport javax.net.SocketFactory\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport okhttp3.Call\nimport okhttp3.CallEvent\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.Canceled\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.RequestFailed\nimport okhttp3.CallEvent.ResponseFailed\nimport okhttp3.DelegatingServerSocketFactory\nimport okhttp3.DelegatingSocketFactory\nimport okhttp3.EventRecorder\nimport okhttp3.MediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Protocol.HTTP_1_1\nimport okhttp3.Request\nimport okhttp3.RequestBody\nimport okhttp3.SimpleProvider\nimport okhttp3.internal.http.CancelTest.CancelMode.CANCEL\nimport okhttp3.internal.http.CancelTest.CancelMode.INTERRUPT\nimport okhttp3.internal.http.CancelTest.ConnectionType.H2\nimport okhttp3.internal.http.CancelTest.ConnectionType.HTTP\nimport okhttp3.internal.http.CancelTest.ConnectionType.HTTPS\nimport okhttp3.testing.PlatformRule\nimport okio.Buffer\nimport okio.BufferedSink\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Timeout(30)\n@Tag(\"Slow\")\n@Burst\nclass CancelTest(\n  private val cancelMode: CancelMode = INTERRUPT,\n  private val connectionType: ConnectionType = H2,\n) {\n  @JvmField @RegisterExtension\n  val platform = PlatformRule()\n\n  private var threadToCancel: Thread? = null\n\n  enum class CancelMode {\n    CANCEL,\n    INTERRUPT,\n  }\n\n  enum class ConnectionType {\n    H2,\n    HTTPS,\n    HTTP,\n  }\n\n  @JvmField @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  val handshakeCertificates = platform.localhostHandshakeCertificates()\n\n  private lateinit var server: MockWebServer\n  private lateinit var client: OkHttpClient\n\n  val eventRecorder = EventRecorder()\n\n  @BeforeEach\n  fun setUp() {\n    if (connectionType == H2) {\n      platform.assumeHttp2Support()\n    }\n\n    // Sockets on some platforms can have large buffers that mean writes do not block when\n    // required. These socket factories explicitly set the buffer sizes on sockets created.\n    server = MockWebServer()\n    server.serverSocketFactory =\n      object : DelegatingServerSocketFactory(ServerSocketFactory.getDefault()) {\n        @Throws(IOException::class)\n        override fun configureServerSocket(serverSocket: ServerSocket): ServerSocket {\n          serverSocket.receiveBufferSize = SOCKET_BUFFER_SIZE\n          return serverSocket\n        }\n      }\n    if (connectionType != HTTP) {\n      server.useHttps(handshakeCertificates.sslSocketFactory())\n    }\n    server.start()\n\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .socketFactory(\n          object : DelegatingSocketFactory(SocketFactory.getDefault()) {\n            @Throws(IOException::class)\n            override fun configureSocket(socket: Socket): Socket {\n              socket.sendBufferSize = SOCKET_BUFFER_SIZE\n              socket.receiveBufferSize = SOCKET_BUFFER_SIZE\n              return socket\n            }\n          },\n        ).sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).eventListener(eventRecorder.eventListener)\n        .apply {\n          if (connectionType == HTTPS) {\n            protocols(listOf(HTTP_1_1))\n          }\n        }.build()\n    threadToCancel = Thread.currentThread()\n  }\n\n  @Test\n  fun cancelWritingRequestBody() {\n    server.enqueue(MockResponse())\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/\"),\n          body =\n            object : RequestBody() {\n              override fun contentType(): MediaType? = null\n\n              @Throws(\n                IOException::class,\n              )\n              override fun writeTo(sink: BufferedSink) {\n                for (i in 0..9) {\n                  sink.writeByte(0)\n                  sink.flush()\n                  sleep(100)\n                }\n                fail(\"Expected connection to be closed\")\n              }\n            },\n        ),\n      )\n    cancelLater(call, 500)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertEquals(cancelMode == INTERRUPT, Thread.interrupted())\n    }\n  }\n\n  @Test\n  fun cancelReadingResponseBody() {\n    val responseBodySize = 8 * 1024 * 1024 // 8 MiB.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          Buffer()\n            .write(ByteArray(responseBodySize)),\n        ).throttleBody(64 * 1024, 125, MILLISECONDS) // 500 Kbps\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    cancelLater(call, 500)\n    val responseBody = response.body.byteStream()\n    val buffer = ByteArray(1024)\n    assertFailsWith<IOException> {\n      while (responseBody.read(buffer) != -1) {\n      }\n    }.also { expected ->\n      assertEquals(cancelMode == INTERRUPT, Thread.interrupted())\n    }\n    responseBody.close()\n    assertEquals(if (connectionType == H2) 1 else 0, client.connectionPool.connectionCount())\n  }\n\n  @Test\n  fun cancelAndFollowup() {\n    val responseBodySize = 8 * 1024 * 1024 // 8 MiB.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          Buffer()\n            .write(ByteArray(responseBodySize)),\n        ).throttleBody(64 * 1024, 125, MILLISECONDS) // 500 Kbps\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \".\"))\n\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    val response = call.execute()\n    val cancelLatch = cancelLater(call, 500)\n    val responseBody = response.body.byteStream()\n    val buffer = ByteArray(1024)\n    assertFailsWith<IOException> {\n      while (responseBody.read(buffer) != -1) {\n      }\n    }.also { expected ->\n      assertEquals(cancelMode == INTERRUPT, Thread.interrupted())\n    }\n    responseBody.close()\n    assertEquals(if (connectionType == H2) 1 else 0, client.connectionPool.connectionCount())\n\n    cancelLatch.await()\n\n    val events = eventRecorder.eventSequence.filter { isConnectionEvent(it) }.map { it.name }\n    eventRecorder.clearAllEvents()\n\n    assertThat(events).startsWith(\"CallStart\", \"ConnectStart\", \"ConnectEnd\", \"ConnectionAcquired\")\n    if (cancelMode == CANCEL) {\n      assertThat(events).contains(\"Canceled\")\n    } else {\n      assertThat(events).doesNotContain(\"Canceled\")\n    }\n    assertThat(events).contains(\"ResponseFailed\")\n    assertThat(events).contains(\"ConnectionReleased\")\n\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    call2.execute().use {\n      assertEquals(\".\", it.body.string())\n    }\n\n    val events2 = eventRecorder.eventSequence.filter { isConnectionEvent(it) }.map { it.name }\n    val expectedEvents2 =\n      mutableListOf<String>().apply {\n        add(\"CallStart\")\n        if (connectionType != H2) {\n          addAll(listOf(\"ConnectStart\", \"ConnectEnd\"))\n        }\n        addAll(listOf(\"ConnectionAcquired\", \"ConnectionReleased\", \"CallEnd\"))\n      }\n\n    assertThat(events2).isEqualTo(expectedEvents2)\n  }\n\n  private fun isConnectionEvent(it: CallEvent?) =\n    it is CallStart ||\n      it is CallEnd ||\n      it is ConnectStart ||\n      it is ConnectEnd ||\n      it is ConnectionAcquired ||\n      it is ConnectionReleased ||\n      it is Canceled ||\n      it is RequestFailed ||\n      it is ResponseFailed\n\n  private fun sleep(delayMillis: Int) {\n    try {\n      Thread.sleep(delayMillis.toLong())\n    } catch (e: InterruptedException) {\n      Thread.currentThread().interrupt()\n    }\n  }\n\n  private fun cancelLater(\n    call: Call,\n    delayMillis: Int,\n  ): CountDownLatch {\n    val latch = CountDownLatch(1)\n    Thread {\n      sleep(delayMillis)\n      if (cancelMode == CANCEL) {\n        call.cancel()\n      } else {\n        threadToCancel!!.interrupt()\n      }\n      latch.countDown()\n    }.apply { start() }\n    return latch\n  }\n\n  companion object {\n    // The size of the socket buffers in bytes.\n    private const val SOCKET_BUFFER_SIZE = 256 * 1024\n  }\n}\n\nclass CancelModelParamProvider : SimpleProvider() {\n  override fun arguments() =\n    CancelTest.CancelMode.values().flatMap { c ->\n      CancelTest.ConnectionType.values().map { x ->\n        Pair(\n          c,\n          x,\n        )\n      }\n    }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http/ExternalHttp2Example.kt",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Request\n\nobject ExternalHttp2Example {\n  @JvmStatic\n  fun main(args: Array<String>) {\n    val client =\n      OkHttpClient\n        .Builder()\n        .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))\n        .build()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(\"https://www.google.ca/\")\n          .build(),\n      )\n    val response = call.execute()\n    try {\n      println(response.code)\n      println(\"PROTOCOL ${response.protocol}\")\n      var line: String?\n      while (response.body\n          .source()\n          .readUtf8Line()\n          .also { line = it } != null\n      ) {\n        println(line)\n      }\n    } finally {\n      response.body.close()\n    }\n    client.connectionPool.evictAll()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http/HttpDateTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport java.util.Date\nimport java.util.TimeZone\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\nclass HttpDateTest {\n  private lateinit var originalDefault: TimeZone\n\n  @BeforeEach\n  fun setUp() {\n    originalDefault = TimeZone.getDefault()\n    // The default timezone should affect none of these tests: HTTP specified GMT, so we set it to\n    // something else.\n    TimeZone.setDefault(TimeZone.getTimeZone(\"America/Los_Angeles\"))\n  }\n\n  @AfterEach\n  @Throws(Exception::class)\n  fun tearDown() {\n    TimeZone.setDefault(originalDefault)\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun parseStandardFormats() {\n    // RFC 822, updated by RFC 1123 with GMT.\n    assertThat(\"Thu, 01 Jan 1970 00:00:00 GMT\".toHttpDateOrNull()!!.time).isEqualTo(0L)\n    assertThat(\"Fri, 06 Jun 2014 12:30:30 GMT\".toHttpDateOrNull()!!.time).isEqualTo(1402057830000L)\n\n    // RFC 850, obsoleted by RFC 1036 with GMT.\n    assertThat(\"Thursday, 01-Jan-70 00:00:00 GMT\".toHttpDateOrNull()!!.time).isEqualTo(0L)\n    assertThat(\"Friday, 06-Jun-14 12:30:30 GMT\".toHttpDateOrNull()!!.time).isEqualTo(1402057830000L)\n\n    // ANSI C's asctime(): should use GMT, not platform default.\n    assertThat(\"Thu Jan 1 00:00:00 1970\".toHttpDateOrNull()!!.time).isEqualTo(0L)\n    assertThat(\"Fri Jun 6 12:30:30 2014\".toHttpDateOrNull()!!.time).isEqualTo(1402057830000L)\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun format() {\n    assertThat(Date(0L).toHttpDateString()).isEqualTo(\"Thu, 01 Jan 1970 00:00:00 GMT\")\n    assertThat(Date(1402057830000L).toHttpDateString()).isEqualTo(\"Fri, 06 Jun 2014 12:30:30 GMT\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun parseNonStandardStrings() {\n    // RFC 822, updated by RFC 1123 with any TZ\n    assertThat(\"Thu, 01 Jan 1970 00:00:00 GMT-01:00\".toHttpDateOrNull()!!.time).isEqualTo(3600000L)\n    assertThat(\"Thu, 01 Jan 1970 00:00:00 PST\".toHttpDateOrNull()!!.time).isEqualTo(28800000L)\n    // Ignore trailing junk\n    assertThat(\"Thu, 01 Jan 1970 00:00:00 GMT JUNK\".toHttpDateOrNull()!!.time).isEqualTo(0L)\n    // Missing timezones treated as bad.\n    assertThat(\"Thu, 01 Jan 1970 00:00:00\".toHttpDateOrNull()).isNull()\n    // Missing seconds treated as bad.\n    assertThat(\"Thu, 01 Jan 1970 00:00 GMT\".toHttpDateOrNull()).isNull()\n    // Extra spaces treated as bad.\n    assertThat(\"Thu,  01 Jan 1970 00:00 GMT\".toHttpDateOrNull()).isNull()\n    // Missing leading zero treated as bad.\n    assertThat(\"Thu, 1 Jan 1970 00:00 GMT\".toHttpDateOrNull()).isNull()\n\n    // RFC 850, obsoleted by RFC 1036 with any TZ.\n    assertThat(\"Thursday, 01-Jan-1970 00:00:00 GMT-01:00\".toHttpDateOrNull()!!.time)\n      .isEqualTo(3600000L)\n    assertThat(\"Thursday, 01-Jan-1970 00:00:00 PST\".toHttpDateOrNull()!!.time)\n      .isEqualTo(28800000L)\n    // Ignore trailing junk\n    assertThat(\"Thursday, 01-Jan-1970 00:00:00 PST JUNK\".toHttpDateOrNull()!!.time)\n      .isEqualTo(28800000L)\n\n    // ANSI C's asctime() format\n    // This format ignores the timezone entirely even if it is present and uses GMT.\n    assertThat(\"Fri Jun 6 12:30:30 2014 PST\".toHttpDateOrNull()!!.time).isEqualTo(1402057830000L)\n    // Ignore trailing junk.\n    assertThat(\"Fri Jun 6 12:30:30 2014 JUNK\".toHttpDateOrNull()!!.time).isEqualTo(1402057830000L)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http/HttpUpgradesTest.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.FollowUpDecision\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.RequestBodyEnd\nimport okhttp3.CallEvent.RequestBodyStart\nimport okhttp3.CallEvent.RequestHeadersEnd\nimport okhttp3.CallEvent.RequestHeadersStart\nimport okhttp3.CallEvent.ResponseBodyEnd\nimport okhttp3.CallEvent.ResponseBodyStart\nimport okhttp3.CallEvent.ResponseHeadersEnd\nimport okhttp3.CallEvent.ResponseHeadersStart\nimport okhttp3.EventRecorder\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Protocol\nimport okhttp3.RecordingHostnameVerifier\nimport okhttp3.Request\nimport okhttp3.RequestBody\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.internal.duplex.MockSocketHandler\nimport okhttp3.testing.PlatformRule\nimport okio.ProtocolException\nimport okio.buffer\nimport okio.use\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass HttpUpgradesTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private var eventRecorder = EventRecorder()\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n      .build()\n\n  fun executeAndCheckUpgrade(request: Request) {\n    val socketHandler =\n      MockSocketHandler()\n        .apply {\n          receiveRequest(\"client says hello\\n\")\n          sendResponse(\"server says hello\\n\")\n          receiveRequest(\"client says goodbye\\n\")\n          sendResponse(\"server says goodbye\\n\")\n          exhaustResponse()\n          exhaustRequest()\n        }\n    server.enqueue(socketHandler.upgradeResponse())\n\n    client\n      .newCall(request)\n      .execute()\n      .use { response ->\n        assertThat(response.code).isEqualTo(HTTP_SWITCHING_PROTOCOLS)\n        val socket = response.socket!!\n        socket.sink.buffer().use { sink ->\n          socket.source.buffer().use { source ->\n            sink.writeUtf8(\"client says hello\\n\")\n            sink.flush()\n\n            assertThat(source.readUtf8Line()).isEqualTo(\"server says hello\")\n\n            sink.writeUtf8(\"client says goodbye\\n\")\n            sink.flush()\n\n            assertThat(source.readUtf8Line()).isEqualTo(\"server says goodbye\")\n\n            assertThat(source.exhausted()).isTrue()\n          }\n        }\n        socketHandler.awaitSuccess()\n      }\n  }\n\n  @Test\n  fun upgrade() {\n    executeAndCheckUpgrade(upgradeRequest())\n  }\n\n  @Test\n  fun upgradeWithEmptyRequestBody() {\n    executeAndCheckUpgrade(upgradeRequest().newBuilder().post(RequestBody.EMPTY).build())\n  }\n\n  @Test\n  fun upgradeWithNonEmptyRequestBody() {\n    executeAndCheckUpgrade(\n      upgradeRequest()\n        .newBuilder()\n        .post(\"Hello\".toRequestBody())\n        .build(),\n    )\n  }\n\n  @Test\n  fun upgradeHttps() {\n    // org.bouncycastle.tls.TlsNoCloseNotifyException: No close_notify alert received before connection closed\n    platform.assumeNotBouncyCastle()\n\n    enableTls(Protocol.HTTP_1_1)\n    upgrade()\n  }\n\n  @Test\n  fun upgradeRefusedByServer() {\n    server.enqueue(MockResponse(body = \"normal request\"))\n    val requestWithUpgrade =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Connection\", \"upgrade\")\n        .build()\n    client.newCall(requestWithUpgrade).execute().use { response ->\n      assertThat(response.code).isEqualTo(200)\n      assertThat(response.socket).isNull()\n      assertThat(response.body.string()).isEqualTo(\"normal request\")\n    }\n    // Confirm there's no RequestBodyStart/RequestBodyEnd on failed upgrades.\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun upgradeForbiddenOnHttp2() {\n    enableTls(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    val socketHandler = MockSocketHandler()\n    server.enqueue(socketHandler.upgradeResponse())\n    val requestWithUpgrade =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Connection\", \"upgrade\")\n        .build()\n    assertFailsWith<ProtocolException> {\n      client.newCall(requestWithUpgrade).execute()\n    }\n  }\n\n  @Test\n  fun upgradesOnReusedConnection() {\n    server.enqueue(MockResponse(body = \"normal request\"))\n    client.newCall(Request(server.url(\"/\"))).execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"normal request\")\n    }\n\n    upgrade()\n\n    assertThat(server.takeRequest().connectionIndex).isEqualTo(0)\n    assertThat(server.takeRequest().connectionIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun cannotReuseConnectionAfterUpgrade() {\n    upgrade()\n\n    server.enqueue(MockResponse(body = \"normal request\"))\n    client.newCall(Request(server.url(\"/\"))).execute().use { response ->\n      assertThat(response.body.string()).isEqualTo(\"normal request\")\n    }\n\n    assertThat(server.takeRequest().connectionIndex).isEqualTo(0)\n    assertThat(server.takeRequest().connectionIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun upgradeEventsWithoutRequestBody() {\n    upgrade()\n\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      RequestBodyStart::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      RequestBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun upgradeEventsWithEmptyRequestBody() {\n    upgradeWithEmptyRequestBody()\n\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      RequestBodyStart::class,\n      RequestBodyEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      RequestBodyStart::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      RequestBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun upgradeEventsWithNonEmptyRequestBody() {\n    upgradeWithNonEmptyRequestBody()\n\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      RequestBodyStart::class,\n      RequestBodyEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      RequestBodyStart::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      RequestBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  private fun enableTls(vararg protocols: Protocol) {\n    client =\n      client\n        .newBuilder()\n        .protocols(protocols.toList())\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.protocols = protocols.toList()\n  }\n\n  private fun upgradeRequest() =\n    Request(\n      url = server.url(\"/\"),\n      headers =\n        headersOf(\n          \"Connection\",\n          \"upgrade\",\n        ),\n    )\n\n  private fun MockSocketHandler.upgradeResponse() =\n    MockResponse\n      .Builder()\n      .code(HTTP_SWITCHING_PROTOCOLS)\n      .addHeader(\"Connection\", \"upgrade\")\n      .socketHandler(this)\n      .build()\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http/SocketFailureTest.kt",
    "content": "/*\n * Copyright (C) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport assertk.assertThat\nimport assertk.assertions.isIn\nimport java.net.Socket\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Call\nimport okhttp3.Connection\nimport okhttp3.EventListener\nimport okhttp3.Headers\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Request\nimport okhttp3.testing.PlatformRule\nimport okio.IOException\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Tag(\"Slowish\")\nclass SocketFailureTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  val listener = SocketClosingEventListener()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .eventListener(listener)\n      .build()\n\n  class SocketClosingEventListener : EventListener() {\n    var shouldClose: Boolean = false\n    var lastSocket: Socket? = null\n\n    override fun connectionAcquired(\n      call: Call,\n      connection: Connection,\n    ) {\n      lastSocket = connection.socket()\n    }\n\n    override fun requestHeadersStart(call: Call) {\n      if (shouldClose) {\n        lastSocket!!.close()\n      }\n    }\n  }\n\n  @Test\n  fun socketFailureOnLargeRequestHeaders() {\n    server.enqueue(MockResponse())\n    server.enqueue(MockResponse())\n    server.start()\n\n    val call1 =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    call1.execute().use { response -> response.body.string() }\n\n    listener.shouldClose = true\n    // Large headers are a likely reason the servers would cut off the connection before it completes sending\n    // request headers.\n    // 431 \"Request Header Fields Too Large\"\n    val largeHeaders =\n      Headers\n        .Builder()\n        .apply {\n          repeat(32) {\n            add(\"name-$it\", \"value-$it-\" + \"0\".repeat(1024))\n          }\n        }.build()\n    val call2 =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .headers(largeHeaders)\n          .build(),\n      )\n\n    val exception =\n      assertFailsWith<IOException> {\n        call2.execute()\n      }\n    assertThat(exception.message).isIn(\"Socket closed\", \"Socket is closed\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http/StatusLineTest.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.net.ProtocolException\nimport kotlin.test.assertFailsWith\nimport okhttp3.Protocol\nimport okhttp3.internal.http.StatusLine.Companion.parse\nimport org.junit.jupiter.api.Test\n\nclass StatusLineTest {\n  @Test\n  fun parse() {\n    val message = \"Temporary Redirect\"\n    val version = 1\n    val code = 200\n    val statusLine = parse(\"HTTP/1.$version $code $message\")\n    assertThat(statusLine.message).isEqualTo(message)\n    assertThat(statusLine.protocol).isEqualTo(Protocol.HTTP_1_1)\n    assertThat(statusLine.code).isEqualTo(code)\n  }\n\n  @Test\n  fun emptyMessage() {\n    val version = 1\n    val code = 503\n    val statusLine = parse(\"HTTP/1.$version $code \")\n    assertThat(statusLine.message).isEqualTo(\"\")\n    assertThat(statusLine.protocol).isEqualTo(Protocol.HTTP_1_1)\n    assertThat(statusLine.code).isEqualTo(code)\n  }\n\n  /**\n   * This is not defined in the protocol but some servers won't add the leading empty space when the\n   * message is empty. http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1\n   */\n  @Test\n  fun emptyMessageAndNoLeadingSpace() {\n    val version = 1\n    val code = 503\n    val statusLine = parse(\"HTTP/1.$version $code\")\n    assertThat(statusLine.message).isEqualTo(\"\")\n    assertThat(statusLine.protocol).isEqualTo(Protocol.HTTP_1_1)\n    assertThat(statusLine.code).isEqualTo(code)\n  }\n\n  // https://github.com/square/okhttp/issues/386\n  @Test\n  fun shoutcast() {\n    val statusLine = parse(\"ICY 200 OK\")\n    assertThat(statusLine.message).isEqualTo(\"OK\")\n    assertThat(statusLine.protocol).isEqualTo(Protocol.HTTP_1_0)\n    assertThat(statusLine.code).isEqualTo(200)\n  }\n\n  @Test\n  fun missingProtocol() {\n    assertInvalid(\"\")\n    assertInvalid(\" \")\n    assertInvalid(\"200 OK\")\n    assertInvalid(\" 200 OK\")\n  }\n\n  @Test\n  fun protocolVersions() {\n    assertInvalid(\"HTTP/2.0 200 OK\")\n    assertInvalid(\"HTTP/2.1 200 OK\")\n    assertInvalid(\"HTTP/-.1 200 OK\")\n    assertInvalid(\"HTTP/1.- 200 OK\")\n    assertInvalid(\"HTTP/0.1 200 OK\")\n    assertInvalid(\"HTTP/101 200 OK\")\n    assertInvalid(\"HTTP/1.1_200 OK\")\n  }\n\n  @Test\n  fun nonThreeDigitCode() {\n    assertInvalid(\"HTTP/1.1  OK\")\n    assertInvalid(\"HTTP/1.1 2 OK\")\n    assertInvalid(\"HTTP/1.1 20 OK\")\n    assertInvalid(\"HTTP/1.1 2000 OK\")\n    assertInvalid(\"HTTP/1.1 two OK\")\n    assertInvalid(\"HTTP/1.1 2\")\n    assertInvalid(\"HTTP/1.1 2000\")\n    assertInvalid(\"HTTP/1.1 two\")\n  }\n\n  @Test\n  fun truncated() {\n    assertInvalid(\"\")\n    assertInvalid(\"H\")\n    assertInvalid(\"HTTP/1\")\n    assertInvalid(\"HTTP/1.\")\n    assertInvalid(\"HTTP/1.1\")\n    assertInvalid(\"HTTP/1.1 \")\n    assertInvalid(\"HTTP/1.1 2\")\n    assertInvalid(\"HTTP/1.1 20\")\n  }\n\n  @Test\n  fun wrongMessageDelimiter() {\n    assertInvalid(\"HTTP/1.1 200_\")\n  }\n\n  private fun assertInvalid(statusLine: String) {\n    assertFailsWith<ProtocolException> {\n      parse(statusLine)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http/ThreadInterruptTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isIn\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isNotNull\nimport assertk.assertions.matchesPredicate\nimport assertk.assertions.startsWith\nimport assertk.fail\nimport java.io.IOException\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.net.SocketException\nimport java.util.concurrent.CompletableFuture\nimport java.util.concurrent.RejectedExecutionException\nimport java.util.concurrent.TimeUnit\nimport kotlin.test.assertFailsWith\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.DelegatingServerSocketFactory\nimport okhttp3.DelegatingSocketFactory\nimport okhttp3.OkHttpClient\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Request\nimport okhttp3.RequestBody\nimport okhttp3.Response\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport okhttp3.mockwebserver.SocketPolicy\nimport okhttp3.testing.PlatformRule\nimport okio.Buffer\nimport okio.BufferedSink\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Tag(\"Slowish\")\nclass ThreadInterruptTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n  private lateinit var server: MockWebServer\n  private lateinit var client: OkHttpClient\n\n  @BeforeEach\n  fun setUp() {\n    // Sockets on some platforms can have large buffers that mean writes do not block when\n    // required. These socket factories explicitly set the buffer sizes on sockets created.\n    server = MockWebServer()\n    server.serverSocketFactory =\n      object : DelegatingServerSocketFactory(getDefault()) {\n        @Throws(SocketException::class)\n        override fun configureServerSocket(serverSocket: ServerSocket): ServerSocket {\n          serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE)\n          return serverSocket\n        }\n      }\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .socketFactory(\n          object : DelegatingSocketFactory(getDefault()) {\n            @Throws(IOException::class)\n            override fun configureSocket(socket: Socket): Socket {\n              socket.setSendBufferSize(SOCKET_BUFFER_SIZE)\n              socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE)\n              return socket\n            }\n          },\n        ).build()\n  }\n\n  @AfterEach\n  fun tearDown() {\n    Thread.interrupted() // Clear interrupted state.\n  }\n\n  @Test\n  fun interruptWritingRequestBody() {\n    server.enqueue(MockResponse())\n    server.start()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .post(\n            object : RequestBody() {\n              override fun contentType() = null\n\n              override fun writeTo(sink: BufferedSink) {\n                for (i in 0..9) {\n                  sink.writeByte(0)\n                  sink.flush()\n                  sleep(100)\n                }\n                fail(\"Expected connection to be closed\")\n              }\n            },\n          ).build(),\n      )\n    interruptLater(500)\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n  }\n\n  @Test\n  fun interruptReadingResponseBody() {\n    val responseBodySize = 8 * 1024 * 1024 // 8 MiB.\n    server.enqueue(\n      MockResponse()\n        .setBody(Buffer().write(ByteArray(responseBodySize)))\n        .throttleBody((64 * 1024).toLong(), 125, TimeUnit.MILLISECONDS),\n    ) // 500 Kbps\n    server.start()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response = call.execute()\n    interruptLater(500)\n    val responseBody = response.body.byteStream()\n    val buffer = ByteArray(1024)\n    assertFailsWith<IOException> {\n      while (responseBody.read(buffer) != -1) {\n      }\n    }\n    responseBody.close()\n  }\n\n  @Test\n  fun forciblyStopDispatcher() {\n    client =\n      client\n        .newBuilder()\n        .fastFallback(true)\n        .build()\n\n    val callFailure = CompletableFuture<Exception>()\n\n    server.enqueue(\n      MockResponse()\n        .setSocketPolicy(SocketPolicy.STALL_SOCKET_AT_START),\n    )\n    server.start()\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    call.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: okio.IOException,\n        ) {\n          callFailure.complete(e)\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n        }\n      },\n    )\n\n    // This should fail the Call, but not cause an unhandled Exception bubbling up\n    client.dispatcher.executorService.shutdownNow()\n\n    val exception = callFailure.get(5, TimeUnit.SECONDS)\n    assertThat(exception.message)\n      .isNotNull()\n      .startsWith(\"canceled due to\")\n    assertThat(exception).isInstanceOf<IOException>()\n    assertThat(exception.cause)\n      .isNotNull()\n      .matchesPredicate { it is InterruptedException || it is RejectedExecutionException }\n    assertThat(clientTestRule.takeUncaughtException())\n      .matchesPredicate { it == null || it is RejectedExecutionException }\n  }\n\n  private fun sleep(delayMillis: Int) {\n    try {\n      Thread.sleep(delayMillis.toLong())\n    } catch (e: InterruptedException) {\n      Thread.currentThread().interrupt()\n    }\n  }\n\n  private fun interruptLater(delayMillis: Int) {\n    val toInterrupt = Thread.currentThread()\n    val interruptingCow =\n      Thread {\n        sleep(delayMillis)\n        toInterrupt.interrupt()\n      }\n    interruptingCow.start()\n  }\n\n  companion object {\n    // The size of the socket buffers in bytes.\n    private const val SOCKET_BUFFER_SIZE = 256 * 1024\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/BaseTestHandler.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport assertk.fail\nimport okio.BufferedSource\nimport okio.ByteString\n\ninternal open class BaseTestHandler : Http2Reader.Handler {\n  override fun data(\n    inFinished: Boolean,\n    streamId: Int,\n    source: BufferedSource,\n    length: Int,\n  ) {\n    fail(\"\")\n  }\n\n  override fun headers(\n    inFinished: Boolean,\n    streamId: Int,\n    associatedStreamId: Int,\n    headerBlock: List<Header>,\n  ) {\n    fail(\"\")\n  }\n\n  override fun rstStream(\n    streamId: Int,\n    errorCode: ErrorCode,\n  ) {\n    fail(\"\")\n  }\n\n  override fun settings(\n    clearPrevious: Boolean,\n    settings: Settings,\n  ) {\n    fail(\"\")\n  }\n\n  override fun ackSettings() {\n    fail(\"\")\n  }\n\n  override fun ping(\n    ack: Boolean,\n    payload1: Int,\n    payload2: Int,\n  ) {\n    fail(\"\")\n  }\n\n  override fun goAway(\n    lastGoodStreamId: Int,\n    errorCode: ErrorCode,\n    debugData: ByteString,\n  ) {\n    fail(\"\")\n  }\n\n  override fun windowUpdate(\n    streamId: Int,\n    windowSizeIncrement: Long,\n  ) {\n    fail(\"\")\n  }\n\n  override fun priority(\n    streamId: Int,\n    streamDependency: Int,\n    weight: Int,\n    exclusive: Boolean,\n  ) {\n    fail(\"\")\n  }\n\n  override fun pushPromise(\n    streamId: Int,\n    associatedStreamId: Int,\n    headerBlock: List<Header>,\n  ) {\n    fail(\"\")\n  }\n\n  override fun alternateService(\n    streamId: Int,\n    origin: String,\n    protocol: ByteString,\n    host: String,\n    port: Int,\n    maxAge: Long,\n  ) {\n    fail(\"\")\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/FrameLogTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport okhttp3.internal.http2.Http2.FLAG_ACK\nimport okhttp3.internal.http2.Http2.FLAG_END_HEADERS\nimport okhttp3.internal.http2.Http2.FLAG_END_STREAM\nimport okhttp3.internal.http2.Http2.FLAG_NONE\nimport okhttp3.internal.http2.Http2.TYPE_CONTINUATION\nimport okhttp3.internal.http2.Http2.TYPE_DATA\nimport okhttp3.internal.http2.Http2.TYPE_GOAWAY\nimport okhttp3.internal.http2.Http2.TYPE_HEADERS\nimport okhttp3.internal.http2.Http2.TYPE_PING\nimport okhttp3.internal.http2.Http2.TYPE_PUSH_PROMISE\nimport okhttp3.internal.http2.Http2.TYPE_SETTINGS\nimport okhttp3.internal.http2.Http2.formatFlags\nimport okhttp3.internal.http2.Http2.frameLog\nimport okhttp3.internal.http2.Http2.frameLogWindowUpdate\nimport org.junit.jupiter.api.Test\n\nclass FrameLogTest {\n  /** Real stream traffic applied to the log format.  */\n  @Test\n  fun exampleStream() {\n    assertThat(frameLog(false, 0, 5, TYPE_SETTINGS, FLAG_NONE))\n      .isEqualTo(\">> 0x00000000     5 SETTINGS      \")\n    assertThat(frameLog(false, 3, 100, TYPE_HEADERS, FLAG_END_HEADERS))\n      .isEqualTo(\">> 0x00000003   100 HEADERS       END_HEADERS\")\n    assertThat(frameLog(false, 3, 0, TYPE_DATA, FLAG_END_STREAM))\n      .isEqualTo(\">> 0x00000003     0 DATA          END_STREAM\")\n    assertThat(frameLog(true, 0, 15, TYPE_SETTINGS, FLAG_NONE))\n      .isEqualTo(\"<< 0x00000000    15 SETTINGS      \")\n    assertThat(frameLog(false, 0, 0, TYPE_SETTINGS, FLAG_ACK))\n      .isEqualTo(\">> 0x00000000     0 SETTINGS      ACK\")\n    assertThat(frameLog(true, 0, 0, TYPE_SETTINGS, FLAG_ACK))\n      .isEqualTo(\"<< 0x00000000     0 SETTINGS      ACK\")\n    assertThat(frameLog(true, 3, 22, TYPE_HEADERS, FLAG_END_HEADERS))\n      .isEqualTo(\"<< 0x00000003    22 HEADERS       END_HEADERS\")\n    assertThat(frameLog(true, 3, 226, TYPE_DATA, FLAG_END_STREAM))\n      .isEqualTo(\"<< 0x00000003   226 DATA          END_STREAM\")\n    assertThat(frameLog(false, 0, 8, TYPE_GOAWAY, FLAG_NONE))\n      .isEqualTo(\">> 0x00000000     8 GOAWAY        \")\n  }\n\n  /** Window update frames have special formatting.  */\n  @Test\n  fun windowUpdateFrames() {\n    assertThat(frameLogWindowUpdate(false, 0, 4, Int.MAX_VALUE.toLong()))\n      .isEqualTo(\">> 0x00000000     4 WINDOW_UPDATE 2147483647\")\n    assertThat(frameLogWindowUpdate(true, 101, 4, 1))\n      .isEqualTo(\"<< 0x00000065     4 WINDOW_UPDATE 1\")\n  }\n\n  @Test\n  fun flagOverlapOn0x1() {\n    assertThat(frameLog(true, 0, 0, TYPE_SETTINGS, 0x1))\n      .isEqualTo(\"<< 0x00000000     0 SETTINGS      ACK\")\n    assertThat(frameLog(true, 0, 8, TYPE_PING, 0x1))\n      .isEqualTo(\"<< 0x00000000     8 PING          ACK\")\n    assertThat(frameLog(true, 3, 0, TYPE_HEADERS, 0x1))\n      .isEqualTo(\"<< 0x00000003     0 HEADERS       END_STREAM\")\n    assertThat(frameLog(true, 3, 0, TYPE_DATA, 0x1))\n      .isEqualTo(\"<< 0x00000003     0 DATA          END_STREAM\")\n  }\n\n  @Test\n  fun flagOverlapOn0x4() {\n    assertThat(frameLog(true, 3, 10000, TYPE_HEADERS, 0x4))\n      .isEqualTo(\"<< 0x00000003 10000 HEADERS       END_HEADERS\")\n    assertThat(frameLog(true, 3, 10000, TYPE_CONTINUATION, 0x4))\n      .isEqualTo(\"<< 0x00000003 10000 CONTINUATION  END_HEADERS\")\n    assertThat(frameLog(true, 4, 10000, TYPE_PUSH_PROMISE, 0x4))\n      .isEqualTo(\"<< 0x00000004 10000 PUSH_PROMISE  END_PUSH_PROMISE\")\n  }\n\n  @Test\n  fun flagOverlapOn0x20() {\n    assertThat(frameLog(true, 3, 10000, TYPE_HEADERS, 0x20))\n      .isEqualTo(\"<< 0x00000003 10000 HEADERS       PRIORITY\")\n    assertThat(frameLog(true, 3, 10000, TYPE_DATA, 0x20))\n      .isEqualTo(\"<< 0x00000003 10000 DATA          COMPRESSED\")\n  }\n\n  /**\n   * Ensures that valid flag combinations appear visually correct, and invalid show in hex.  This\n   * also demonstrates how sparse the lookup table is.\n   */\n  @Test\n  fun allFormattedFlagsWithValidBits() {\n    val formattedFlags = mutableListOf<String>() // Highest valid flag is 0x20.\n    for (i in 0..0x3f) formattedFlags.add(formatFlags(TYPE_HEADERS, i))\n    assertThat(formattedFlags).containsExactly(\n      \"\",\n      \"END_STREAM\",\n      \"00000010\",\n      \"00000011\",\n      \"END_HEADERS\",\n      \"END_STREAM|END_HEADERS\",\n      \"00000110\",\n      \"00000111\",\n      \"PADDED\",\n      \"END_STREAM|PADDED\",\n      \"00001010\",\n      \"00001011\",\n      \"00001100\",\n      \"END_STREAM|END_HEADERS|PADDED\",\n      \"00001110\",\n      \"00001111\",\n      \"00010000\",\n      \"00010001\",\n      \"00010010\",\n      \"00010011\",\n      \"00010100\",\n      \"00010101\",\n      \"00010110\",\n      \"00010111\",\n      \"00011000\",\n      \"00011001\",\n      \"00011010\",\n      \"00011011\",\n      \"00011100\",\n      \"00011101\",\n      \"00011110\",\n      \"00011111\",\n      \"PRIORITY\",\n      \"END_STREAM|PRIORITY\",\n      \"00100010\",\n      \"00100011\",\n      \"END_HEADERS|PRIORITY\",\n      \"END_STREAM|END_HEADERS|PRIORITY\",\n      \"00100110\",\n      \"00100111\",\n      \"00101000\",\n      \"END_STREAM|PRIORITY|PADDED\",\n      \"00101010\",\n      \"00101011\",\n      \"00101100\",\n      \"END_STREAM|END_HEADERS|PRIORITY|PADDED\",\n      \"00101110\",\n      \"00101111\",\n      \"00110000\",\n      \"00110001\",\n      \"00110010\",\n      \"00110011\",\n      \"00110100\",\n      \"00110101\",\n      \"00110110\",\n      \"00110111\",\n      \"00111000\",\n      \"00111001\",\n      \"00111010\",\n      \"00111011\",\n      \"00111100\",\n      \"00111101\",\n      \"00111110\",\n      \"00111111\",\n    )\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/HpackTest.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport java.io.IOException\nimport java.util.Arrays\nimport kotlin.test.assertFailsWith\nimport okhttp3.TestUtil.headerEntries\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\nclass HpackTest {\n  private val bytesIn = Buffer()\n  private var hpackReader: Hpack.Reader? = null\n  private val bytesOut = Buffer()\n  private var hpackWriter: Hpack.Writer? = null\n\n  @BeforeEach\n  fun reset() {\n    hpackReader = newReader(bytesIn)\n    hpackWriter = Hpack.Writer(4096, false, bytesOut)\n  }\n\n  /**\n   * Variable-length quantity special cases strings which are longer than 127 bytes.  Values such as\n   * cookies can be 4KiB, and should be possible to send.\n   *\n   *  http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-5.2\n   */\n  @Test\n  fun largeHeaderValue() {\n    val value = CharArray(4096)\n    Arrays.fill(value, '!')\n    val headerBlock = headerEntries(\"cookie\", String(value))\n    hpackWriter!!.writeHeaders(headerBlock)\n    bytesIn.writeAll(bytesOut)\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(0)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(headerBlock)\n  }\n\n  /**\n   * HPACK has a max header table size, which can be smaller than the max header message. Ensure the\n   * larger header content is not lost.\n   */\n  @Test\n  fun tooLargeToHPackIsStillEmitted() {\n    bytesIn.writeByte(0x21) // Dynamic table size update (size = 1).\n    bytesIn.writeByte(0x00) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-key\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(0)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\"custom-key\", \"custom-header\"),\n    )\n  }\n\n  /** Oldest entries are evicted to support newer ones.  */\n  @Test\n  fun writerEviction() {\n    val headerBlock =\n      headerEntries(\n        \"custom-foo\",\n        \"custom-header\",\n        \"custom-bar\",\n        \"custom-header\",\n        \"custom-baz\",\n        \"custom-header\",\n      )\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-foo\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-bar\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-baz\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n\n    // Set to only support 110 bytes (enough for 2 headers).\n    // Use a new Writer because we don't support change the dynamic table\n    // size after Writer constructed.\n    val writer = Hpack.Writer(110, false, bytesOut)\n    writer.writeHeaders(headerBlock)\n    assertThat(bytesOut).isEqualTo(bytesIn)\n    assertThat(writer.headerCount).isEqualTo(2)\n    val tableLength = writer.dynamicTable.size\n    var entry = writer.dynamicTable[tableLength - 1]!!\n    checkEntry(entry, \"custom-bar\", \"custom-header\", 55)\n    entry = writer.dynamicTable[tableLength - 2]!!\n    checkEntry(entry, \"custom-baz\", \"custom-header\", 55)\n  }\n\n  @Test\n  fun readerEviction() {\n    val headerBlock =\n      headerEntries(\n        \"custom-foo\",\n        \"custom-header\",\n        \"custom-bar\",\n        \"custom-header\",\n        \"custom-baz\",\n        \"custom-header\",\n      )\n\n    // Set to only support 110 bytes (enough for 2 headers).\n    bytesIn.writeByte(0x3F) // Dynamic table size update (size = 110).\n    bytesIn.writeByte(0x4F)\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-foo\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-bar\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-baz\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(2)\n    val entry1 = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry1, \"custom-bar\", \"custom-header\", 55)\n    val entry2 = hpackReader!!.dynamicTable[readerHeaderTableLength() - 2]!!\n    checkEntry(entry2, \"custom-baz\", \"custom-header\", 55)\n\n    // Once a header field is decoded and added to the reconstructed header\n    // list, it cannot be removed from it. Hence, foo is here.\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(headerBlock)\n\n    // Simulate receiving a small dynamic table size update, that implies eviction.\n    bytesIn.writeByte(0x3F) // Dynamic table size update (size = 55).\n    bytesIn.writeByte(0x18)\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(1)\n  }\n\n  /** Header table backing array is initially 8 long, let's ensure it grows.  */\n  @Test\n  fun dynamicallyGrowsBeyond64Entries() {\n    // Lots of headers need more room!\n    hpackReader = Hpack.Reader(bytesIn, 16384, 4096)\n    bytesIn.writeByte(0x3F) // Dynamic table size update (size = 16384).\n    bytesIn.writeByte(0xE1)\n    bytesIn.writeByte(0x7F)\n    for (i in 0..255) {\n      bytesIn.writeByte(0x40) // Literal indexed\n      bytesIn.writeByte(0x0a) // Literal name (len = 10)\n      bytesIn.writeUtf8(\"custom-foo\")\n      bytesIn.writeByte(0x0d) // Literal value (len = 13)\n      bytesIn.writeUtf8(\"custom-header\")\n    }\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(256)\n  }\n\n  @Test\n  fun huffmanDecodingSupported() {\n    bytesIn.writeByte(0x44) // == Literal indexed ==\n    // Indexed name (idx = 4) -> :path\n    bytesIn.writeByte(0x8c) // Literal value Huffman encoded 12 bytes\n    // decodes to www.example.com which is length 15\n    bytesIn.write(\"f1e3c2e5f23a6ba0ab90f4ff\".decodeHex())\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(1)\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(52)\n    val entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry, \":path\", \"www.example.com\", 52)\n  }\n\n  /**\n   * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.2.1\n   */\n  @Test\n  fun readLiteralHeaderFieldWithIndexing() {\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-key\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(1)\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(55)\n    val entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry, \"custom-key\", \"custom-header\", 55)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\"custom-key\", \"custom-header\"),\n    )\n  }\n\n  /**\n   * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.2.2\n   */\n  @Test\n  fun literalHeaderFieldWithoutIndexingIndexedName() {\n    val headerBlock = headerEntries(\":path\", \"/sample/path\")\n    bytesIn.writeByte(0x04) // == Literal not indexed ==\n    // Indexed name (idx = 4) -> :path\n    bytesIn.writeByte(0x0c) // Literal value (len = 12)\n    bytesIn.writeUtf8(\"/sample/path\")\n    hpackWriter!!.writeHeaders(headerBlock)\n    assertThat(bytesOut).isEqualTo(bytesIn)\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(0)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(headerBlock)\n  }\n\n  @Test\n  fun literalHeaderFieldWithoutIndexingNewName() {\n    val headerBlock = headerEntries(\"custom-key\", \"custom-header\")\n    bytesIn.writeByte(0x00) // Not indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-key\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(0)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(headerBlock)\n  }\n\n  @Test\n  fun literalHeaderFieldNeverIndexedIndexedName() {\n    bytesIn.writeByte(0x14) // == Literal never indexed ==\n    // Indexed name (idx = 4) -> :path\n    bytesIn.writeByte(0x0c) // Literal value (len = 12)\n    bytesIn.writeUtf8(\"/sample/path\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(0)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\":path\", \"/sample/path\"),\n    )\n  }\n\n  @Test\n  fun literalHeaderFieldNeverIndexedNewName() {\n    val headerBlock = headerEntries(\"custom-key\", \"custom-header\")\n    bytesIn.writeByte(0x10) // Never indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-key\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(0)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(headerBlock)\n  }\n\n  @Test\n  fun literalHeaderFieldWithIncrementalIndexingIndexedName() {\n    val headerBlock = headerEntries(\":path\", \"/sample/path\")\n    bytesIn.writeByte(0x44) // Indexed name (idx = 4) -> :path\n    bytesIn.writeByte(0x0c) // Literal value (len = 12)\n    bytesIn.writeUtf8(\"/sample/path\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(1)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(headerBlock)\n  }\n\n  @Test\n  fun literalHeaderFieldWithIncrementalIndexingNewName() {\n    val headerBlock = headerEntries(\"custom-key\", \"custom-header\")\n    bytesIn.writeByte(0x40) // Never indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-key\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    hpackWriter!!.writeHeaders(headerBlock)\n    assertThat(bytesOut).isEqualTo(bytesIn)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n    val entry = hpackWriter!!.dynamicTable[hpackWriter!!.dynamicTable.size - 1]!!\n    checkEntry(entry, \"custom-key\", \"custom-header\", 55)\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(1)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(headerBlock)\n  }\n\n  @Test\n  fun theSameHeaderAfterOneIncrementalIndexed() {\n    val headerBlock =\n      headerEntries(\n        \"custom-key\",\n        \"custom-header\",\n        \"custom-key\",\n        \"custom-header\",\n      )\n    bytesIn.writeByte(0x40) // Never indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-key\")\n    bytesIn.writeByte(0x0d) // Literal value (len = 13)\n    bytesIn.writeUtf8(\"custom-header\")\n    bytesIn.writeByte(0xbe) // Indexed name and value (idx = 63)\n    hpackWriter!!.writeHeaders(headerBlock)\n    assertThat(bytesOut).isEqualTo(bytesIn)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n    val entry = hpackWriter!!.dynamicTable[hpackWriter!!.dynamicTable.size - 1]!!\n    checkEntry(entry, \"custom-key\", \"custom-header\", 55)\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(1)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(headerBlock)\n  }\n\n  @Test\n  fun staticHeaderIsNotCopiedIntoTheIndexedTable() {\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.headerCount).isEqualTo(0)\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(0)\n    assertThat(hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]).isNull()\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\":method\", \"GET\"),\n    )\n  }\n\n  // Example taken from twitter/hpack DecoderTest.testUnusedIndex\n  @Test\n  fun readIndexedHeaderFieldIndex0() {\n    bytesIn.writeByte(0x80) // == Indexed - Add idx = 0\n    assertFailsWith<IOException> {\n      hpackReader!!.readHeaders()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"index == 0\")\n    }\n  }\n\n  // Example taken from twitter/hpack DecoderTest.testIllegalIndex\n  @Test\n  fun readIndexedHeaderFieldTooLargeIndex() {\n    bytesIn.writeShort(0xff00) // == Indexed - Add idx = 127\n    assertFailsWith<IOException> {\n      hpackReader!!.readHeaders()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Header index too large 127\")\n    }\n  }\n\n  // Example taken from twitter/hpack DecoderTest.testInsidiousIndex\n  @Test\n  fun readIndexedHeaderFieldInsidiousIndex() {\n    bytesIn.writeByte(0xff) // == Indexed - Add ==\n    bytesIn.write(\"8080808008\".decodeHex()) // idx = -2147483521\n    assertFailsWith<IOException> {\n      hpackReader!!.readHeaders()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Header index too large -2147483521\")\n    }\n  }\n\n  // Example taken from twitter/hpack DecoderTest.testHeaderTableSizeUpdate\n  @Test\n  fun minMaxHeaderTableSize() {\n    bytesIn.writeByte(0x20)\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.maxDynamicTableByteCount()).isEqualTo(0)\n    bytesIn.writeByte(0x3f) // encode size 4096\n    bytesIn.writeByte(0xe1)\n    bytesIn.writeByte(0x1f)\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.maxDynamicTableByteCount()).isEqualTo(4096)\n  }\n\n  // Example taken from twitter/hpack DecoderTest.testIllegalHeaderTableSizeUpdate\n  @Test\n  fun cannotSetTableSizeLargerThanSettingsValue() {\n    bytesIn.writeByte(0x3f) // encode size 4097\n    bytesIn.writeByte(0xe2)\n    bytesIn.writeByte(0x1f)\n    assertFailsWith<IOException> {\n      hpackReader!!.readHeaders()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Invalid dynamic table size update 4097\")\n    }\n  }\n\n  // Example taken from twitter/hpack DecoderTest.testInsidiousMaxHeaderSize\n  @Test\n  fun readHeaderTableStateChangeInsidiousMaxHeaderByteCount() {\n    bytesIn.writeByte(0x3f)\n    bytesIn.write(\"e1ffffff07\".decodeHex()) // count = -2147483648\n    assertFailsWith<IOException> {\n      hpackReader!!.readHeaders()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Invalid dynamic table size update -2147483648\")\n    }\n  }\n\n  /**\n   * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.2.4\n   */\n  @Test\n  fun readIndexedHeaderFieldFromStaticTableWithoutBuffering() {\n    bytesIn.writeByte(0x20) // Dynamic table size update (size = 0).\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    hpackReader!!.readHeaders()\n\n    // Not buffered in header table.\n    assertThat(hpackReader!!.headerCount).isEqualTo(0)\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\":method\", \"GET\"),\n    )\n  }\n\n  @Test\n  fun readLiteralHeaderWithIncrementalIndexingStaticName() {\n    bytesIn.writeByte(0x7d) // == Literal indexed ==\n    // Indexed name (idx = 60) -> \"www-authenticate\"\n    bytesIn.writeByte(0x05) // Literal value (len = 5)\n    bytesIn.writeUtf8(\"Basic\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.getAndResetHeaderList())\n      .containsExactly(Header(\"www-authenticate\", \"Basic\"))\n  }\n\n  @Test\n  fun readLiteralHeaderWithIncrementalIndexingDynamicName() {\n    bytesIn.writeByte(0x40)\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-foo\")\n    bytesIn.writeByte(0x05) // Literal value (len = 5)\n    bytesIn.writeUtf8(\"Basic\")\n    bytesIn.writeByte(0x7e)\n    bytesIn.writeByte(0x06) // Literal value (len = 6)\n    bytesIn.writeUtf8(\"Basic2\")\n    hpackReader!!.readHeaders()\n    assertThat(hpackReader!!.getAndResetHeaderList()).containsExactly(\n      Header(\"custom-foo\", \"Basic\"),\n      Header(\"custom-foo\", \"Basic2\"),\n    )\n  }\n\n  /**\n   * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.2\n   */\n  @Test\n  fun readRequestExamplesWithoutHuffman() {\n    firstRequestWithoutHuffman()\n    hpackReader!!.readHeaders()\n    checkReadFirstRequestWithoutHuffman()\n    secondRequestWithoutHuffman()\n    hpackReader!!.readHeaders()\n    checkReadSecondRequestWithoutHuffman()\n    thirdRequestWithoutHuffman()\n    hpackReader!!.readHeaders()\n    checkReadThirdRequestWithoutHuffman()\n  }\n\n  @Test\n  fun readFailingRequestExample() {\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    bytesIn.writeByte(0x86) // == Indexed - Add ==\n    // idx = 7 -> :scheme: http\n    bytesIn.writeByte(0x84) // == Indexed - Add ==\n    bytesIn.writeByte(0x7f) // == Bad index! ==\n\n    // Indexed name (idx = 4) -> :authority\n    bytesIn.writeByte(0x0f) // Literal value (len = 15)\n    bytesIn.writeUtf8(\"www.example.com\")\n    assertFailsWith<IOException> {\n      hpackReader!!.readHeaders()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Header index too large 78\")\n    }\n  }\n\n  private fun firstRequestWithoutHuffman() {\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    bytesIn.writeByte(0x86) // == Indexed - Add ==\n    // idx = 7 -> :scheme: http\n    bytesIn.writeByte(0x84) // == Indexed - Add ==\n    // idx = 6 -> :path: /\n    bytesIn.writeByte(0x41) // == Literal indexed ==\n    // Indexed name (idx = 4) -> :authority\n    bytesIn.writeByte(0x0f) // Literal value (len = 15)\n    bytesIn.writeUtf8(\"www.example.com\")\n  }\n\n  private fun checkReadFirstRequestWithoutHuffman() {\n    assertThat(hpackReader!!.headerCount).isEqualTo(1)\n\n    // [  1] (s =  57) :authority: www.example.com\n    val entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry, \":authority\", \"www.example.com\", 57)\n\n    // Table size: 57\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(57)\n\n    // Decoded header list:\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\n        \":method\",\n        \"GET\",\n        \":scheme\",\n        \"http\",\n        \":path\",\n        \"/\",\n        \":authority\",\n        \"www.example.com\",\n      ),\n    )\n  }\n\n  private fun secondRequestWithoutHuffman() {\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    bytesIn.writeByte(0x86) // == Indexed - Add ==\n    // idx = 7 -> :scheme: http\n    bytesIn.writeByte(0x84) // == Indexed - Add ==\n    // idx = 6 -> :path: /\n    bytesIn.writeByte(0xbe) // == Indexed - Add ==\n    // Indexed name (idx = 62) -> :authority: www.example.com\n    bytesIn.writeByte(0x58) // == Literal indexed ==\n    // Indexed name (idx = 24) -> cache-control\n    bytesIn.writeByte(0x08) // Literal value (len = 8)\n    bytesIn.writeUtf8(\"no-cache\")\n  }\n\n  private fun checkReadSecondRequestWithoutHuffman() {\n    assertThat(hpackReader!!.headerCount).isEqualTo(2)\n\n    // [  1] (s =  53) cache-control: no-cache\n    var entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 2]!!\n    checkEntry(entry, \"cache-control\", \"no-cache\", 53)\n\n    // [  2] (s =  57) :authority: www.example.com\n    entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry, \":authority\", \"www.example.com\", 57)\n\n    // Table size: 110\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(110)\n\n    // Decoded header list:\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\n        \":method\",\n        \"GET\",\n        \":scheme\",\n        \"http\",\n        \":path\",\n        \"/\",\n        \":authority\",\n        \"www.example.com\",\n        \"cache-control\",\n        \"no-cache\",\n      ),\n    )\n  }\n\n  private fun thirdRequestWithoutHuffman() {\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    bytesIn.writeByte(0x87) // == Indexed - Add ==\n    // idx = 7 -> :scheme: http\n    bytesIn.writeByte(0x85) // == Indexed - Add ==\n    // idx = 5 -> :path: /index.html\n    bytesIn.writeByte(0xbf) // == Indexed - Add ==\n    // Indexed name (idx = 63) -> :authority: www.example.com\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x0a) // Literal name (len = 10)\n    bytesIn.writeUtf8(\"custom-key\")\n    bytesIn.writeByte(0x0c) // Literal value (len = 12)\n    bytesIn.writeUtf8(\"custom-value\")\n  }\n\n  private fun checkReadThirdRequestWithoutHuffman() {\n    assertThat(hpackReader!!.headerCount).isEqualTo(3)\n\n    // [  1] (s =  54) custom-key: custom-value\n    var entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 3]!!\n    checkEntry(entry, \"custom-key\", \"custom-value\", 54)\n\n    // [  2] (s =  53) cache-control: no-cache\n    entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 2]!!\n    checkEntry(entry, \"cache-control\", \"no-cache\", 53)\n\n    // [  3] (s =  57) :authority: www.example.com\n    entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry, \":authority\", \"www.example.com\", 57)\n\n    // Table size: 164\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(164)\n\n    // Decoded header list:\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\n        \":method\",\n        \"GET\",\n        \":scheme\",\n        \"https\",\n        \":path\",\n        \"/index.html\",\n        \":authority\",\n        \"www.example.com\",\n        \"custom-key\",\n        \"custom-value\",\n      ),\n    )\n  }\n\n  /**\n   * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.4\n   */\n  @Test\n  fun readRequestExamplesWithHuffman() {\n    firstRequestWithHuffman()\n    hpackReader!!.readHeaders()\n    checkReadFirstRequestWithHuffman()\n    secondRequestWithHuffman()\n    hpackReader!!.readHeaders()\n    checkReadSecondRequestWithHuffman()\n    thirdRequestWithHuffman()\n    hpackReader!!.readHeaders()\n    checkReadThirdRequestWithHuffman()\n  }\n\n  private fun firstRequestWithHuffman() {\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    bytesIn.writeByte(0x86) // == Indexed - Add ==\n    // idx = 6 -> :scheme: http\n    bytesIn.writeByte(0x84) // == Indexed - Add ==\n    // idx = 4 -> :path: /\n    bytesIn.writeByte(0x41) // == Literal indexed ==\n    // Indexed name (idx = 1) -> :authority\n    bytesIn.writeByte(0x8c) // Literal value Huffman encoded 12 bytes\n    // decodes to www.example.com which is length 15\n    bytesIn.write(\"f1e3c2e5f23a6ba0ab90f4ff\".decodeHex())\n  }\n\n  private fun checkReadFirstRequestWithHuffman() {\n    assertThat(hpackReader!!.headerCount).isEqualTo(1)\n\n    // [  1] (s =  57) :authority: www.example.com\n    val entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry, \":authority\", \"www.example.com\", 57)\n\n    // Table size: 57\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(57)\n\n    // Decoded header list:\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\n        \":method\",\n        \"GET\",\n        \":scheme\",\n        \"http\",\n        \":path\",\n        \"/\",\n        \":authority\",\n        \"www.example.com\",\n      ),\n    )\n  }\n\n  private fun secondRequestWithHuffman() {\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    bytesIn.writeByte(0x86) // == Indexed - Add ==\n    // idx = 6 -> :scheme: http\n    bytesIn.writeByte(0x84) // == Indexed - Add ==\n    // idx = 4 -> :path: /\n    bytesIn.writeByte(0xbe) // == Indexed - Add ==\n    // idx = 62 -> :authority: www.example.com\n    bytesIn.writeByte(0x58) // == Literal indexed ==\n    // Indexed name (idx = 24) -> cache-control\n    bytesIn.writeByte(0x86) // Literal value Huffman encoded 6 bytes\n    // decodes to no-cache which is length 8\n    bytesIn.write(\"a8eb10649cbf\".decodeHex())\n  }\n\n  private fun checkReadSecondRequestWithHuffman() {\n    assertThat(hpackReader!!.headerCount).isEqualTo(2)\n\n    // [  1] (s =  53) cache-control: no-cache\n    var entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 2]!!\n    checkEntry(entry, \"cache-control\", \"no-cache\", 53)\n\n    // [  2] (s =  57) :authority: www.example.com\n    entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry, \":authority\", \"www.example.com\", 57)\n\n    // Table size: 110\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(110)\n\n    // Decoded header list:\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\n        \":method\",\n        \"GET\",\n        \":scheme\",\n        \"http\",\n        \":path\",\n        \"/\",\n        \":authority\",\n        \"www.example.com\",\n        \"cache-control\",\n        \"no-cache\",\n      ),\n    )\n  }\n\n  private fun thirdRequestWithHuffman() {\n    bytesIn.writeByte(0x82) // == Indexed - Add ==\n    // idx = 2 -> :method: GET\n    bytesIn.writeByte(0x87) // == Indexed - Add ==\n    // idx = 7 -> :scheme: https\n    bytesIn.writeByte(0x85) // == Indexed - Add ==\n    // idx = 5 -> :path: /index.html\n    bytesIn.writeByte(0xbf) // == Indexed - Add ==\n    // idx = 63 -> :authority: www.example.com\n    bytesIn.writeByte(0x40) // Literal indexed\n    bytesIn.writeByte(0x88) // Literal name Huffman encoded 8 bytes\n    // decodes to custom-key which is length 10\n    bytesIn.write(\"25a849e95ba97d7f\".decodeHex())\n    bytesIn.writeByte(0x89) // Literal value Huffman encoded 9 bytes\n    // decodes to custom-value which is length 12\n    bytesIn.write(\"25a849e95bb8e8b4bf\".decodeHex())\n  }\n\n  private fun checkReadThirdRequestWithHuffman() {\n    assertThat(hpackReader!!.headerCount).isEqualTo(3)\n\n    // [  1] (s =  54) custom-key: custom-value\n    var entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 3]!!\n    checkEntry(entry, \"custom-key\", \"custom-value\", 54)\n\n    // [  2] (s =  53) cache-control: no-cache\n    entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 2]!!\n    checkEntry(entry, \"cache-control\", \"no-cache\", 53)\n\n    // [  3] (s =  57) :authority: www.example.com\n    entry = hpackReader!!.dynamicTable[readerHeaderTableLength() - 1]!!\n    checkEntry(entry, \":authority\", \"www.example.com\", 57)\n\n    // Table size: 164\n    assertThat(hpackReader!!.dynamicTableByteCount).isEqualTo(164)\n\n    // Decoded header list:\n    assertThat(hpackReader!!.getAndResetHeaderList()).isEqualTo(\n      headerEntries(\n        \":method\",\n        \"GET\",\n        \":scheme\",\n        \"https\",\n        \":path\",\n        \"/index.html\",\n        \":authority\",\n        \"www.example.com\",\n        \"custom-key\",\n        \"custom-value\",\n      ),\n    )\n  }\n\n  @Test\n  fun readSingleByteInt() {\n    assertThat(newReader(byteStream()).readInt(10, 31)).isEqualTo(10)\n    assertThat(newReader(byteStream()).readInt(0xe0 or 10, 31)).isEqualTo(10)\n  }\n\n  @Test\n  fun readMultibyteInt() {\n    assertThat(newReader(byteStream(154, 10)).readInt(31, 31)).isEqualTo(1337)\n  }\n\n  @Test\n  fun writeSingleByteInt() {\n    hpackWriter!!.writeInt(10, 31, 0)\n    assertBytes(10)\n    hpackWriter!!.writeInt(10, 31, 0xe0)\n    assertBytes(0xe0 or 10)\n  }\n\n  @Test\n  fun writeMultibyteInt() {\n    hpackWriter!!.writeInt(1337, 31, 0)\n    assertBytes(31, 154, 10)\n    hpackWriter!!.writeInt(1337, 31, 0xe0)\n    assertBytes(0xe0 or 31, 154, 10)\n  }\n\n  @Test\n  fun max31BitValue() {\n    hpackWriter!!.writeInt(0x7fffffff, 31, 0)\n    assertBytes(31, 224, 255, 255, 255, 7)\n    assertThat(newReader(byteStream(224, 255, 255, 255, 7)).readInt(31, 31))\n      .isEqualTo(0x7fffffff)\n  }\n\n  @Test\n  fun prefixMask() {\n    hpackWriter!!.writeInt(31, 31, 0)\n    assertBytes(31, 0)\n    assertThat(newReader(byteStream(0)).readInt(31, 31)).isEqualTo(31)\n  }\n\n  @Test\n  fun prefixMaskMinusOne() {\n    hpackWriter!!.writeInt(30, 31, 0)\n    assertBytes(30)\n    assertThat(newReader(byteStream(0)).readInt(31, 31)).isEqualTo(31)\n  }\n\n  @Test\n  fun zero() {\n    hpackWriter!!.writeInt(0, 31, 0)\n    assertBytes(0)\n    assertThat(newReader(byteStream()).readInt(0, 31)).isEqualTo(0)\n  }\n\n  @Test\n  fun lowercaseHeaderNameBeforeEmit() {\n    hpackWriter!!.writeHeaders(listOf(Header(\"FoO\", \"BaR\")))\n    assertBytes(0x40, 3, 'f'.code, 'o'.code, 'o'.code, 3, 'B'.code, 'a'.code, 'R'.code)\n  }\n\n  @Test\n  fun mixedCaseHeaderNameIsMalformed() {\n    assertFailsWith<IOException> {\n      newReader(\n        byteStream(\n          0,\n          3,\n          'F'.code,\n          'o'.code,\n          'o'.code,\n          3,\n          'B'.code,\n          'a'.code,\n          'R'.code,\n        ),\n      ).readHeaders()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"PROTOCOL_ERROR response malformed: mixed case name: Foo\",\n      )\n    }\n  }\n\n  @Test\n  fun emptyHeaderName() {\n    hpackWriter!!.writeByteString(\"\".encodeUtf8())\n    assertBytes(0)\n    assertThat(newReader(byteStream(0)).readByteString())\n      .isEqualTo(ByteString.EMPTY)\n  }\n\n  @Test\n  fun emitsDynamicTableSizeUpdate() {\n    hpackWriter!!.resizeHeaderTable(2048)\n    hpackWriter!!.writeHeaders(listOf(Header(\"foo\", \"bar\")))\n    assertBytes(\n      // Dynamic table size update (size = 2048).\n      0x3F,\n      0xE1,\n      0xF,\n      0x40,\n      3,\n      'f'.code,\n      'o'.code,\n      'o'.code,\n      3,\n      'b'.code,\n      'a'.code,\n      'r'.code,\n    )\n    hpackWriter!!.resizeHeaderTable(8192)\n    hpackWriter!!.writeHeaders(listOf(Header(\"bar\", \"foo\")))\n    assertBytes(\n      // Dynamic table size update (size = 8192).\n      0x3F,\n      0xE1,\n      0x3F,\n      0x40,\n      3,\n      'b'.code,\n      'a'.code,\n      'r'.code,\n      3,\n      'f'.code,\n      'o'.code,\n      'o'.code,\n    )\n\n    // No more dynamic table updates should be emitted.\n    hpackWriter!!.writeHeaders(listOf(Header(\"far\", \"boo\")))\n    assertBytes(0x40, 3, 'f'.code, 'a'.code, 'r'.code, 3, 'b'.code, 'o'.code, 'o'.code)\n  }\n\n  @Test\n  fun noDynamicTableSizeUpdateWhenSizeIsEqual() {\n    val currentSize = hpackWriter!!.headerTableSizeSetting\n    hpackWriter!!.resizeHeaderTable(currentSize)\n    hpackWriter!!.writeHeaders(listOf(Header(\"foo\", \"bar\")))\n    assertBytes(0x40, 3, 'f'.code, 'o'.code, 'o'.code, 3, 'b'.code, 'a'.code, 'r'.code)\n  }\n\n  @Test\n  fun growDynamicTableSize() {\n    hpackWriter!!.resizeHeaderTable(8192)\n    hpackWriter!!.resizeHeaderTable(16384)\n    hpackWriter!!.writeHeaders(listOf(Header(\"foo\", \"bar\")))\n    assertBytes(\n      // Dynamic table size update (size = 16384).\n      0x3F,\n      0xE1,\n      0x7F,\n      0x40,\n      3,\n      'f'.code,\n      'o'.code,\n      'o'.code,\n      3,\n      'b'.code,\n      'a'.code,\n      'r'.code,\n    )\n  }\n\n  @Test\n  fun shrinkDynamicTableSize() {\n    hpackWriter!!.resizeHeaderTable(2048)\n    hpackWriter!!.resizeHeaderTable(0)\n    hpackWriter!!.writeHeaders(listOf(Header(\"foo\", \"bar\")))\n    assertBytes(\n      // Dynamic size update (size = 0).\n      0x20,\n      0x40,\n      3,\n      'f'.code,\n      'o'.code,\n      'o'.code,\n      3,\n      'b'.code,\n      'a'.code,\n      'r'.code,\n    )\n  }\n\n  @Test\n  fun manyDynamicTableSizeChanges() {\n    hpackWriter!!.resizeHeaderTable(16384)\n    hpackWriter!!.resizeHeaderTable(8096)\n    hpackWriter!!.resizeHeaderTable(0)\n    hpackWriter!!.resizeHeaderTable(4096)\n    hpackWriter!!.resizeHeaderTable(2048)\n    hpackWriter!!.writeHeaders(listOf(Header(\"foo\", \"bar\")))\n    assertBytes(\n      // Dynamic size update (size = 0).\n      0x20,\n      // Dynamic size update (size = 2048).\n      0x3F,\n      0xE1,\n      0xF,\n      0x40,\n      3,\n      'f'.code,\n      'o'.code,\n      'o'.code,\n      3,\n      'b'.code,\n      'a'.code,\n      'r'.code,\n    )\n  }\n\n  @Test\n  fun dynamicTableEvictionWhenSizeLowered() {\n    val headerBlock =\n      headerEntries(\n        \"custom-key1\",\n        \"custom-header\",\n        \"custom-key2\",\n        \"custom-header\",\n      )\n    hpackWriter!!.writeHeaders(headerBlock)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(2)\n    hpackWriter!!.resizeHeaderTable(56)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n    hpackWriter!!.resizeHeaderTable(0)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n  }\n\n  @Test\n  fun noEvictionOnDynamicTableSizeIncrease() {\n    val headerBlock =\n      headerEntries(\n        \"custom-key1\",\n        \"custom-header\",\n        \"custom-key2\",\n        \"custom-header\",\n      )\n    hpackWriter!!.writeHeaders(headerBlock)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(2)\n    hpackWriter!!.resizeHeaderTable(8192)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(2)\n  }\n\n  @Test\n  fun dynamicTableSizeHasAnUpperBound() {\n    hpackWriter!!.resizeHeaderTable(1048576)\n    assertThat(hpackWriter!!.maxDynamicTableByteCount).isEqualTo(16384)\n  }\n\n  @Test\n  fun huffmanEncode() {\n    hpackWriter = Hpack.Writer(4096, true, bytesOut)\n    hpackWriter!!.writeHeaders(headerEntries(\"foo\", \"bar\"))\n    val expected =\n      Buffer()\n        .writeByte(0x40) // Literal header, new name.\n        .writeByte(0x82) // String literal is Huffman encoded (len = 2).\n        .writeByte(0x94) // 'foo' Huffman encoded.\n        .writeByte(0xE7)\n        .writeByte(3) // String literal not Huffman encoded (len = 3).\n        .writeByte('b'.code)\n        .writeByte('a'.code)\n        .writeByte('r'.code)\n        .readByteString()\n    val actual = bytesOut.readByteString()\n    assertThat(actual).isEqualTo(expected)\n  }\n\n  @Test\n  fun staticTableIndexedHeaders() {\n    hpackWriter!!.writeHeaders(headerEntries(\":method\", \"GET\"))\n    assertBytes(0x82)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n    hpackWriter!!.writeHeaders(headerEntries(\":method\", \"POST\"))\n    assertBytes(0x83)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n    hpackWriter!!.writeHeaders(headerEntries(\":path\", \"/\"))\n    assertBytes(0x84)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n    hpackWriter!!.writeHeaders(headerEntries(\":path\", \"/index.html\"))\n    assertBytes(0x85)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n    hpackWriter!!.writeHeaders(headerEntries(\":scheme\", \"http\"))\n    assertBytes(0x86)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n    hpackWriter!!.writeHeaders(headerEntries(\":scheme\", \"https\"))\n    assertBytes(0x87)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n  }\n\n  @Test\n  fun dynamicTableIndexedHeader() {\n    hpackWriter!!.writeHeaders(headerEntries(\"custom-key\", \"custom-header\"))\n    assertBytes(\n      0x40,\n      10,\n      'c'.code,\n      'u'.code,\n      's'.code,\n      't'.code,\n      'o'.code,\n      'm'.code,\n      '-'.code,\n      'k'.code,\n      'e'.code,\n      'y'.code,\n      13,\n      'c'.code,\n      'u'.code,\n      's'.code,\n      't'.code,\n      'o'.code,\n      'm'.code,\n      '-'.code,\n      'h'.code,\n      'e'.code,\n      'a'.code,\n      'd'.code,\n      'e'.code,\n      'r'.code,\n    )\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n    hpackWriter!!.writeHeaders(headerEntries(\"custom-key\", \"custom-header\"))\n    assertBytes(0xbe)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n  }\n\n  @Test\n  fun doNotIndexPseudoHeaders() {\n    hpackWriter!!.writeHeaders(headerEntries(\":method\", \"PUT\"))\n    assertBytes(0x02, 3, 'P'.code, 'U'.code, 'T'.code)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n    hpackWriter!!.writeHeaders(headerEntries(\":path\", \"/okhttp\"))\n    assertBytes(0x04, 7, '/'.code, 'o'.code, 'k'.code, 'h'.code, 't'.code, 't'.code, 'p'.code)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(0)\n  }\n\n  @Test\n  fun incrementalIndexingWithAuthorityPseudoHeader() {\n    hpackWriter!!.writeHeaders(headerEntries(\":authority\", \"foo.com\"))\n    assertBytes(0x41, 7, 'f'.code, 'o'.code, 'o'.code, '.'.code, 'c'.code, 'o'.code, 'm'.code)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n    hpackWriter!!.writeHeaders(headerEntries(\":authority\", \"foo.com\"))\n    assertBytes(0xbe)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n\n    // If the :authority header somehow changes, it should be re-added to the dynamic table.\n    hpackWriter!!.writeHeaders(headerEntries(\":authority\", \"bar.com\"))\n    assertBytes(0x41, 7, 'b'.code, 'a'.code, 'r'.code, '.'.code, 'c'.code, 'o'.code, 'm'.code)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(2)\n    hpackWriter!!.writeHeaders(headerEntries(\":authority\", \"bar.com\"))\n    assertBytes(0xbe)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(2)\n  }\n\n  @Test\n  fun incrementalIndexingWithStaticTableIndexedName() {\n    hpackWriter!!.writeHeaders(headerEntries(\"accept-encoding\", \"gzip\"))\n    assertBytes(0x50, 4, 'g'.code, 'z'.code, 'i'.code, 'p'.code)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n    hpackWriter!!.writeHeaders(headerEntries(\"accept-encoding\", \"gzip\"))\n    assertBytes(0xbe)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n  }\n\n  @Test\n  fun incrementalIndexingWithDynamicTableIndexedName() {\n    hpackWriter!!.writeHeaders(headerEntries(\"foo\", \"bar\"))\n    assertBytes(0x40, 3, 'f'.code, 'o'.code, 'o'.code, 3, 'b'.code, 'a'.code, 'r'.code)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(1)\n    hpackWriter!!.writeHeaders(headerEntries(\"foo\", \"bar1\"))\n    assertBytes(0x7e, 4, 'b'.code, 'a'.code, 'r'.code, '1'.code)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(2)\n    hpackWriter!!.writeHeaders(headerEntries(\"foo\", \"bar1\"))\n    assertBytes(0xbe)\n    assertThat(hpackWriter!!.headerCount).isEqualTo(2)\n  }\n\n  private fun newReader(source: Buffer): Hpack.Reader = Hpack.Reader(source, 4096)\n\n  private fun byteStream(vararg bytes: Int): Buffer = Buffer().write(intArrayToByteArray(bytes))\n\n  private fun checkEntry(\n    entry: Header,\n    name: String,\n    value: String,\n    size: Int,\n  ) {\n    assertThat(entry.name.utf8()).isEqualTo(name)\n    assertThat(entry.value.utf8()).isEqualTo(value)\n    assertThat(entry.hpackSize).isEqualTo(size)\n  }\n\n  private fun assertBytes(vararg bytes: Int) {\n    val expected = intArrayToByteArray(bytes)\n    val actual = bytesOut.readByteString()\n    assertThat(actual).isEqualTo(expected)\n  }\n\n  private fun intArrayToByteArray(bytes: IntArray): ByteString {\n    val data = ByteArray(bytes.size)\n    for (i in bytes.indices) {\n      data[i] = bytes[i].toByte()\n    }\n    return ByteString.of(*data)\n  }\n\n  private fun readerHeaderTableLength(): Int = hpackReader!!.dynamicTable.size\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/Http2ConnectionTest.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.hasSize\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isGreaterThan\nimport assertk.assertions.isLessThan\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.io.EOFException\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicInteger\nimport kotlin.test.assertFailsWith\nimport okhttp3.Headers\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.TestUtil.headerEntries\nimport okhttp3.TestUtil.repeat\nimport okhttp3.internal.EMPTY_BYTE_ARRAY\nimport okhttp3.internal.concurrent.Lockable\nimport okhttp3.internal.concurrent.TaskFaker\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.notifyAll\nimport okhttp3.internal.concurrent.wait\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.connection.asBufferedSocket\nimport okio.AsyncTimeout\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.Source\nimport okio.buffer\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.assertArrayEquals\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\n\n@Timeout(5)\n@Tag(\"Slow\")\nclass Http2ConnectionTest {\n  private val peer = MockHttp2Peer()\n  private val taskFaker = TaskFaker()\n\n  @AfterEach fun tearDown() {\n    peer.close()\n    taskFaker.close()\n  }\n\n  @Test fun serverPingsClientHttp2() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.sendFrame().ping(false, 2, 3)\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    connect(peer)\n\n    // Verify the peer received what was expected.\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n    assertThat(ping.streamId).isEqualTo(0)\n    assertThat(ping.payload1).isEqualTo(2)\n    assertThat(ping.payload2).isEqualTo(3)\n    assertThat(ping.ack).isTrue()\n  }\n\n  @Test fun peerHttp2ServerLowersInitialWindowSize() {\n    val initial = Settings()\n    initial[Settings.INITIAL_WINDOW_SIZE] = 1684\n    val shouldntImpactConnection = Settings()\n    shouldntImpactConnection[Settings.INITIAL_WINDOW_SIZE] = 3368\n    peer.sendFrame().settings(initial)\n    peer.acceptFrame() // ACK\n    peer.sendFrame().settings(shouldntImpactConnection)\n    peer.acceptFrame() // ACK 2\n    peer.acceptFrame() // HEADERS\n    peer.play()\n    val connection = connect(peer)\n\n    // Verify the peer received the second ACK.\n    val ackFrame = peer.takeFrame()\n    assertThat(ackFrame.type).isEqualTo(Http2.TYPE_SETTINGS)\n    assertThat(ackFrame.streamId).isEqualTo(0)\n    assertThat(ackFrame.ack).isTrue()\n\n    // This stream was created *after* the connection settings were adjusted.\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), false)\n    assertThat(connection.peerSettings.initialWindowSize).isEqualTo(3368)\n    // New Stream is has the most recent initial window size.\n    assertThat(stream.writeBytesTotal).isEqualTo(0L)\n    assertThat(stream.writeBytesMaximum).isEqualTo(3368L)\n  }\n\n  @Test fun peerHttp2ServerZerosCompressionTable() {\n    val client = false // Peer is server, so we are client.\n    val settings = Settings()\n    settings[Settings.HEADER_TABLE_SIZE] = 0\n    val connection = connectWithSettings(client, settings)\n\n    // Verify the peer's settings were read and applied.\n    assertThat(connection.peerSettings.headerTableSize).isEqualTo(0)\n    val writer = connection.writer\n    assertThat(writer.hpackWriter.dynamicTableByteCount).isEqualTo(0)\n    assertThat(writer.hpackWriter.headerTableSizeSetting).isEqualTo(0)\n  }\n\n  @Test fun peerHttp2ClientDisablesPush() {\n    val client = false // Peer is client, so we are server.\n    val settings = Settings()\n    settings[Settings.ENABLE_PUSH] = 0 // The peer client disables push.\n    val connection = connectWithSettings(client, settings)\n\n    // verify the peer's settings were read and applied.\n    assertThat(connection.peerSettings.getEnablePush(true)).isFalse()\n  }\n\n  @Test fun peerIncreasesMaxFrameSize() {\n    val newMaxFrameSize = 0x4001\n    val settings = Settings()\n    settings[Settings.MAX_FRAME_SIZE] = newMaxFrameSize\n    val connection = connectWithSettings(true, settings)\n\n    // verify the peer's settings were read and applied.\n    assertThat(connection.peerSettings.getMaxFrameSize(-1)).isEqualTo(newMaxFrameSize)\n    assertThat(connection.writer.maxDataLength()).isEqualTo(newMaxFrameSize)\n  }\n\n  /**\n   * Webservers may set the initial window size to zero, which is a special case because it means\n   * that we have to flush headers immediately before any request body can be sent.\n   * https://github.com/square/okhttp/issues/2543\n   */\n  @Test fun peerSetsZeroFlowControl() {\n    peer.setClient(true)\n\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings().set(Settings.INITIAL_WINDOW_SIZE, 0))\n    peer.acceptFrame() // ACK\n    peer.sendFrame().windowUpdate(0, 10) // Increase the connection window size.\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.acceptFrame() // HEADERS STREAM 3\n    peer.sendFrame().windowUpdate(3, 5)\n    peer.acceptFrame() // DATA STREAM 3 \"abcde\"\n    peer.sendFrame().windowUpdate(3, 5)\n    peer.acceptFrame() // DATA STREAM 3 \"fghi\"\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.writePingAndAwaitPong() // Ensure the SETTINGS have been received.\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), true)\n    val sink = stream.sink.buffer()\n    sink.writeUtf8(\"abcdefghi\")\n    sink.flush()\n\n    // Verify the peer received what was expected.\n    peer.takeFrame() // PING\n    val headers = peer.takeFrame()\n    assertThat(headers.type).isEqualTo(Http2.TYPE_HEADERS)\n    val data1 = peer.takeFrame()\n    assertThat(data1.type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(data1.streamId).isEqualTo(3)\n    assertArrayEquals(\"abcde\".toByteArray(), data1.data)\n    val data2 = peer.takeFrame()\n    assertThat(data2.type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(data2.streamId).isEqualTo(3)\n    assertArrayEquals(\"fghi\".toByteArray(), data2.data)\n  }\n\n  /**\n   * Confirm that we account for discarded data frames. It's possible that data frames are in-flight\n   * just prior to us canceling a stream.\n   */\n  @Test fun discardedDataFramesAreCounted() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM 3\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"apple\"))\n    peer.sendFrame().data(false, 3, data(1024), 1024)\n    peer.acceptFrame() // RST_STREAM\n    peer.sendFrame().data(true, 3, data(1024), 1024)\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n    val connection = connect(peer)\n    val stream1 = connection.newStream(headerEntries(\"b\", \"bark\"), false)\n    val source = stream1.source\n    val buffer = Buffer()\n    while (buffer.size != 1024L) source.read(buffer, 1024)\n    stream1.close(ErrorCode.CANCEL, null)\n    val frame1 = peer.takeFrame()\n    assertThat(frame1.type).isEqualTo(Http2.TYPE_HEADERS)\n    val frame2 = peer.takeFrame()\n    assertThat(frame2.type).isEqualTo(Http2.TYPE_RST_STREAM)\n    val frame3 = peer.takeFrame()\n    assertThat(frame3.type).isEqualTo(Http2.TYPE_RST_STREAM)\n    assertThat(connection.readBytes.acknowledged).isEqualTo(0L)\n    assertThat(connection.readBytes.total).isEqualTo(2048L)\n  }\n\n  @Test fun receiveGoAwayHttp2() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM 3\n    peer.acceptFrame() // SYN_STREAM 5\n    peer.sendFrame().goAway(3, ErrorCode.PROTOCOL_ERROR, EMPTY_BYTE_ARRAY)\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.acceptFrame() // DATA STREAM 3\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream1 = connection.newStream(headerEntries(\"a\", \"android\"), true)\n    val stream2 = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    connection.writePingAndAwaitPong() // Ensure the GO_AWAY that resets stream2 has been received.\n    val sink1 = stream1.sink.buffer()\n    val sink2 = stream2.sink.buffer()\n    sink1.writeUtf8(\"abc\")\n    assertFailsWith<IOException> {\n      sink2.writeUtf8(\"abc\")\n      sink2.flush()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream was reset: REFUSED_STREAM\")\n    }\n    sink1.writeUtf8(\"def\")\n    sink1.close()\n    assertFailsWith<ConnectionShutdownException> {\n      connection.newStream(headerEntries(\"c\", \"cola\"), true)\n    }\n    assertThat(stream1.isOpen).isTrue()\n    assertThat(stream2.isOpen).isFalse()\n    assertThat(connection.openStreamCount()).isEqualTo(1)\n\n    // Verify the peer received what was expected.\n    val synStream1 = peer.takeFrame()\n    assertThat(synStream1.type).isEqualTo(Http2.TYPE_HEADERS)\n    val synStream2 = peer.takeFrame()\n    assertThat(synStream2.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n    val data1 = peer.takeFrame()\n    assertThat(data1.type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(data1.streamId).isEqualTo(3)\n    assertArrayEquals(\"abcdef\".toByteArray(), data1.data)\n  }\n\n  @Test fun readSendsWindowUpdateHttp2() {\n    val windowSize = 100\n    val windowUpdateThreshold = 50\n\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    for (i in 0..2) {\n      // Send frames of summing to size 50, which is windowUpdateThreshold.\n      peer.sendFrame().data(false, 3, data(24), 24)\n      peer.sendFrame().data(false, 3, data(25), 25)\n      peer.sendFrame().data(false, 3, data(1), 1)\n      peer.acceptFrame() // connection WINDOW UPDATE\n      peer.acceptFrame() // stream WINDOW UPDATE\n    }\n    peer.sendFrame().data(true, 3, data(0), 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.okHttpSettings[Settings.INITIAL_WINDOW_SIZE] = windowSize\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertThat(stream.readBytes.acknowledged).isEqualTo(0L)\n    assertThat(stream.readBytes.total).isEqualTo(0L)\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    val source = stream.source\n    val buffer = Buffer()\n    buffer.writeAll(source)\n    assertThat(source.read(buffer, 1)).isEqualTo(-1)\n    assertThat(buffer.size).isEqualTo(150)\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    for (i in 0..2) {\n      val windowUpdateStreamIds: MutableList<Int?> = ArrayList(2)\n      for (j in 0..1) {\n        val windowUpdate = peer.takeFrame()\n        assertThat(windowUpdate.type).isEqualTo(Http2.TYPE_WINDOW_UPDATE)\n        windowUpdateStreamIds.add(windowUpdate.streamId)\n        assertThat(windowUpdate.windowSizeIncrement).isEqualTo(windowUpdateThreshold.toLong())\n      }\n      // connection\n      assertThat(windowUpdateStreamIds).contains(0)\n      // stream\n      assertThat(windowUpdateStreamIds).contains(3)\n    }\n  }\n\n  @Test fun serverSendsEmptyDataClientDoesntSendWindowUpdateHttp2() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().data(true, 3, data(0), 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val client = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertThat(client.source.read(Buffer(), 1)).isEqualTo(-1)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.frameCount()).isEqualTo(5)\n  }\n\n  @Test fun clientSendsEmptyDataServerDoesntSendWindowUpdateHttp2() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // DATA\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val client = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    val out = client.sink.buffer()\n    out.write(EMPTY_BYTE_ARRAY)\n    out.flush()\n    out.close()\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(peer.frameCount()).isEqualTo(5)\n  }\n\n  @Test fun maxFrameSizeHonored() {\n    val buff = ByteArray(peer.maxOutboundDataLength() + 1)\n    buff.fill('*'.code.toByte())\n\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // DATA\n    peer.acceptFrame() // DATA\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    val out = stream.sink.buffer()\n    out.write(buff)\n    out.flush()\n    out.close()\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    var data = peer.takeFrame()\n    assertThat(data.data!!.size).isEqualTo(peer.maxOutboundDataLength())\n    data = peer.takeFrame()\n    assertThat(data.data!!.size).isEqualTo(1)\n  }\n\n  @Test fun pushPromiseStream() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    val expectedRequestHeaders =\n      listOf(\n        Header(Header.TARGET_METHOD, \"GET\"),\n        Header(Header.TARGET_SCHEME, \"https\"),\n        Header(Header.TARGET_AUTHORITY, \"squareup.com\"),\n        Header(Header.TARGET_PATH, \"/cached\"),\n      )\n    peer.sendFrame().pushPromise(3, 2, expectedRequestHeaders)\n    val expectedResponseHeaders =\n      listOf(\n        Header(Header.RESPONSE_STATUS, \"200\"),\n      )\n    peer.sendFrame().headers(true, 2, expectedResponseHeaders)\n    peer.sendFrame().data(true, 3, data(0), 0)\n    peer.play()\n    val observer = RecordingPushObserver()\n\n    // Play it back.\n    val connection = connect(peer, observer, Http2Connection.Listener.REFUSE_INCOMING_STREAMS)\n    val client = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertThat(client.source.read(Buffer(), 1)).isEqualTo(-1)\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(observer.takeEvent()).isEqualTo(expectedRequestHeaders)\n    assertThat(observer.takeEvent()).isEqualTo(expectedResponseHeaders)\n  }\n\n  @Test fun doublePushPromise() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.sendFrame().pushPromise(3, 2, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // SYN_REPLY\n    peer.sendFrame().pushPromise(3, 2, headerEntries(\"b\", \"banana\"))\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.newStream(headerEntries(\"b\", \"banana\"), false)\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.takeFrame().errorCode).isEqualTo(ErrorCode.PROTOCOL_ERROR)\n  }\n\n  @Test fun pushPromiseStreamsAutomaticallyCancel() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer\n      .sendFrame()\n      .pushPromise(\n        streamId = 3,\n        promisedStreamId = 2,\n        requestHeaders =\n          listOf(\n            Header(Header.TARGET_METHOD, \"GET\"),\n            Header(Header.TARGET_SCHEME, \"https\"),\n            Header(Header.TARGET_AUTHORITY, \"squareup.com\"),\n            Header(Header.TARGET_PATH, \"/cached\"),\n          ),\n      )\n    peer\n      .sendFrame()\n      .headers(\n        outFinished = true,\n        streamId = 2,\n        headerBlock =\n          listOf(\n            Header(Header.RESPONSE_STATUS, \"200\"),\n          ),\n      )\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n\n    // Play it back.\n    connect(peer, PushObserver.CANCEL, Http2Connection.Listener.REFUSE_INCOMING_STREAMS)\n\n    // Verify the peer received what was expected.\n    val rstStream = peer.takeFrame()\n    assertThat(rstStream.type).isEqualTo(Http2.TYPE_RST_STREAM)\n    assertThat(rstStream.streamId).isEqualTo(2)\n    assertThat(rstStream.errorCode).isEqualTo(ErrorCode.CANCEL)\n  }\n\n  /**\n   * When writing a set of headers fails due to an `IOException`, make sure the writer is left\n   * in a consistent state so the next writer also gets an `IOException` also instead of\n   * something worse (like an [IllegalStateException].\n   *\n   *\n   * See https://github.com/square/okhttp/issues/1651\n   */\n  @Test fun socketExceptionWhileWritingHeaders() {\n    peer.acceptFrame() // SYN_STREAM.\n    peer.play()\n    val longString = repeat('a', Http2.INITIAL_MAX_FRAME_SIZE + 1)\n    val socket = peer.openSocket()\n    val connection =\n      Http2Connection\n        .Builder(true, TaskRunner.INSTANCE)\n        .socket(socket.asBufferedSocket(), \"peer\")\n        .pushObserver(IGNORE)\n        .build()\n    connection.start(sendConnectionPreface = false)\n    socket.shutdownOutput()\n    assertFailsWith<IOException> {\n      connection.newStream(headerEntries(\"a\", longString), false)\n    }\n    assertFailsWith<IOException> {\n      connection.newStream(headerEntries(\"b\", longString), false)\n    }\n  }\n\n  @Test fun clientCreatesStreamAndServerReplies() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // DATA\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().data(true, 3, Buffer().writeUtf8(\"robot\"), 5)\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PING\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    val out = stream.sink.buffer()\n    out.writeUtf8(\"c3po\")\n    out.close()\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    assertStreamData(\"robot\", stream.source)\n    connection.writePingAndAwaitPong()\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.outFinished).isFalse()\n    assertThat(synStream.streamId).isEqualTo(3)\n    assertThat(synStream.associatedStreamId).isEqualTo(-1)\n    assertThat(synStream.headerBlock).isEqualTo(headerEntries(\"b\", \"banana\"))\n    val requestData = peer.takeFrame()\n    assertArrayEquals(\"c3po\".toByteArray(), requestData.data)\n  }\n\n  @Test fun serverFinishesStreamWithHeaders() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(true, 3, headerEntries(\"headers\", \"bam\"))\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"artichaut\"), false)\n    connection.writePingAndAwaitPong()\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"headers\", \"bam\"))\n    assertThat(stream.peekTrailers()).isEqualTo(Headers.EMPTY)\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.outFinished).isFalse()\n    assertThat(synStream.streamId).isEqualTo(3)\n    assertThat(synStream.associatedStreamId).isEqualTo(-1)\n    assertThat(synStream.headerBlock).isEqualTo(headerEntries(\"a\", \"artichaut\"))\n  }\n\n  @Test fun serverWritesTrailersAndClientReadsTrailers() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"headers\", \"bam\"))\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(true, 3, headerEntries(\"trailers\", \"boom\"))\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"artichaut\"), false)\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"headers\", \"bam\"))\n    connection.writePingAndAwaitPong()\n    assertThat(stream.peekTrailers()).isEqualTo(headersOf(\"trailers\", \"boom\"))\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.outFinished).isFalse()\n    assertThat(synStream.streamId).isEqualTo(3)\n    assertThat(synStream.associatedStreamId).isEqualTo(-1)\n    assertThat(synStream.headerBlock).isEqualTo(headerEntries(\"a\", \"artichaut\"))\n  }\n\n  /** A server RST_STREAM shouldn't prevent the client from consuming the response body.  */\n  @Test fun serverResponseBodyRstStream() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().data(true, 3, Buffer().writeUtf8(\"robot\"), 5)\n    peer.sendFrame().rstStream(3, ErrorCode.NO_ERROR)\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(), false)\n    connection.writePingAndAwaitPong()\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    val source = stream.source.buffer()\n    assertThat(source.readUtf8(5)).isEqualTo(\"robot\")\n    stream.sink.close()\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  /** A server RST_STREAM shouldn't prevent the client from consuming trailers.  */\n  @Test fun serverTrailersRstStream() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().headers(true, 3, headerEntries(\"z\", \"zebra\"))\n    peer.sendFrame().rstStream(3, ErrorCode.NO_ERROR)\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(), true)\n    connection.writePingAndAwaitPong()\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    stream.sink.close()\n    assertThat(stream.peekTrailers()).isEqualTo(headersOf(\"z\", \"zebra\"))\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  /**\n   * A server RST_STREAM shouldn't prevent the client from consuming the response body, even if it\n   * follows a truncated request body.\n   */\n  @Test fun clientRequestBodyServerResponseBodyRstStream() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().data(true, 3, Buffer().writeUtf8(\"robot\"), 5)\n    peer.sendFrame().rstStream(3, ErrorCode.NO_ERROR)\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(), true)\n    connection.writePingAndAwaitPong()\n    val sink = stream.sink.buffer()\n    sink.writeUtf8(\"abc\")\n    assertFailsWith<StreamResetException> {\n      sink.close()\n    }.also { expected ->\n      assertThat(expected.errorCode).isEqualTo(ErrorCode.NO_ERROR)\n    }\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    val source = stream.source.buffer()\n    assertThat(source.readUtf8(5)).isEqualTo(\"robot\")\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  @Test fun serverWritesTrailersWithData() {\n    // We buffer some outbound data and headers and confirm that the END_STREAM flag comes with the\n    // headers (and not with the data).\n\n    // Write the mocking script. for the client\n    peer.setClient(true)\n\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.sendFrame().headers(true, 3, headerEntries(\"client\", \"abc\"))\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // HEADERS STREAM 3\n    peer.acceptFrame() // DATA STREAM 3 \"abcde\"\n    peer.acceptFrame() // HEADERS STREAM 3\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), true)\n    stream.enqueueTrailers(headersOf(\"foo\", \"bar\"))\n    val sink = stream.sink.buffer()\n    sink.writeUtf8(\"abcdefghi\")\n    sink.close()\n\n    // Verify the peer received what was expected.\n    val headers1 = peer.takeFrame()\n    assertThat(headers1.type).isEqualTo(Http2.TYPE_HEADERS)\n    val data1 = peer.takeFrame()\n    assertThat(data1.type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(data1.streamId).isEqualTo(3)\n    assertArrayEquals(\"abcdefghi\".toByteArray(), data1.data)\n    assertThat(data1.inFinished).isFalse()\n    val headers2 = peer.takeFrame()\n    assertThat(headers2.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(headers2.inFinished).isTrue()\n  }\n\n  @Test fun clientCannotReadTrailersWithoutExhaustingStream() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().data(false, 3, Buffer().writeUtf8(\"robot\"), 5)\n    peer.sendFrame().headers(true, 3, headerEntries(\"trailers\", \"boom\"))\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"artichaut\"), true)\n    connection.writePingAndAwaitPong()\n    assertThat(stream.peekTrailers()).isNull()\n  }\n\n  @Test fun clientCannotReadTrailersIfTheStreamFailed() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().rstStream(3, ErrorCode.PROTOCOL_ERROR)\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"artichaut\"), true)\n    connection.writePingAndAwaitPong()\n    assertFailsWith<StreamResetException> {\n      stream.peekTrailers()\n    }\n  }\n\n  @Test fun serverCannotEnqueueTrailersAfterFinishingTheStream() {\n    peer.setClient(true)\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.writePingAndAwaitPong()\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), true)\n    // finish the stream\n    stream.writeHeaders(headerEntries(\"b\", \"berserk\"), true, false)\n    assertFailsWith<IllegalStateException> {\n      stream.enqueueTrailers(headersOf(\"trailers\", \"boom\"))\n    }\n  }\n\n  @Test fun noTrailersFrameYieldsEmptyTrailers() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"headers\", \"bam\"))\n    peer.sendFrame().data(true, 3, Buffer().writeUtf8(\"robot\"), 5)\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"artichaut\"), false)\n    val source = stream.source.buffer()\n    connection.writePingAndAwaitPong()\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"headers\", \"bam\"))\n    assertThat(source.readUtf8(5)).isEqualTo(\"robot\")\n    assertThat(stream.peekTrailers()).isEqualTo(Headers.EMPTY)\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.outFinished).isFalse()\n    assertThat(synStream.streamId).isEqualTo(3)\n    assertThat(synStream.associatedStreamId).isEqualTo(-1)\n    assertThat(synStream.headerBlock).isEqualTo(headerEntries(\"a\", \"artichaut\"))\n  }\n\n  @Test fun serverReadsHeadersDataHeaders() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // DATA\n    peer.acceptFrame() // HEADERS\n    peer.sendFrame().headers(true, 3, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PING\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    val out = stream.sink.buffer()\n    out.writeUtf8(\"c3po\")\n    out.close()\n    stream.writeHeaders(headerEntries(\"e\", \"elephant\"), false, false)\n    connection.writePingAndAwaitPong()\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.outFinished).isFalse()\n    assertThat(synStream.streamId).isEqualTo(3)\n    assertThat(synStream.associatedStreamId).isEqualTo(-1)\n    assertThat(synStream.headerBlock).isEqualTo(headerEntries(\"b\", \"banana\"))\n    val requestData = peer.takeFrame()\n    assertArrayEquals(\"c3po\".toByteArray(), requestData.data)\n    val nextFrame = peer.takeFrame()\n    assertThat(nextFrame.headerBlock).isEqualTo(headerEntries(\"e\", \"elephant\"))\n  }\n\n  @Test fun clientCreatesStreamAndServerRepliesWithFin() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(true, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertThat(connection.openStreamCount()).isEqualTo(1)\n    connection.writePingAndAwaitPong() // Ensure that the SYN_REPLY has been received.\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  @Test fun serverPingsClient() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.sendFrame().ping(false, 2, 0)\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    connect(peer)\n\n    // Verify the peer received what was expected.\n    val ping = peer.takeFrame()\n    assertThat(ping.streamId).isEqualTo(0)\n    assertThat(ping.payload1).isEqualTo(2)\n    assertThat(ping.payload2).isEqualTo(0)\n    assertThat(ping.ack).isTrue()\n  }\n\n  @Test fun clientPingsServer() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 5)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val pingAtNanos = System.nanoTime()\n    connection.writePingAndAwaitPong()\n    val elapsedNanos = System.nanoTime() - pingAtNanos\n    assertThat(elapsedNanos).isGreaterThan(0L)\n    assertThat(elapsedNanos).isLessThan(TimeUnit.SECONDS.toNanos(1))\n\n    // Verify the peer received what was expected.\n    val pingFrame = peer.takeFrame()\n    assertThat(pingFrame.type).isEqualTo(Http2.TYPE_PING)\n    assertThat(pingFrame.streamId).isEqualTo(0)\n    assertThat(pingFrame.payload1).isEqualTo(Http2Connection.AWAIT_PING)\n    assertThat(pingFrame.payload2).isEqualTo(0x4f4b6f6b) // OKok.\n    assertThat(pingFrame.ack).isFalse()\n  }\n\n  @Test fun unexpectedPongIsNotReturned() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.sendFrame().ping(false, 2, 0)\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, 99, 0) // This pong is silently ignored.\n    peer.sendFrame().ping(false, 4, 0)\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    connect(peer)\n\n    // Verify the peer received what was expected.\n    val ping2 = peer.takeFrame()\n    assertThat(ping2.payload1).isEqualTo(2)\n    val ping4 = peer.takeFrame()\n    assertThat(ping4.payload1).isEqualTo(4)\n  }\n\n  @Test fun serverSendsSettingsToClient() {\n    // Write the mocking script.\n    val settings = Settings()\n    settings[Settings.MAX_CONCURRENT_STREAMS] = 10\n    peer.sendFrame().settings(settings)\n    peer.acceptFrame() // ACK\n    peer.sendFrame().ping(false, 2, 0)\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    val maxConcurrentStreamsUpdated = CountDownLatch(1)\n    val maxConcurrentStreams = AtomicInteger()\n    val listener: Http2Connection.Listener =\n      object : Http2Connection.Listener() {\n        override fun onStream(stream: Http2Stream): Unit = throw AssertionError()\n\n        override fun onSettings(\n          connection: Http2Connection,\n          settings: Settings,\n        ) {\n          maxConcurrentStreams.set(settings.getMaxConcurrentStreams())\n          maxConcurrentStreamsUpdated.countDown()\n        }\n      }\n    val connection = connect(peer, IGNORE, listener)\n    connection.withLock {\n      assertThat(connection.peerSettings.getMaxConcurrentStreams()).isEqualTo(10)\n    }\n    maxConcurrentStreamsUpdated.await()\n    assertThat(maxConcurrentStreams.get()).isEqualTo(10)\n  }\n\n  @Test fun multipleSettingsFramesAreMerged() {\n    // Write the mocking script.\n    val settings1 = Settings()\n    settings1[Settings.HEADER_TABLE_SIZE] = 10000\n    settings1[Settings.INITIAL_WINDOW_SIZE] = 20000\n    settings1[Settings.MAX_FRAME_SIZE] = 30000\n    peer.sendFrame().settings(settings1)\n    peer.acceptFrame() // ACK SETTINGS\n    val settings2 = Settings()\n    settings2[Settings.INITIAL_WINDOW_SIZE] = 40000\n    settings2[Settings.MAX_FRAME_SIZE] = 50000\n    settings2[Settings.MAX_CONCURRENT_STREAMS] = 60000\n    peer.sendFrame().settings(settings2)\n    peer.acceptFrame() // ACK SETTINGS\n    peer.sendFrame().ping(false, 2, 0)\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_SETTINGS)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_PING)\n    connection.withLock {\n      assertThat(connection.peerSettings.headerTableSize).isEqualTo(10000)\n      assertThat(connection.peerSettings.initialWindowSize).isEqualTo(40000)\n      assertThat(connection.peerSettings.getMaxFrameSize(-1)).isEqualTo(50000)\n      assertThat(connection.peerSettings.getMaxConcurrentStreams()).isEqualTo(60000)\n    }\n  }\n\n  @Test fun clearSettingsBeforeMerge() {\n    // Write the mocking script.\n    val settings1 = Settings()\n    settings1[Settings.HEADER_TABLE_SIZE] = 10000\n    settings1[Settings.INITIAL_WINDOW_SIZE] = 20000\n    settings1[Settings.MAX_FRAME_SIZE] = 30000\n    peer.sendFrame().settings(settings1)\n    peer.acceptFrame() // ACK\n    peer.sendFrame().ping(false, 2, 0)\n    peer.acceptFrame()\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n\n    // fake a settings frame with clear flag set.\n    val settings2 = Settings()\n    settings2[Settings.MAX_CONCURRENT_STREAMS] = 60000\n    connection.readerRunnable.applyAndAckSettings(true, settings2)\n    connection.withLock {\n      assertThat(connection.peerSettings.headerTableSize).isEqualTo(-1)\n      assertThat(connection.peerSettings.initialWindowSize)\n        .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE)\n      assertThat(connection.peerSettings.getMaxFrameSize(-1)).isEqualTo(-1)\n      assertThat(connection.peerSettings.getMaxConcurrentStreams()).isEqualTo(60000)\n    }\n  }\n\n  @Test fun bogusDataFrameDoesNotDisruptConnection() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.sendFrame().data(true, 41, Buffer().writeUtf8(\"bogus\"), 5)\n    peer.acceptFrame() // RST_STREAM\n    peer.sendFrame().ping(false, 2, 0)\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    connect(peer)\n\n    // Verify the peer received what was expected.\n    val rstStream = peer.takeFrame()\n    assertThat(rstStream.type).isEqualTo(Http2.TYPE_RST_STREAM)\n    assertThat(rstStream.streamId).isEqualTo(41)\n    assertThat(rstStream.errorCode).isEqualTo(ErrorCode.PROTOCOL_ERROR)\n    val ping = peer.takeFrame()\n    assertThat(ping.payload1).isEqualTo(2)\n  }\n\n  @Test fun bogusReplySilentlyIgnored() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.sendFrame().headers(false, 41, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().ping(false, 2, 0)\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    connect(peer)\n\n    // Verify the peer received what was expected.\n    val ping = peer.takeFrame()\n    assertThat(ping.payload1).isEqualTo(2)\n  }\n\n  @Test fun serverClosesClientOutputStream() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().rstStream(3, ErrorCode.CANCEL)\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), true)\n    val out = stream.sink.buffer()\n    connection.writePingAndAwaitPong() // Ensure that the RST_CANCEL has been received.\n    assertFailsWith<IOException> {\n      out.writeUtf8(\"square\")\n      out.flush()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream was reset: CANCEL\")\n    }\n    // Close throws because buffered data wasn't flushed.\n    assertFailsWith<IOException> {\n      out.close()\n    }\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.inFinished).isFalse()\n    assertThat(synStream.outFinished).isFalse()\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  /**\n   * Test that the client sends a RST_STREAM if doing so won't disrupt the output stream.\n   */\n  @Test fun clientClosesClientInputStream() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), false)\n    val source = stream.source\n    val out = stream.sink.buffer()\n    source.close()\n    assertFailsWith<IOException> {\n      source.read(Buffer(), 1)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream closed\")\n    }\n    assertFailsWith<IOException> {\n      out.writeUtf8(\"a\")\n      out.flush()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream finished\")\n    }\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.inFinished).isTrue()\n    assertThat(synStream.outFinished).isFalse()\n    val rstStream = peer.takeFrame()\n    assertThat(rstStream.type).isEqualTo(Http2.TYPE_RST_STREAM)\n    assertThat(rstStream.errorCode).isEqualTo(ErrorCode.CANCEL)\n  }\n\n  /**\n   * Test that the client doesn't send a RST_STREAM if doing so will disrupt the output stream.\n   */\n  @Test fun clientClosesClientInputStreamIfOutputStreamIsClosed() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // DATA\n    peer.acceptFrame() // DATA with FLAG_FIN\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), true)\n    val source = stream.source\n    val out = stream.sink.buffer()\n    source.close()\n    assertFailsWith<IOException> {\n      source.read(Buffer(), 1)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream closed\")\n    }\n    out.writeUtf8(\"square\")\n    out.flush()\n    out.close()\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.inFinished).isFalse()\n    assertThat(synStream.outFinished).isFalse()\n    val data = peer.takeFrame()\n    assertThat(data.type).isEqualTo(Http2.TYPE_DATA)\n    assertArrayEquals(\"square\".toByteArray(), data.data)\n    val fin = peer.takeFrame()\n    assertThat(fin.type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(fin.inFinished).isTrue()\n    assertThat(fin.outFinished).isFalse()\n    val rstStream = peer.takeFrame()\n    assertThat(rstStream.type).isEqualTo(Http2.TYPE_RST_STREAM)\n    assertThat(rstStream.errorCode).isEqualTo(ErrorCode.CANCEL)\n  }\n\n  @Test fun serverClosesClientInputStream() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"b\", \"banana\"))\n    peer.sendFrame().data(true, 3, Buffer().writeUtf8(\"square\"), 6)\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), false)\n    val source = stream.source\n    assertStreamData(\"square\", source)\n    connection.writePingAndAwaitPong() // Ensure that inFinished has been received.\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(synStream.inFinished).isTrue()\n    assertThat(synStream.outFinished).isFalse()\n  }\n\n  @Test fun remoteDoubleSynReply() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(false, 3, headerEntries(\"b\", \"banana\"))\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"c\", \"cola\"), false)\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    connection.writePingAndAwaitPong() // Ensure that the 2nd SYN REPLY has been received.\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  @Test fun remoteSendsDataAfterInFinished() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().data(true, 3, Buffer().writeUtf8(\"robot\"), 5)\n    peer.sendFrame().data(true, 3, Buffer().writeUtf8(\"c3po\"), 4)\n    peer.acceptFrame() // RST_STREAM\n    peer.sendFrame().ping(false, 2, 0) // Ping just to make sure the stream was fastforwarded.\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    assertStreamData(\"robot\", stream.source)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val rstStream = peer.takeFrame()\n    assertThat(rstStream.type).isEqualTo(Http2.TYPE_RST_STREAM)\n    assertThat(rstStream.streamId).isEqualTo(3)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n    assertThat(ping.payload1).isEqualTo(2)\n  }\n\n  @Test fun clientDoesNotLimitFlowControl() {\n    val dataLength = 16384\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"b\", \"banana\"))\n    peer.sendFrame().data(false, 3, Buffer().write(ByteArray(dataLength)), dataLength)\n    peer.sendFrame().data(false, 3, Buffer().write(ByteArray(dataLength)), dataLength)\n    peer.sendFrame().data(false, 3, Buffer().write(ByteArray(dataLength)), dataLength)\n    peer.sendFrame().data(false, 3, Buffer().write(ByteArray(dataLength)), dataLength)\n    peer.sendFrame().data(false, 3, Buffer().write(ByteArray(1)), 1)\n    peer.sendFrame().ping(false, 2, 0) // Ping just to make sure the stream was fastforwarded.\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), false)\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"b\", \"banana\"))\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n    assertThat(ping.payload1).isEqualTo(2)\n  }\n\n  @Test fun remoteSendsRefusedStreamBeforeReplyHeaders() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().rstStream(3, ErrorCode.REFUSED_STREAM)\n    peer.sendFrame().ping(false, 2, 0)\n    peer.acceptFrame() // PING\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), false)\n    assertFailsWith<IOException> {\n      stream.takeHeaders()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream was reset: REFUSED_STREAM\")\n    }\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n    assertThat(ping.payload1).isEqualTo(2)\n  }\n\n  @Test fun receiveGoAway() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM 1\n    peer.acceptFrame() // SYN_STREAM 3\n    peer.acceptFrame() // PING.\n    peer.sendFrame().goAway(3, ErrorCode.PROTOCOL_ERROR, EMPTY_BYTE_ARRAY)\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.acceptFrame() // DATA STREAM 1\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream1 = connection.newStream(headerEntries(\"a\", \"android\"), true)\n    val stream2 = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    connection.writePingAndAwaitPong() // Ensure the GO_AWAY that resets stream2 has been received.\n    val sink1 = stream1.sink.buffer()\n    val sink2 = stream2.sink.buffer()\n    sink1.writeUtf8(\"abc\")\n    assertFailsWith<IOException> {\n      sink2.writeUtf8(\"abc\")\n      sink2.flush()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream was reset: REFUSED_STREAM\")\n    }\n    sink1.writeUtf8(\"def\")\n    sink1.close()\n    assertFailsWith<ConnectionShutdownException> {\n      connection.newStream(headerEntries(\"c\", \"cola\"), false)\n    }\n    assertThat(stream1.isOpen).isTrue()\n    assertThat(stream2.isOpen).isFalse()\n    assertThat(connection.openStreamCount()).isEqualTo(1)\n\n    // Verify the peer received what was expected.\n    val synStream1 = peer.takeFrame()\n    assertThat(synStream1.type).isEqualTo(Http2.TYPE_HEADERS)\n    val synStream2 = peer.takeFrame()\n    assertThat(synStream2.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n    val data1 = peer.takeFrame()\n    assertThat(data1.type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(data1.streamId).isEqualTo(3)\n    assertArrayEquals(\"abcdef\".toByteArray(), data1.data)\n  }\n\n  @Test fun sendGoAway() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM 1\n    peer.acceptFrame() // GOAWAY\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(false, 2, headerEntries(\"b\", \"b\")) // Should be ignored!\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.newStream(headerEntries(\"a\", \"android\"), false)\n    connection.withLock {\n      if (!connection.isHealthy(System.nanoTime())) {\n        throw ConnectionShutdownException()\n      }\n    }\n    connection.writePing()\n    connection.shutdown(ErrorCode.PROTOCOL_ERROR)\n    assertThat(connection.openStreamCount()).isEqualTo(1)\n    connection.awaitPong() // Prevent the peer from exiting prematurely.\n\n    // Verify the peer received what was expected.\n    val synStream1 = peer.takeFrame()\n    assertThat(synStream1.type).isEqualTo(Http2.TYPE_HEADERS)\n    val pingFrame = peer.takeFrame()\n    assertThat(pingFrame.type).isEqualTo(Http2.TYPE_PING)\n    val goaway = peer.takeFrame()\n    assertThat(goaway.type).isEqualTo(Http2.TYPE_GOAWAY)\n    assertThat(goaway.streamId).isEqualTo(0)\n    assertThat(goaway.errorCode).isEqualTo(ErrorCode.PROTOCOL_ERROR)\n  }\n\n  @Test fun close() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // GOAWAY\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"a\", \"android\"), false)\n    assertThat(connection.openStreamCount()).isEqualTo(1)\n    connection.close()\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n    assertFailsWith<ConnectionShutdownException> {\n      connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    }\n    val sink = stream.sink.buffer()\n    assertFailsWith<IOException> {\n      sink.writeByte(0)\n      sink.flush()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream finished\")\n    }\n    assertFailsWith<IOException> {\n      stream.source.read(Buffer(), 1)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"stream was reset: CANCEL\")\n    }\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val goaway = peer.takeFrame()\n    assertThat(goaway.type).isEqualTo(Http2.TYPE_GOAWAY)\n    val rstStream = peer.takeFrame()\n    assertThat(rstStream.type).isEqualTo(Http2.TYPE_RST_STREAM)\n    assertThat(rstStream.streamId).isEqualTo(3)\n  }\n\n  @Test fun getResponseHeadersTimesOut() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    stream.readTimeout().timeout(500, TimeUnit.MILLISECONDS)\n    val startNanos = System.nanoTime()\n    assertFailsWith<InterruptedIOException> {\n      stream.takeHeaders()\n    }\n    val elapsedNanos = System.nanoTime() - startNanos\n    awaitWatchdogIdle()\n    // 200ms delta\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedNanos).toDouble())\n      .isCloseTo(500.0, 200.0)\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_RST_STREAM)\n  }\n\n  /**\n   * Confirm that the client times out if the server stalls after 3 bytes. After the timeout the\n   * connection is still considered healthy while we await the degraded pong. When that doesn't\n   * arrive the connection goes unhealthy.\n   */\n  @Test fun readTimesOut() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().data(false, 3, Buffer().writeUtf8(\"abc\"), 3)\n    peer.acceptFrame() // RST_STREAM\n    peer.acceptFrame() // DEGRADED PING\n    peer.acceptFrame() // AWAIT PING\n    peer.sendFrame().ping(true, Http2Connection.DEGRADED_PING, 1) // DEGRADED PONG\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // AWAIT PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    stream.readTimeout().timeout(500, TimeUnit.MILLISECONDS)\n    val source = stream.source.buffer()\n    source.require(3)\n    val startNanos = System.nanoTime()\n    assertFailsWith<InterruptedIOException> {\n      source.require(4)\n    }\n    val elapsedNanos = System.nanoTime() - startNanos\n    awaitWatchdogIdle()\n    // 200ms delta\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedNanos).toDouble())\n      .isCloseTo(500.0, 200.0)\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // When the timeout is sent the connection doesn't immediately go unhealthy.\n    assertThat(connection.isHealthy(System.nanoTime())).isTrue()\n\n    // But if the ping doesn't arrive, the connection goes unhealthy.\n    Thread.sleep(TimeUnit.NANOSECONDS.toMillis(Http2Connection.DEGRADED_PONG_TIMEOUT_NS.toLong()))\n    assertThat(connection.isHealthy(System.nanoTime())).isFalse()\n\n    // When a pong does arrive, the connection becomes healthy again.\n    connection.writePingAndAwaitPong()\n    assertThat(connection.isHealthy(System.nanoTime())).isTrue()\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_RST_STREAM)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_PING)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  @Test fun writeTimesOutAwaitingStreamWindow() {\n    // Set the peer's receive window to 5 bytes!\n    val peerSettings = Settings().set(Settings.INITIAL_WINDOW_SIZE, 5)\n\n    // Write the mocking script.\n    peer.sendFrame().settings(peerSettings)\n    peer.acceptFrame() // ACK SETTINGS\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // DATA\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.writePingAndAwaitPong() // Make sure settings have been received.\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    val sink = stream.sink\n    sink.write(Buffer().writeUtf8(\"abcde\"), 5)\n    stream.writeTimeout().timeout(500, TimeUnit.MILLISECONDS)\n    val startNanos = System.nanoTime()\n    sink.write(Buffer().writeUtf8(\"f\"), 1)\n    assertFailsWith<InterruptedIOException> {\n      sink.flush() // This will time out waiting on the write window.\n    }\n    val elapsedNanos = System.nanoTime() - startNanos\n    awaitWatchdogIdle()\n    // 200ms delta\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedNanos).toDouble())\n      .isCloseTo(500.0, 200.0)\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_PING)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_RST_STREAM)\n  }\n\n  @Test fun writeTimesOutAwaitingConnectionWindow() {\n    // Set the peer's receive window to 5 bytes. Give the stream 5 bytes back, so only the\n    // connection-level window is applicable.\n    val peerSettings = Settings().set(Settings.INITIAL_WINDOW_SIZE, 5)\n\n    // Write the mocking script.\n    peer.sendFrame().settings(peerSettings)\n    peer.acceptFrame() // ACK SETTINGS\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // PING\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.acceptFrame() // DATA\n    peer.acceptFrame() // RST_STREAM\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.writePingAndAwaitPong() // Make sure settings have been acked.\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    connection.writePingAndAwaitPong() // Make sure the window update has been received.\n    val sink = stream.sink\n    stream.writeTimeout().timeout(500, TimeUnit.MILLISECONDS)\n    sink.write(Buffer().writeUtf8(\"abcdef\"), 6)\n    val startNanos = System.nanoTime()\n    assertFailsWith<InterruptedIOException> {\n      sink.flush() // This will time out waiting on the write window.\n    }\n    val elapsedNanos = System.nanoTime() - startNanos\n    awaitWatchdogIdle()\n    // 200ms delta\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedNanos).toDouble())\n      .isCloseTo(500.0, 200.0)\n    assertThat(connection.openStreamCount()).isEqualTo(0)\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_PING)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_PING)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_RST_STREAM)\n  }\n\n  @Test fun outgoingWritesAreBatched() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // DATA\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n\n    // two outgoing writes\n    val sink = stream.sink\n    sink.write(Buffer().writeUtf8(\"abcde\"), 5)\n    sink.write(Buffer().writeUtf8(\"fghij\"), 5)\n    sink.close()\n\n    // verify the peer received one incoming frame\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    val data = peer.takeFrame()\n    assertThat(data.type).isEqualTo(Http2.TYPE_DATA)\n    assertArrayEquals(\"abcdefghij\".toByteArray(), data.data)\n    assertThat(data.inFinished).isTrue()\n  }\n\n  @Test fun headers() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // PING\n    peer\n      .sendFrame()\n      .headers(false, 3, headerEntries(Header.RESPONSE_STATUS_UTF8, \"HTTP/1.1 100\"))\n    peer\n      .sendFrame()\n      .headers(false, 3, headerEntries(Header.RESPONSE_STATUS_UTF8, \"HTTP/1.1 200\"))\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    connection.writePingAndAwaitPong() // Ensure that the HEADERS has been received.\n    assertThat(stream.takeHeaders())\n      .isEqualTo(headersOf(Header.RESPONSE_STATUS_UTF8, \"HTTP/1.1 100\"))\n    assertThat(stream.takeHeaders())\n      .isEqualTo(headersOf(Header.RESPONSE_STATUS_UTF8, \"HTTP/1.1 200\"))\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val ping = peer.takeFrame()\n    assertThat(ping.type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  @Test fun readMultipleSetsOfResponseHeaders() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // PING\n    peer.sendFrame().headers(true, 3, headerEntries(\"c\", \"cola\"))\n    peer.sendFrame().ping(true, Http2Connection.AWAIT_PING, 0) // PONG\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    stream.connection.flush()\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    connection.writePingAndAwaitPong()\n    assertThat(stream.peekTrailers()).isEqualTo(headersOf(\"c\", \"cola\"))\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_PING)\n  }\n\n  @Test fun readSendsWindowUpdate() {\n    val windowSize = 100\n    val windowUpdateThreshold = 50\n\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    for (i in 0..2) {\n      // Send frames of summing to size 50, which is windowUpdateThreshold.\n      peer.sendFrame().data(false, 3, data(24), 24)\n      peer.sendFrame().data(false, 3, data(25), 25)\n      peer.sendFrame().data(false, 3, data(1), 1)\n      peer.acceptFrame() // connection WINDOW UPDATE\n      peer.acceptFrame() // stream WINDOW UPDATE\n    }\n    peer.sendFrame().data(true, 3, data(0), 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    connection.okHttpSettings[Settings.INITIAL_WINDOW_SIZE] = windowSize\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertThat(stream.readBytes.acknowledged).isEqualTo(0L)\n    assertThat(stream.readBytes.total).isEqualTo(0L)\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    val source = stream.source\n    val buffer = Buffer()\n    buffer.writeAll(source)\n    assertThat(source.read(buffer, 1)).isEqualTo(-1)\n    assertThat(buffer.size).isEqualTo(150)\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    for (i in 0..2) {\n      val windowUpdateStreamIds: MutableList<Int?> = ArrayList(2)\n      for (j in 0..1) {\n        val windowUpdate = peer.takeFrame()\n        assertThat(windowUpdate.type).isEqualTo(Http2.TYPE_WINDOW_UPDATE)\n        windowUpdateStreamIds.add(windowUpdate.streamId)\n        assertThat(windowUpdate.windowSizeIncrement)\n          .isEqualTo(windowUpdateThreshold.toLong())\n      }\n      // connection\n      assertThat(windowUpdateStreamIds).contains(0)\n      // stream\n      assertThat(windowUpdateStreamIds).contains(3)\n    }\n  }\n\n  @Test fun serverSendsEmptyDataClientDoesntSendWindowUpdate() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().data(true, 3, data(0), 0)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val client = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertThat(client.source.read(Buffer(), 1)).isEqualTo(-1)\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.frameCount()).isEqualTo(5)\n  }\n\n  @Test fun clientSendsEmptyDataServerDoesntSendWindowUpdate() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.acceptFrame() // DATA\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val client = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    val out = client.sink.buffer()\n    out.write(EMPTY_BYTE_ARRAY)\n    out.flush()\n    out.close()\n\n    // Verify the peer received what was expected.\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_HEADERS)\n    assertThat(peer.takeFrame().type).isEqualTo(Http2.TYPE_DATA)\n    assertThat(peer.frameCount()).isEqualTo(5)\n  }\n\n  @Test fun testTruncatedDataFrame() {\n    // Write the mocking script.\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // ACK\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.sendFrame().data(false, 3, data(1024), 1024)\n    peer.truncateLastFrame(8 + 100)\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertThat(stream.takeHeaders()).isEqualTo(headersOf(\"a\", \"android\"))\n    val source = stream.source\n    assertFailsWith<EOFException> {\n      source.buffer().readByteString(101)\n    }\n  }\n\n  @Test fun blockedStreamDoesntStarveNewStream() {\n    val framesThatFillWindow =\n      roundUp(Settings.DEFAULT_INITIAL_WINDOW_SIZE, peer.maxOutboundDataLength())\n\n    // Write the mocking script. This accepts more data frames than necessary!\n    peer.sendFrame().settings(Settings())\n    peer.acceptFrame() // SETTINGS ACK\n    peer.acceptFrame() // SYN_STREAM on stream 1\n    for (i in 0 until framesThatFillWindow) {\n      peer.acceptFrame() // DATA on stream 1\n    }\n    peer.acceptFrame() // SYN_STREAM on stream 2\n    peer.acceptFrame() // DATA on stream 2\n    peer.play()\n\n    // Play it back.\n    val connection = connect(peer)\n    val stream1 = connection.newStream(headerEntries(\"a\", \"apple\"), true)\n    val out1 = stream1.sink.buffer()\n    out1.write(ByteArray(Settings.DEFAULT_INITIAL_WINDOW_SIZE))\n    out1.flush()\n\n    // Check that we've filled the window for both the stream and also the connection.\n    assertThat(connection.writeBytesTotal)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n    assertThat(connection.writeBytesMaximum)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n    assertThat(stream1.writeBytesTotal)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n    assertThat(stream1.writeBytesMaximum)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n\n    // receiving a window update on the connection will unblock new streams.\n    connection.readerRunnable.windowUpdate(0, 3)\n    assertThat(connection.writeBytesTotal)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n    assertThat(connection.writeBytesMaximum)\n      .isEqualTo((Settings.DEFAULT_INITIAL_WINDOW_SIZE + 3).toLong())\n    assertThat(stream1.writeBytesTotal)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n    assertThat(stream1.writeBytesMaximum)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n\n    // Another stream should be able to send data even though 1 is blocked.\n    val stream2 = connection.newStream(headerEntries(\"b\", \"banana\"), true)\n    val out2 = stream2.sink.buffer()\n    out2.writeUtf8(\"foo\")\n    out2.flush()\n    assertThat(connection.writeBytesTotal)\n      .isEqualTo((Settings.DEFAULT_INITIAL_WINDOW_SIZE + 3).toLong())\n    assertThat(connection.writeBytesMaximum)\n      .isEqualTo((Settings.DEFAULT_INITIAL_WINDOW_SIZE + 3).toLong())\n    assertThat(stream1.writeBytesTotal)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n    assertThat(stream1.writeBytesMaximum)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n    assertThat(stream2.writeBytesTotal).isEqualTo(3L)\n    assertThat(stream2.writeBytesMaximum)\n      .isEqualTo(Settings.DEFAULT_INITIAL_WINDOW_SIZE.toLong())\n  }\n\n  @Test fun remoteOmitsInitialSettings() {\n    // Write the mocking script. Note no SETTINGS frame is sent or acknowledged.\n    peer.acceptFrame() // SYN_STREAM\n    peer.sendFrame().headers(false, 3, headerEntries(\"a\", \"android\"))\n    peer.acceptFrame() // GOAWAY\n    peer.play()\n    val connection =\n      Http2Connection\n        .Builder(true, TaskRunner.INSTANCE)\n        .socket(peer.openSocket().asBufferedSocket(), \"peer\")\n        .build()\n    connection.start(sendConnectionPreface = false)\n    val stream = connection.newStream(headerEntries(\"b\", \"banana\"), false)\n    assertFailsWith<IOException> {\n      stream.takeHeaders()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Expected a SETTINGS frame but was HEADERS\")\n    }\n\n    // Verify the peer received what was expected.\n    val synStream = peer.takeFrame()\n    assertThat(synStream.type).isEqualTo(Http2.TYPE_HEADERS)\n    val goaway = peer.takeFrame()\n    assertThat(goaway.type).isEqualTo(Http2.TYPE_GOAWAY)\n    assertThat(goaway.errorCode).isEqualTo(ErrorCode.PROTOCOL_ERROR)\n  }\n\n  @Test fun connectionUsesTaskRunner() {\n    peer.acceptFrame() // SYN_STREAM.\n    peer.play()\n    val taskRunner = taskFaker.taskRunner\n    val connection =\n      Http2Connection\n        .Builder(true, taskRunner)\n        .socket(peer.openSocket().asBufferedSocket(), \"peer\")\n        .pushObserver(IGNORE)\n        .build()\n    connection.start(sendConnectionPreface = false)\n    val queues = taskRunner.activeQueues()\n    assertThat(queues).hasSize(1)\n  }\n\n  private fun data(byteCount: Int): Buffer = Buffer().write(ByteArray(byteCount))\n\n  private fun assertStreamData(\n    expected: String?,\n    source: Source?,\n  ) {\n    val actual = source!!.buffer().readUtf8()\n    assertThat(actual).isEqualTo(expected)\n  }\n\n  /**\n   * Returns true when all work currently in progress by the watchdog have completed. This method\n   * creates more work for the watchdog and waits for that work to be executed. When it is, we know\n   * work that preceded this call is complete.\n   */\n  private fun awaitWatchdogIdle() {\n    val latch = CountDownLatch(1)\n    val watchdogJob: AsyncTimeout =\n      object : AsyncTimeout() {\n        override fun timedOut() {\n          latch.countDown()\n        }\n      }\n    watchdogJob.deadlineNanoTime(System.nanoTime()) // Due immediately!\n    watchdogJob.enter()\n    latch.await()\n  }\n\n  private fun connectWithSettings(\n    client: Boolean,\n    settings: Settings?,\n  ): Http2Connection {\n    peer.setClient(client)\n    peer.sendFrame().settings(settings!!)\n    peer.acceptFrame() // ACK\n    peer.play()\n    return connect(peer)\n  }\n\n  /** Builds a new connection to `peer` with settings acked.  */\n  private fun connect(\n    peer: MockHttp2Peer,\n    pushObserver: PushObserver = IGNORE,\n    listener: Http2Connection.Listener = Http2Connection.Listener.REFUSE_INCOMING_STREAMS,\n  ): Http2Connection {\n    val connection =\n      Http2Connection\n        .Builder(true, TaskRunner.INSTANCE)\n        .socket(peer.openSocket().asBufferedSocket(), \"peer\")\n        .pushObserver(pushObserver)\n        .listener(listener)\n        .build()\n    connection.start(sendConnectionPreface = false)\n\n    // verify the peer received the ACK\n    val ackFrame = peer.takeFrame()\n    assertThat(ackFrame.type).isEqualTo(Http2.TYPE_SETTINGS)\n    assertThat(ackFrame.streamId).isEqualTo(0)\n    assertThat(ackFrame.ack).isTrue()\n    return connection\n  }\n\n  private class RecordingPushObserver :\n    PushObserver,\n    Lockable {\n    val events = mutableListOf<Any>()\n\n    @Synchronized fun takeEvent(): Any {\n      while (events.isEmpty()) {\n        wait()\n      }\n      return events.removeAt(0)\n    }\n\n    @Synchronized override fun onRequest(\n      streamId: Int,\n      requestHeaders: List<Header>,\n    ): Boolean {\n      assertThat(streamId).isEqualTo(2)\n      events.add(requestHeaders)\n      notifyAll()\n      return false\n    }\n\n    @Synchronized override fun onHeaders(\n      streamId: Int,\n      responseHeaders: List<Header>,\n      last: Boolean,\n    ): Boolean {\n      assertThat(streamId).isEqualTo(2)\n      assertThat(last).isTrue()\n      events.add(responseHeaders)\n      notifyAll()\n      return false\n    }\n\n    @Synchronized override fun onData(\n      streamId: Int,\n      source: BufferedSource,\n      byteCount: Int,\n      last: Boolean,\n    ): Boolean {\n      events.add(AssertionError(\"onData\"))\n      notifyAll()\n      return false\n    }\n\n    @Synchronized override fun onReset(\n      streamId: Int,\n      errorCode: ErrorCode,\n    ) {\n      events.add(AssertionError(\"onReset\"))\n      notifyAll()\n    }\n  }\n\n  companion object {\n    fun roundUp(\n      num: Int,\n      divisor: Int,\n    ): Int = (num + divisor - 1) / divisor\n\n    val IGNORE =\n      object : PushObserver {\n        override fun onRequest(\n          streamId: Int,\n          requestHeaders: List<Header>,\n        ) = false\n\n        override fun onHeaders(\n          streamId: Int,\n          responseHeaders: List<Header>,\n          last: Boolean,\n        ) = false\n\n        override fun onData(\n          streamId: Int,\n          source: BufferedSource,\n          byteCount: Int,\n          last: Boolean,\n        ): Boolean {\n          source.skip(byteCount.toLong())\n          return false\n        }\n\n        override fun onReset(\n          streamId: Int,\n          errorCode: ErrorCode,\n        ) {}\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/Http2Test.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.util.Arrays\nimport java.util.concurrent.atomic.AtomicInteger\nimport kotlin.math.pow\nimport kotlin.test.assertFailsWith\nimport okhttp3.TestUtil.headerEntries\nimport okhttp3.internal.EMPTY_BYTE_ARRAY\nimport okhttp3.internal.http2.Http2.FLAG_COMPRESSED\nimport okhttp3.internal.http2.Http2.FLAG_END_HEADERS\nimport okhttp3.internal.http2.Http2.FLAG_END_STREAM\nimport okhttp3.internal.http2.Http2.FLAG_NONE\nimport okhttp3.internal.http2.Http2.FLAG_PADDED\nimport okhttp3.internal.http2.Http2.FLAG_PRIORITY\nimport okhttp3.internal.http2.Http2.TYPE_GOAWAY\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.GzipSink\nimport okio.buffer\nimport org.junit.jupiter.api.Test\n\nclass Http2Test {\n  val frame = Buffer()\n  val reader = Http2Reader(frame, false)\n  val expectedStreamId = 15\n\n  @Test fun unknownFrameTypeSkipped() {\n    writeMedium(frame, 4) // has a 4-byte field\n    frame.writeByte(99) // type 99\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(expectedStreamId)\n    frame.writeInt(111111111) // custom data\n    reader.nextFrame(requireSettings = false, BaseTestHandler()) // Should not callback.\n  }\n\n  @Test fun onlyOneLiteralHeadersFrame() {\n    val sentHeaders = headerEntries(\"name\", \"value\")\n    val headerBytes = literalHeaders(sentHeaders)\n    writeMedium(frame, headerBytes.size.toInt())\n    frame.writeByte(Http2.TYPE_HEADERS)\n    frame.writeByte(FLAG_END_HEADERS or FLAG_END_STREAM)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeAll(headerBytes)\n\n    // Check writer sends the same bytes.\n    assertThat(sendHeaderFrames(true, sentHeaders)).isEqualTo(frame)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun headers(\n          inFinished: Boolean,\n          streamId: Int,\n          associatedStreamId: Int,\n          headerBlock: List<Header>,\n        ) {\n          assertThat(inFinished).isTrue()\n          assertThat(streamId).isEqualTo(expectedStreamId)\n          assertThat(associatedStreamId).isEqualTo(-1)\n          assertThat(headerBlock).isEqualTo(sentHeaders)\n        }\n      },\n    )\n  }\n\n  @Test fun headersWithPriority() {\n    val sentHeaders = headerEntries(\"name\", \"value\")\n    val headerBytes = literalHeaders(sentHeaders)\n    writeMedium(frame, (headerBytes.size + 5).toInt())\n    frame.writeByte(Http2.TYPE_HEADERS)\n    frame.writeByte(FLAG_END_HEADERS or FLAG_PRIORITY)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeInt(0) // Independent stream.\n    frame.writeByte(255) // Heaviest weight, zero-indexed.\n    frame.writeAll(headerBytes)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun priority(\n          streamId: Int,\n          streamDependency: Int,\n          weight: Int,\n          exclusive: Boolean,\n        ) {\n          assertThat(streamDependency).isEqualTo(0)\n          assertThat(weight).isEqualTo(256)\n          assertThat(exclusive).isFalse()\n        }\n\n        override fun headers(\n          inFinished: Boolean,\n          streamId: Int,\n          associatedStreamId: Int,\n          headerBlock: List<Header>,\n        ) {\n          assertThat(inFinished).isFalse()\n          assertThat(streamId).isEqualTo(expectedStreamId)\n          assertThat(associatedStreamId).isEqualTo(-1)\n          assertThat(headerBlock).isEqualTo(sentHeaders)\n        }\n      },\n    )\n  }\n\n  /** Headers are compressed, then framed.  */\n  @Test fun headersFrameThenContinuation() {\n    val sentHeaders = largeHeaders()\n    val headerBlock = literalHeaders(sentHeaders)\n\n    // Write the first headers frame.\n    writeMedium(frame, Http2.INITIAL_MAX_FRAME_SIZE)\n    frame.writeByte(Http2.TYPE_HEADERS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.write(headerBlock, Http2.INITIAL_MAX_FRAME_SIZE.toLong())\n\n    // Write the continuation frame, specifying no more frames are expected.\n    writeMedium(frame, headerBlock.size.toInt())\n    frame.writeByte(Http2.TYPE_CONTINUATION)\n    frame.writeByte(FLAG_END_HEADERS)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeAll(headerBlock)\n\n    // Check writer sends the same bytes.\n    assertThat(sendHeaderFrames(false, sentHeaders)).isEqualTo(frame)\n\n    // Reading the above frames should result in a concatenated headerBlock.\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun headers(\n          inFinished: Boolean,\n          streamId: Int,\n          associatedStreamId: Int,\n          headerBlock: List<Header>,\n        ) {\n          assertThat(inFinished).isFalse()\n          assertThat(streamId).isEqualTo(expectedStreamId)\n          assertThat(associatedStreamId).isEqualTo(-1)\n          assertThat(headerBlock).isEqualTo(sentHeaders)\n        }\n      },\n    )\n  }\n\n  @Test fun pushPromise() {\n    val expectedPromisedStreamId = 11\n    val pushPromise =\n      listOf(\n        Header(Header.TARGET_METHOD, \"GET\"),\n        Header(Header.TARGET_SCHEME, \"https\"),\n        Header(Header.TARGET_AUTHORITY, \"squareup.com\"),\n        Header(Header.TARGET_PATH, \"/\"),\n      )\n\n    // Write the push promise frame, specifying the associated stream ID.\n    val headerBytes = literalHeaders(pushPromise)\n    writeMedium(frame, (headerBytes.size + 4).toInt())\n    frame.writeByte(Http2.TYPE_PUSH_PROMISE)\n    frame.writeByte(Http2.FLAG_END_PUSH_PROMISE)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeInt(expectedPromisedStreamId and 0x7fffffff)\n    frame.writeAll(headerBytes)\n    assertThat(sendPushPromiseFrames(expectedPromisedStreamId, pushPromise)).isEqualTo(\n      frame,\n    )\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun pushPromise(\n          streamId: Int,\n          promisedStreamId: Int,\n          requestHeaders: List<Header>,\n        ) {\n          assertThat(streamId).isEqualTo(expectedStreamId)\n          assertThat(promisedStreamId).isEqualTo(expectedPromisedStreamId)\n          assertThat(requestHeaders).isEqualTo(pushPromise)\n        }\n      },\n    )\n  }\n\n  /** Headers are compressed, then framed.  */\n  @Test fun pushPromiseThenContinuation() {\n    val expectedPromisedStreamId = 11\n    val pushPromise = largeHeaders()\n\n    // Decoding the first header will cross frame boundaries.\n    val headerBlock = literalHeaders(pushPromise)\n\n    // Write the first headers frame.\n    writeMedium(frame, Http2.INITIAL_MAX_FRAME_SIZE)\n    frame.writeByte(Http2.TYPE_PUSH_PROMISE)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeInt(expectedPromisedStreamId and 0x7fffffff)\n    frame.write(headerBlock, (Http2.INITIAL_MAX_FRAME_SIZE - 4).toLong())\n\n    // Write the continuation frame, specifying no more frames are expected.\n    writeMedium(frame, headerBlock.size.toInt())\n    frame.writeByte(Http2.TYPE_CONTINUATION)\n    frame.writeByte(FLAG_END_HEADERS)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeAll(headerBlock)\n    assertThat(sendPushPromiseFrames(expectedPromisedStreamId, pushPromise)).isEqualTo(\n      frame,\n    )\n\n    // Reading the above frames should result in a concatenated headerBlock.\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun pushPromise(\n          streamId: Int,\n          promisedStreamId: Int,\n          requestHeaders: List<Header>,\n        ) {\n          assertThat(streamId).isEqualTo(expectedStreamId)\n          assertThat(promisedStreamId).isEqualTo(expectedPromisedStreamId)\n          assertThat(requestHeaders).isEqualTo(pushPromise)\n        }\n      },\n    )\n  }\n\n  @Test fun readRstStreamFrame() {\n    writeMedium(frame, 4)\n    frame.writeByte(Http2.TYPE_RST_STREAM)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeInt(ErrorCode.PROTOCOL_ERROR.httpCode)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun rstStream(\n          streamId: Int,\n          errorCode: ErrorCode,\n        ) {\n          assertThat(streamId).isEqualTo(expectedStreamId)\n          assertThat(errorCode).isEqualTo(ErrorCode.PROTOCOL_ERROR)\n        }\n      },\n    )\n  }\n\n  @Test fun readSettingsFrame() {\n    val reducedTableSizeBytes = 16\n    writeMedium(frame, 12) // 2 settings * 6 bytes (2 for the code and 4 for the value).\n    frame.writeByte(Http2.TYPE_SETTINGS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // Settings are always on the connection stream 0.\n    frame.writeShort(1) // SETTINGS_HEADER_TABLE_SIZE\n    frame.writeInt(reducedTableSizeBytes)\n    frame.writeShort(2) // SETTINGS_ENABLE_PUSH\n    frame.writeInt(0)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun settings(\n          clearPrevious: Boolean,\n          settings: Settings,\n        ) {\n          // No clearPrevious in HTTP/2.\n          assertThat(clearPrevious).isFalse()\n          assertThat(settings.headerTableSize).isEqualTo(reducedTableSizeBytes)\n          assertThat(settings.getEnablePush(true)).isFalse()\n        }\n      },\n    )\n  }\n\n  @Test fun readSettingsFrameInvalidPushValue() {\n    writeMedium(frame, 6) // 2 for the code and 4 for the value\n    frame.writeByte(Http2.TYPE_SETTINGS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // Settings are always on the connection stream 0.\n    frame.writeShort(2)\n    frame.writeInt(2)\n    assertFailsWith<IOException> {\n      reader.nextFrame(requireSettings = false, BaseTestHandler())\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1\")\n    }\n  }\n\n  @Test fun readSettingsFrameUnknownSettingId() {\n    writeMedium(frame, 6) // 2 for the code and 4 for the value\n    frame.writeByte(Http2.TYPE_SETTINGS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // Settings are always on the connection stream 0.\n    frame.writeShort(7) // old number for SETTINGS_INITIAL_WINDOW_SIZE\n    frame.writeInt(1)\n    val settingValue = AtomicInteger()\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun settings(\n          clearPrevious: Boolean,\n          settings: Settings,\n        ) {\n          settingValue.set(settings[7])\n        }\n      },\n    )\n    assertThat(1).isEqualTo(settingValue.toInt())\n  }\n\n  @Test fun readSettingsFrameExperimentalId() {\n    writeMedium(frame, 6) // 2 for the code and 4 for the value\n    frame.writeByte(Http2.TYPE_SETTINGS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // Settings are always on the connection stream 0.\n    frame.write(\"f000\".decodeHex()) // Id reserved for experimental use.\n    frame.writeInt(1)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun settings(\n          clearPrevious: Boolean,\n          settings: Settings,\n        ) {\n          // no-op\n        }\n      },\n    )\n  }\n\n  @Test fun readSettingsFrameNegativeWindowSize() {\n    writeMedium(frame, 6) // 2 for the code and 4 for the value\n    frame.writeByte(Http2.TYPE_SETTINGS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // Settings are always on the connection stream 0.\n    frame.writeShort(4) // SETTINGS_INITIAL_WINDOW_SIZE\n    frame.writeInt(Int.MIN_VALUE)\n    assertFailsWith<IOException> {\n      reader.nextFrame(requireSettings = false, BaseTestHandler())\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1\",\n      )\n    }\n  }\n\n  @Test fun readSettingsFrameNegativeFrameLength() {\n    writeMedium(frame, 6) // 2 for the code and 4 for the value\n    frame.writeByte(Http2.TYPE_SETTINGS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // Settings are always on the connection stream 0.\n    frame.writeShort(5) // SETTINGS_MAX_FRAME_SIZE\n    frame.writeInt(Int.MIN_VALUE)\n    assertFailsWith<IOException> {\n      reader.nextFrame(requireSettings = false, BaseTestHandler())\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: -2147483648\",\n      )\n    }\n  }\n\n  @Test fun readSettingsFrameTooShortFrameLength() {\n    writeMedium(frame, 6) // 2 for the code and 4 for the value\n    frame.writeByte(Http2.TYPE_SETTINGS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // Settings are always on the connection stream 0.\n    frame.writeShort(5) // SETTINGS_MAX_FRAME_SIZE\n    frame.writeInt(2.0.pow(14.0).toInt() - 1)\n    assertFailsWith<IOException> {\n      reader.nextFrame(requireSettings = false, BaseTestHandler())\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: 16383\")\n    }\n  }\n\n  @Test fun readSettingsFrameTooLongFrameLength() {\n    writeMedium(frame, 6) // 2 for the code and 4 for the value\n    frame.writeByte(Http2.TYPE_SETTINGS)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // Settings are always on the connection stream 0.\n    frame.writeShort(5) // SETTINGS_MAX_FRAME_SIZE\n    frame.writeInt(2.0.pow(24.0).toInt())\n    assertFailsWith<IOException> {\n      reader.nextFrame(requireSettings = false, BaseTestHandler())\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: 16777216\")\n    }\n  }\n\n  @Test fun pingRoundTrip() {\n    val expectedPayload1 = 7\n    val expectedPayload2 = 8\n    writeMedium(frame, 8) // length\n    frame.writeByte(Http2.TYPE_PING)\n    frame.writeByte(Http2.FLAG_ACK)\n    frame.writeInt(0) // connection-level\n    frame.writeInt(expectedPayload1)\n    frame.writeInt(expectedPayload2)\n\n    // Check writer sends the same bytes.\n    assertThat(sendPingFrame(true, expectedPayload1, expectedPayload2)).isEqualTo(frame)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun ping(\n          ack: Boolean,\n          payload1: Int,\n          payload2: Int,\n        ) {\n          assertThat(ack).isTrue()\n          assertThat(payload1).isEqualTo(expectedPayload1)\n          assertThat(payload2).isEqualTo(expectedPayload2)\n        }\n      },\n    )\n  }\n\n  @Test fun maxLengthDataFrame() {\n    val expectedData = ByteArray(Http2.INITIAL_MAX_FRAME_SIZE)\n    Arrays.fill(expectedData, 2.toByte())\n    writeMedium(frame, expectedData.size)\n    frame.writeByte(Http2.TYPE_DATA)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.write(expectedData)\n\n    // Check writer sends the same bytes.\n    assertThat(sendDataFrame(Buffer().write(expectedData))).isEqualTo(frame)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun data(\n          inFinished: Boolean,\n          streamId: Int,\n          source: BufferedSource,\n          length: Int,\n        ) {\n          assertThat(inFinished).isFalse()\n          assertThat(streamId).isEqualTo(expectedStreamId)\n          assertThat(length).isEqualTo(Http2.INITIAL_MAX_FRAME_SIZE)\n          val data = source.readByteString(length.toLong())\n          for (b in data.toByteArray()) {\n            assertThat(b).isEqualTo(2.toByte())\n          }\n        }\n      },\n    )\n  }\n\n  @Test fun dataFrameNotAssociateWithStream() {\n    val payload = byteArrayOf(0x01, 0x02)\n    writeMedium(frame, payload.size)\n    frame.writeByte(Http2.TYPE_DATA)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0)\n    frame.write(payload)\n    assertFailsWith<IOException> {\n      reader.nextFrame(requireSettings = false, BaseTestHandler())\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"PROTOCOL_ERROR: TYPE_DATA streamId == 0\")\n    }\n  }\n\n  /** We do not send SETTINGS_COMPRESS_DATA = 1, nor want to. Let's make sure we error.  */\n  @Test fun compressedDataFrameWhenSettingDisabled() {\n    val expectedData = ByteArray(Http2.INITIAL_MAX_FRAME_SIZE)\n    Arrays.fill(expectedData, 2.toByte())\n    val zipped = gzip(expectedData)\n    val zippedSize = zipped.size.toInt()\n    writeMedium(frame, zippedSize)\n    frame.writeByte(Http2.TYPE_DATA)\n    frame.writeByte(FLAG_COMPRESSED)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    zipped.readAll(frame)\n    assertFailsWith<IOException> {\n      reader.nextFrame(requireSettings = false, BaseTestHandler())\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA\")\n    }\n  }\n\n  @Test fun readPaddedDataFrame() {\n    val dataLength = 1123\n    val expectedData = ByteArray(dataLength)\n    Arrays.fill(expectedData, 2.toByte())\n    val paddingLength = 254\n    val padding = ByteArray(paddingLength)\n    Arrays.fill(padding, 0.toByte())\n    writeMedium(frame, dataLength + paddingLength + 1)\n    frame.writeByte(Http2.TYPE_DATA)\n    frame.writeByte(FLAG_PADDED)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeByte(paddingLength)\n    frame.write(expectedData)\n    frame.write(padding)\n    reader.nextFrame(requireSettings = false, assertData())\n    // Padding was skipped.\n    assertThat(frame.exhausted()).isTrue()\n  }\n\n  @Test fun readPaddedDataFrameZeroPadding() {\n    val dataLength = 1123\n    val expectedData = ByteArray(dataLength)\n    Arrays.fill(expectedData, 2.toByte())\n    writeMedium(frame, dataLength + 1)\n    frame.writeByte(Http2.TYPE_DATA)\n    frame.writeByte(FLAG_PADDED)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeByte(0)\n    frame.write(expectedData)\n    reader.nextFrame(requireSettings = false, assertData())\n  }\n\n  @Test fun readPaddedHeadersFrame() {\n    val paddingLength = 254\n    val padding = ByteArray(paddingLength)\n    Arrays.fill(padding, 0.toByte())\n    val headerBlock = literalHeaders(headerEntries(\"foo\", \"barrr\", \"baz\", \"qux\"))\n    writeMedium(frame, headerBlock.size.toInt() + paddingLength + 1)\n    frame.writeByte(Http2.TYPE_HEADERS)\n    frame.writeByte(FLAG_END_HEADERS or FLAG_PADDED)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeByte(paddingLength)\n    frame.writeAll(headerBlock)\n    frame.write(padding)\n    reader.nextFrame(requireSettings = false, assertHeaderBlock())\n    // Padding was skipped.\n    assertThat(frame.exhausted()).isTrue()\n  }\n\n  @Test fun readPaddedHeadersFrameZeroPadding() {\n    val headerBlock = literalHeaders(headerEntries(\"foo\", \"barrr\", \"baz\", \"qux\"))\n    writeMedium(frame, headerBlock.size.toInt() + 1)\n    frame.writeByte(Http2.TYPE_HEADERS)\n    frame.writeByte(FLAG_END_HEADERS or FLAG_PADDED)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeByte(0)\n    frame.writeAll(headerBlock)\n    reader.nextFrame(requireSettings = false, assertHeaderBlock())\n  }\n\n  /** Headers are compressed, then framed.  */\n  @Test fun readPaddedHeadersFrameThenContinuation() {\n    val paddingLength = 254\n    val padding = ByteArray(paddingLength)\n    Arrays.fill(padding, 0.toByte())\n\n    // Decoding the first header will cross frame boundaries.\n    val headerBlock = literalHeaders(headerEntries(\"foo\", \"barrr\", \"baz\", \"qux\"))\n\n    // Write the first headers frame.\n    writeMedium(frame, (headerBlock.size / 2).toInt() + paddingLength + 1)\n    frame.writeByte(Http2.TYPE_HEADERS)\n    frame.writeByte(FLAG_PADDED)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeByte(paddingLength)\n    frame.write(headerBlock, headerBlock.size / 2)\n    frame.write(padding)\n\n    // Write the continuation frame, specifying no more frames are expected.\n    writeMedium(frame, headerBlock.size.toInt())\n    frame.writeByte(Http2.TYPE_CONTINUATION)\n    frame.writeByte(FLAG_END_HEADERS)\n    frame.writeInt(expectedStreamId and 0x7fffffff)\n    frame.writeAll(headerBlock)\n    reader.nextFrame(requireSettings = false, assertHeaderBlock())\n    assertThat(frame.exhausted()).isTrue()\n  }\n\n  @Test fun tooLargeDataFrame() {\n    assertFailsWith<IllegalArgumentException> {\n      sendDataFrame(Buffer().write(ByteArray(0x1000000)))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"FRAME_SIZE_ERROR length > 16384: 16777216\")\n    }\n  }\n\n  @Test fun windowUpdateRoundTrip() {\n    val expectedWindowSizeIncrement: Long = 0x7fffffff\n    writeMedium(frame, 4) // length\n    frame.writeByte(Http2.TYPE_WINDOW_UPDATE)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(expectedStreamId)\n    frame.writeInt(expectedWindowSizeIncrement.toInt())\n\n    // Check writer sends the same bytes.\n    assertThat(windowUpdate(expectedWindowSizeIncrement)).isEqualTo(frame)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun windowUpdate(\n          streamId: Int,\n          windowSizeIncrement: Long,\n        ) {\n          assertThat(streamId).isEqualTo(expectedStreamId)\n          assertThat(windowSizeIncrement).isEqualTo(expectedWindowSizeIncrement)\n        }\n      },\n    )\n  }\n\n  @Test fun badWindowSizeIncrement() {\n    assertFailsWith<IllegalArgumentException> {\n      windowUpdate(0)\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: 0\")\n    }\n    assertFailsWith<IllegalArgumentException> {\n      windowUpdate(0x80000000L)\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: 2147483648\")\n    }\n  }\n\n  @Test fun goAwayWithoutDebugDataRoundTrip() {\n    val expectedError = ErrorCode.PROTOCOL_ERROR\n    writeMedium(frame, 8) // Without debug data there's only 2 32-bit fields.\n    frame.writeByte(TYPE_GOAWAY)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // connection-scope\n    frame.writeInt(expectedStreamId) // last good stream.\n    frame.writeInt(expectedError.httpCode)\n\n    // Check writer sends the same bytes.\n    assertThat(sendGoAway(expectedStreamId, expectedError, EMPTY_BYTE_ARRAY))\n      .isEqualTo(frame)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun goAway(\n          lastGoodStreamId: Int,\n          errorCode: ErrorCode,\n          debugData: ByteString,\n        ) {\n          assertThat(lastGoodStreamId).isEqualTo(expectedStreamId)\n          assertThat(errorCode).isEqualTo(expectedError)\n          assertThat(debugData.size).isEqualTo(0)\n        }\n      },\n    )\n  }\n\n  @Test fun goAwayWithDebugDataRoundTrip() {\n    val expectedError = ErrorCode.PROTOCOL_ERROR\n    val expectedData: ByteString = \"abcdefgh\".encodeUtf8()\n\n    // Compose the expected GOAWAY frame without debug data.\n    writeMedium(frame, 8 + expectedData.size)\n    frame.writeByte(TYPE_GOAWAY)\n    frame.writeByte(FLAG_NONE)\n    frame.writeInt(0) // connection-scope\n    frame.writeInt(0) // never read any stream!\n    frame.writeInt(expectedError.httpCode)\n    frame.write(expectedData.toByteArray())\n\n    // Check writer sends the same bytes.\n    assertThat(sendGoAway(0, expectedError, expectedData.toByteArray())).isEqualTo(frame)\n    reader.nextFrame(\n      requireSettings = false,\n      object : BaseTestHandler() {\n        override fun goAway(\n          lastGoodStreamId: Int,\n          errorCode: ErrorCode,\n          debugData: ByteString,\n        ) {\n          assertThat(lastGoodStreamId).isEqualTo(0)\n          assertThat(errorCode).isEqualTo(expectedError)\n          assertThat(debugData).isEqualTo(expectedData)\n        }\n      },\n    )\n  }\n\n  @Test fun frameSizeError() {\n    val writer = Http2Writer(Buffer(), true)\n    assertFailsWith<IllegalArgumentException> {\n      writer.frameHeader(0, 16777216, Http2.TYPE_DATA, FLAG_NONE)\n    }.also { expected ->\n      // TODO: real max is based on settings between 16384 and 16777215\n      assertThat(expected.message).isEqualTo(\"FRAME_SIZE_ERROR length > 16384: 16777216\")\n    }\n  }\n\n  @Test fun ackSettingsAppliesMaxFrameSize() {\n    val newMaxFrameSize = 16777215\n    val writer = Http2Writer(Buffer(), true)\n    writer.applyAndAckSettings(Settings().set(Settings.MAX_FRAME_SIZE, newMaxFrameSize))\n    assertThat(writer.maxDataLength()).isEqualTo(newMaxFrameSize)\n    writer.frameHeader(0, newMaxFrameSize, Http2.TYPE_DATA, FLAG_NONE)\n  }\n\n  @Test fun streamIdHasReservedBit() {\n    val writer = Http2Writer(Buffer(), true)\n    assertFailsWith<IllegalArgumentException> {\n      var streamId = 3\n      streamId = streamId or (1L shl 31).toInt() // set reserved bit\n      writer.frameHeader(streamId, Http2.INITIAL_MAX_FRAME_SIZE, Http2.TYPE_DATA, FLAG_NONE)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"reserved bit set: -2147483645\")\n    }\n  }\n\n  private fun literalHeaders(sentHeaders: List<Header>): Buffer {\n    val out = Buffer()\n    Hpack.Writer(out = out).writeHeaders(sentHeaders)\n    return out\n  }\n\n  private fun sendHeaderFrames(\n    outFinished: Boolean,\n    headers: List<Header>,\n  ): Buffer {\n    val out = Buffer()\n    Http2Writer(out, true).headers(outFinished, expectedStreamId, headers)\n    return out\n  }\n\n  private fun sendPushPromiseFrames(\n    streamId: Int,\n    headers: List<Header>,\n  ): Buffer {\n    val out = Buffer()\n    Http2Writer(out, true).pushPromise(expectedStreamId, streamId, headers)\n    return out\n  }\n\n  private fun sendPingFrame(\n    ack: Boolean,\n    payload1: Int,\n    payload2: Int,\n  ): Buffer {\n    val out = Buffer()\n    Http2Writer(out, true).ping(ack, payload1, payload2)\n    return out\n  }\n\n  private fun sendGoAway(\n    lastGoodStreamId: Int,\n    errorCode: ErrorCode,\n    debugData: ByteArray,\n  ): Buffer {\n    val out = Buffer()\n    Http2Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData)\n    return out\n  }\n\n  private fun sendDataFrame(data: Buffer): Buffer {\n    val out = Buffer()\n    Http2Writer(out, true).dataFrame(expectedStreamId, FLAG_NONE, data, data.size.toInt())\n    return out\n  }\n\n  private fun windowUpdate(windowSizeIncrement: Long): Buffer {\n    val out = Buffer()\n    Http2Writer(out, true).windowUpdate(expectedStreamId, windowSizeIncrement)\n    return out\n  }\n\n  private fun assertHeaderBlock(): Http2Reader.Handler =\n    object : BaseTestHandler() {\n      override fun headers(\n        inFinished: Boolean,\n        streamId: Int,\n        associatedStreamId: Int,\n        headerBlock: List<Header>,\n      ) {\n        assertThat(inFinished).isFalse()\n        assertThat(streamId).isEqualTo(expectedStreamId)\n        assertThat(associatedStreamId).isEqualTo(-1)\n        assertThat(headerBlock).isEqualTo(headerEntries(\"foo\", \"barrr\", \"baz\", \"qux\"))\n      }\n    }\n\n  private fun assertData(): Http2Reader.Handler =\n    object : BaseTestHandler() {\n      override fun data(\n        inFinished: Boolean,\n        streamId: Int,\n        source: BufferedSource,\n        length: Int,\n      ) {\n        assertThat(inFinished).isFalse()\n        assertThat(streamId).isEqualTo(expectedStreamId)\n        assertThat(length).isEqualTo(1123)\n        val data = source.readByteString(length.toLong())\n        for (b in data.toByteArray()) {\n          assertThat(b).isEqualTo(2.toByte())\n        }\n      }\n    }\n\n  private fun gzip(data: ByteArray): Buffer {\n    val buffer = Buffer()\n    GzipSink(buffer).buffer().write(data).close()\n    return buffer\n  }\n\n  /** Create a sufficiently large header set to overflow INITIAL_MAX_FRAME_SIZE bytes.  */\n  private fun largeHeaders(): List<Header> {\n    val nameValues = arrayOfNulls<String>(32)\n    val chars = CharArray(512)\n    var i = 0\n    while (i < nameValues.size) {\n      Arrays.fill(chars, i.toChar())\n      val string = String(chars)\n      nameValues[i++] = string\n      nameValues[i++] = string\n    }\n    return headerEntries(*nameValues)\n  }\n\n  private fun writeMedium(\n    sink: BufferedSink,\n    i: Int,\n  ) {\n    sink.writeByte(i ushr 16 and 0xff)\n    sink.writeByte(i ushr 8 and 0xff)\n    sink.writeByte(i and 0xff)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/HttpOverHttp2Test.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport app.cash.burst.Burst\nimport app.cash.burst.burstValues\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport assertk.fail\nimport java.io.IOException\nimport java.net.HttpURLConnection\nimport java.net.SocketTimeoutException\nimport java.time.Duration\nimport java.util.Arrays\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.Executors\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.SynchronousQueue\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.TimeoutException\nimport java.util.concurrent.atomic.AtomicReference\nimport javax.net.ssl.SSLException\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.Dispatcher\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.PushPromise\nimport mockwebserver3.QueueDispatcher\nimport mockwebserver3.RecordedRequest\nimport mockwebserver3.SocketEffect.CloseStream\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.SocketEffect.Stall\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Cache\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.Connection\nimport okhttp3.Cookie\nimport okhttp3.Credentials.basic\nimport okhttp3.EventListener\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.Interceptor\nimport okhttp3.MediaType\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Protocol\nimport okhttp3.RecordingCookieJar\nimport okhttp3.RecordingHostnameVerifier\nimport okhttp3.Request\nimport okhttp3.RequestBody\nimport okhttp3.Response\nimport okhttp3.Route\nimport okhttp3.TestLogHandler\nimport okhttp3.TestUtil.assumeNotWindows\nimport okhttp3.TestUtil.repeat\nimport okhttp3.TestUtil.threadFactory\nimport okhttp3.internal.DoubleInetAddressDns\nimport okhttp3.internal.RecordingOkAuthenticator\nimport okhttp3.internal.connection.RealConnection\nimport okhttp3.internal.discard\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.GzipSink\nimport okio.Path.Companion.toPath\nimport okio.buffer\nimport okio.fakefilesystem.FakeFileSystem\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.assertArrayEquals\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n/** Test how HTTP/2 interacts with HTTP features.  */\n@Timeout(60)\n@Flaky\n@Tag(\"Slow\")\n@Burst\nclass HttpOverHttp2Test(\n  val protocol: Protocol = burstValues(Protocol.H2_PRIOR_KNOWLEDGE, Protocol.HTTP_2),\n) {\n  @RegisterExtension\n  val platform: PlatformRule = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = configureClientTestRule()\n\n  @RegisterExtension\n  val testLogHandler: TestLogHandler = TestLogHandler(Http2::class.java)\n\n  // Flaky https://github.com/square/okhttp/issues/4632\n  // Flaky https://github.com/square/okhttp/issues/4633\n  private val handshakeCertificates: HandshakeCertificates =\n    platform.localhostHandshakeCertificates()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private lateinit var client: OkHttpClient\n  private val fileSystem: FakeFileSystem = FakeFileSystem()\n  private val cache: Cache = Cache(fileSystem, \"/tmp/cache\".toPath(), Long.MAX_VALUE)\n  private lateinit var scheme: String\n\n  private fun configureClientTestRule(): OkHttpClientTestRule {\n    val clientTestRule = OkHttpClientTestRule()\n    clientTestRule.recordTaskRunner = true\n    return clientTestRule\n  }\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n    if (protocol === Protocol.HTTP_2) {\n      platform.assumeHttp2Support()\n      server.useHttps(handshakeCertificates.sslSocketFactory())\n      client =\n        clientTestRule\n          .newClientBuilder()\n          .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))\n          .sslSocketFactory(\n            handshakeCertificates.sslSocketFactory(),\n            handshakeCertificates.trustManager,\n          ).hostnameVerifier(RecordingHostnameVerifier())\n          .build()\n      scheme = \"https\"\n    } else {\n      server.protocols = listOf(Protocol.H2_PRIOR_KNOWLEDGE)\n      client =\n        clientTestRule\n          .newClientBuilder()\n          .protocols(listOf(Protocol.H2_PRIOR_KNOWLEDGE))\n          .build()\n      scheme = \"http\"\n    }\n  }\n\n  @AfterEach fun tearDown() {\n//    TODO reenable after https://github.com/square/okhttp/issues/8206\n//    fileSystem.checkNoOpenFiles()\n    cache.close()\n\n    java.net.Authenticator.setDefault(null)\n  }\n\n  @Test\n  fun get() {\n    server.enqueue(MockResponse(body = \"ABCDE\"))\n    val call = client.newCall(Request(server.url(\"/foo\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABCDE\")\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.message).isEqualTo(\"\")\n    assertThat(response.protocol).isEqualTo(protocol)\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET /foo HTTP/2\")\n    assertThat(request.headers[\":scheme\"]).isEqualTo(scheme)\n    assertThat(request.headers[\":authority\"]).isEqualTo(\"${server.hostName}:${server.port}\")\n  }\n\n  @Test\n  fun get204Response() {\n    val responseWithoutBody =\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 204\")\n        .removeHeader(\"Content-Length\")\n        .build()\n    server.enqueue(responseWithoutBody)\n    val call = client.newCall(Request(server.url(\"/foo\")))\n    val response = call.execute()\n\n    // Body contains nothing.\n    assertThat(response.body.bytes().size).isEqualTo(0)\n    assertThat(response.body.contentLength()).isEqualTo(0)\n\n    // Content-Length header doesn't exist in a 204 response.\n    assertThat(response.header(\"content-length\")).isNull()\n    assertThat(response.code).isEqualTo(204)\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET /foo HTTP/2\")\n  }\n\n  @Test\n  fun head() {\n    val mockResponse =\n      MockResponse\n        .Builder()\n        .setHeader(\"Content-Length\", 5)\n        .status(\"HTTP/1.1 200\")\n        .build()\n    server.enqueue(mockResponse)\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .head()\n          .url(server.url(\"/foo\"))\n          .build(),\n      )\n    val response = call.execute()\n\n    // Body contains nothing.\n    assertThat(response.body.bytes().size).isEqualTo(0)\n    assertThat(response.body.contentLength()).isEqualTo(0)\n\n    // Content-Length header stays correctly.\n    assertThat(response.header(\"content-length\")).isEqualTo(\"5\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"HEAD /foo HTTP/2\")\n  }\n\n  @Test\n  fun emptyResponse() {\n    server.enqueue(MockResponse())\n    val call = client.newCall(Request(server.url(\"/foo\")))\n    val response = call.execute()\n    assertThat(response.body.byteStream().read()).isEqualTo(-1)\n    response.body.close()\n  }\n\n  @Test\n  fun noDefaultContentLengthOnStreamingPost() {\n    val postBytes = \"FGHIJ\".toByteArray()\n    server.enqueue(MockResponse(body = \"ABCDE\"))\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/foo\"),\n          body =\n            object : RequestBody() {\n              override fun contentType(): MediaType = \"text/plain; charset=utf-8\".toMediaType()\n\n              override fun writeTo(sink: BufferedSink) {\n                sink.write(postBytes)\n              }\n            },\n        ),\n      )\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABCDE\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"POST /foo HTTP/2\")\n    assertArrayEquals(postBytes, request.body?.toByteArray())\n    assertThat(request.headers[\"Content-Length\"]).isNull()\n  }\n\n  @Test\n  fun userSuppliedContentLengthHeader() {\n    val postBytes = \"FGHIJ\".toByteArray()\n    server.enqueue(MockResponse(body = \"ABCDE\"))\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/foo\"),\n          body =\n            object : RequestBody() {\n              override fun contentType(): MediaType = \"text/plain; charset=utf-8\".toMediaType()\n\n              override fun contentLength(): Long = postBytes.size.toLong()\n\n              override fun writeTo(sink: BufferedSink) {\n                sink.write(postBytes)\n              }\n            },\n        ),\n      )\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABCDE\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"POST /foo HTTP/2\")\n    assertArrayEquals(postBytes, request.body?.toByteArray())\n    assertThat(request.headers[\"Content-Length\"]!!.toInt()).isEqualTo(postBytes.size)\n  }\n\n  @Test\n  fun closeAfterFlush() {\n    val postBytes = \"FGHIJ\".toByteArray()\n    server.enqueue(MockResponse(body = \"ABCDE\"))\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/foo\"),\n          body =\n            object : RequestBody() {\n              override fun contentType(): MediaType = \"text/plain; charset=utf-8\".toMediaType()\n\n              override fun contentLength(): Long = postBytes.size.toLong()\n\n              override fun writeTo(sink: BufferedSink) {\n                sink.write(postBytes) // push bytes into the stream's buffer\n                sink.flush() // Http2Connection.writeData subject to write window\n                sink.close() // Http2Connection.writeData empty frame\n              }\n            },\n        ),\n      )\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABCDE\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"POST /foo HTTP/2\")\n    assertArrayEquals(postBytes, request.body?.toByteArray())\n    assertThat(request.headers[\"Content-Length\"]!!.toInt()).isEqualTo(postBytes.size)\n  }\n\n  @Test\n  fun connectionReuse() {\n    server.enqueue(MockResponse(body = \"ABCDEF\"))\n    server.enqueue(MockResponse(body = \"GHIJKL\"))\n    val call1 = client.newCall(Request(server.url(\"/r1\")))\n    val call2 = client.newCall(Request(server.url(\"/r1\")))\n    val response1 = call1.execute()\n    val response2 = call2.execute()\n    assertThat(response1.body.source().readUtf8(3)).isEqualTo(\"ABC\")\n    assertThat(response2.body.source().readUtf8(3)).isEqualTo(\"GHI\")\n    assertThat(response1.body.source().readUtf8(3)).isEqualTo(\"DEF\")\n    assertThat(response2.body.source().readUtf8(3)).isEqualTo(\"JKL\")\n    val c0e0 = server.takeRequest()\n    assertThat(c0e0.connectionIndex).isEqualTo(0)\n    assertThat(c0e0.exchangeIndex).isEqualTo(0)\n    val c0e1 = server.takeRequest()\n    assertThat(c0e1.connectionIndex).isEqualTo(0)\n    assertThat(c0e1.exchangeIndex).isEqualTo(1)\n    response1.close()\n    response2.close()\n  }\n\n  @Test\n  fun connectionWindowUpdateAfterCanceling() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(Buffer().write(ByteArray(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE + 1)))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    waitForDataFrames(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE)\n\n    // Cancel the call and discard what we've buffered for the response body. This should free up\n    // the connection flow-control window so new requests can proceed.\n    call1.cancel()\n    assertThat(\n      response1.body.source().discard(1, TimeUnit.SECONDS),\n      \"Call should not have completed successfully.\",\n    ).isFalse()\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(\"abc\")\n  }\n\n  /** Wait for the client to receive `dataLength` DATA frames.  */\n  private fun waitForDataFrames(dataLength: Int) {\n    val expectedFrameCount = dataLength / 16384\n    var dataFrameCount = 0\n    while (dataFrameCount < expectedFrameCount) {\n      val log = testLogHandler.take()\n      if (log == \"FINE: << 0x00000003 16384 DATA          \") {\n        dataFrameCount++\n      }\n    }\n  }\n\n  @Test\n  fun connectionWindowUpdateOnClose() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(Buffer().write(ByteArray(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE + 1)))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    // Enqueue an additional response that show if we burnt a good prior response.\n    server.enqueue(\n      MockResponse(body = \"XXX\"),\n    )\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    waitForDataFrames(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE)\n\n    // Cancel the call and close the response body. This should discard the buffered data and update\n    // the connection flow-control window.\n    call1.cancel()\n    response1.close()\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun concurrentRequestWithEmptyFlowControlWindow() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(Buffer().write(ByteArray(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE)))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    waitForDataFrames(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE)\n    assertThat(response1.body.contentLength()).isEqualTo(\n      Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE.toLong(),\n    )\n    val read = response1.body.source().read(ByteArray(8192))\n    assertThat(read).isEqualTo(8192)\n\n    // Make a second call that should transmit the response headers. The response body won't be\n    // transmitted until the flow-control window is updated from the first request.\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.code).isEqualTo(200)\n\n    // Close the response body. This should discard the buffered data and update the connection\n    // flow-control window.\n    response1.close()\n    assertThat(response2.body.string()).isEqualTo(\"abc\")\n  }\n\n  /** https://github.com/square/okhttp/issues/373  */\n  @Test\n  @Disabled\n  fun synchronousRequest() {\n    server.enqueue(MockResponse(body = \"A\"))\n    server.enqueue(MockResponse(body = \"A\"))\n    val executor = Executors.newCachedThreadPool(threadFactory(\"HttpOverHttp2Test\"))\n    val countDownLatch = CountDownLatch(2)\n    executor.execute(AsyncRequest(\"/r1\", countDownLatch))\n    executor.execute(AsyncRequest(\"/r2\", countDownLatch))\n    countDownLatch.await()\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun gzippedResponseBody() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Content-Encoding: gzip\")\n        .body(gzip(\"ABCABCABC\"))\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/r1\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABCABCABC\")\n  }\n\n  @Test\n  fun authenticate() {\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_UNAUTHORIZED,\n        headers = headersOf(\"www-authenticate\", \"Basic realm=\\\"protected area\\\"\"),\n        body = \"Please authenticate.\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(body = \"Successful auth!\"),\n    )\n    val credential = basic(\"username\", \"password\")\n    client =\n      client\n        .newBuilder()\n        .authenticator(RecordingOkAuthenticator(credential, \"Basic\"))\n        .build()\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"Successful auth!\")\n    val denied = server.takeRequest()\n    assertThat(denied.headers[\"Authorization\"]).isNull()\n    val accepted = server.takeRequest()\n    assertThat(accepted.requestLine).isEqualTo(\"GET / HTTP/2\")\n    assertThat(accepted.headers[\"Authorization\"]).isEqualTo(credential)\n  }\n\n  @Test\n  fun redirect() {\n    server.enqueue(\n      MockResponse(\n        code = HttpURLConnection.HTTP_MOVED_TEMP,\n        headers = headersOf(\"Location\", \"/foo\"),\n        body = \"This page has moved!\",\n      ),\n    )\n    server.enqueue(MockResponse(body = \"This is the new location!\"))\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"This is the new location!\")\n    val request1 = server.takeRequest()\n    assertThat(request1.url.encodedPath).isEqualTo(\"/\")\n    val request2 = server.takeRequest()\n    assertThat(request2.url.encodedPath).isEqualTo(\"/foo\")\n  }\n\n  @Test\n  fun readAfterLastByte() {\n    server.enqueue(MockResponse(body = \"ABC\"))\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    val inputStream = response.body.byteStream()\n    assertThat(inputStream.read()).isEqualTo('A'.code)\n    assertThat(inputStream.read()).isEqualTo('B'.code)\n    assertThat(inputStream.read()).isEqualTo('C'.code)\n    assertThat(inputStream.read()).isEqualTo(-1)\n    assertThat(inputStream.read()).isEqualTo(-1)\n    inputStream.close()\n  }\n\n  @Test\n  fun readResponseHeaderTimeout() {\n    server.enqueue(MockResponse.Builder().onResponseStart(Stall).build())\n    server.enqueue(MockResponse(body = \"A\"))\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofSeconds(1))\n        .build()\n\n    // Make a call expecting a timeout reading the response headers.\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<SocketTimeoutException> {\n      call1.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n    }\n\n    // Confirm that a subsequent request on the same connection is not impacted.\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n\n    // Confirm that the connection was reused.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  /**\n   * Test to ensure we don't  throw a read timeout on responses that are progressing.  For this\n   * case, we take a 4KiB body and throttle it to 1KiB/second.  We set the read timeout to two\n   * seconds.  If our implementation is acting correctly, it will not throw, as it is progressing.\n   */\n  @Test\n  fun readTimeoutMoreGranularThanBodySize() {\n    val body = CharArray(4096) // 4KiB to read.\n    Arrays.fill(body, 'y')\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(String(body))\n        .throttleBody(1024, 1, TimeUnit.SECONDS) // Slow connection 1KiB/second.\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofSeconds(2))\n        .build()\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(String(body))\n  }\n\n  /**\n   * Test to ensure we throw a read timeout on responses that are progressing too slowly.  For this\n   * case, we take a 2KiB body and throttle it to 1KiB/second.  We set the read timeout to half a\n   * second.  If our implementation is acting correctly, it will throw, as a byte doesn't arrive in\n   * time.\n   */\n  @Test\n  fun readTimeoutOnSlowConnection() {\n    val body = repeat('y', 2048)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(body)\n        .throttleBody(1024, 1, TimeUnit.SECONDS)\n        .build(),\n    ) // Slow connection 1KiB/second.\n    server.enqueue(\n      MockResponse(body = body),\n    )\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(500)) // Half a second to read something.\n        .build()\n\n    // Make a call expecting a timeout reading the response body.\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    assertFailsWith<SocketTimeoutException> {\n      response1.body.string()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"timeout\")\n    }\n\n    // Confirm that a subsequent request on the same connection is not impacted.\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(body)\n\n    // Confirm that the connection was reused.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun connectionTimeout() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"A\")\n        .bodyDelay(1, TimeUnit.SECONDS)\n        .build(),\n    )\n    val client1 =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofSeconds(2))\n        .build()\n    val call1 =\n      client1\n        .newCall(\n          Request\n            .Builder()\n            .url(server.url(\"/\"))\n            .build(),\n        )\n    val client2 =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(200))\n        .build()\n    val call2 =\n      client2\n        .newCall(\n          Request\n            .Builder()\n            .url(server.url(\"/\"))\n            .build(),\n        )\n    val response1 = call1.execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertFailsWith<IOException> {\n      call2.execute()\n    }\n\n    // Confirm that the connection was reused.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun responsesAreCached() {\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"cache-control\", \"max-age=60\"),\n        body = \"A\",\n      ),\n    )\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(1)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(0)\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    val call3 = client.newCall(Request(server.url(\"/\")))\n    val response3 = call3.execute()\n    assertThat(response3.body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(3)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(2)\n  }\n\n  @Test\n  fun conditionalCache() {\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"ETag\", \"v1\"),\n        body = \"A\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(code = HttpURLConnection.HTTP_NOT_MODIFIED),\n    )\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    assertThat(response1.body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(1)\n    assertThat(cache.networkCount()).isEqualTo(1)\n    assertThat(cache.hitCount()).isEqualTo(0)\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(\"A\")\n    assertThat(cache.requestCount()).isEqualTo(2)\n    assertThat(cache.networkCount()).isEqualTo(2)\n    assertThat(cache.hitCount()).isEqualTo(1)\n  }\n\n  @Test\n  fun responseCachedWithoutConsumingFullBody() {\n    client =\n      client\n        .newBuilder()\n        .cache(cache)\n        .build()\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"cache-control\", \"max-age=60\"),\n        body = \"ABCD\",\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        headers = headersOf(\"cache-control\", \"max-age=60\"),\n        body = \"EFGH\",\n      ),\n    )\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    assertThat(response1.body.source().readUtf8(2)).isEqualTo(\"AB\")\n    response1.body.close()\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.source().readUtf8()).isEqualTo(\"ABCD\")\n    response2.body.close()\n  }\n\n  @Test\n  fun sendRequestCookies() {\n    val cookieJar = RecordingCookieJar()\n    val requestCookie =\n      Cookie\n        .Builder()\n        .name(\"a\")\n        .value(\"b\")\n        .domain(server.hostName)\n        .build()\n    cookieJar.enqueueRequestCookies(requestCookie)\n    client =\n      client\n        .newBuilder()\n        .cookieJar(cookieJar)\n        .build()\n    server.enqueue(MockResponse())\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"\")\n    val request = server.takeRequest()\n    assertThat(request.headers[\"Cookie\"]).isEqualTo(\"a=b\")\n  }\n\n  @Test\n  fun receiveResponseCookies() {\n    val cookieJar = RecordingCookieJar()\n    client =\n      client\n        .newBuilder()\n        .cookieJar(cookieJar)\n        .build()\n    server.enqueue(\n      MockResponse(headers = headersOf(\"set-cookie\", \"a=b\")),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"\")\n    cookieJar.assertResponseCookies(\"a=b; path=/\")\n  }\n\n  @Test\n  fun cancelWithStreamNotCompleted() {\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n\n    // Disconnect before the stream is created. A connection is still established!\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response = call1.execute()\n    call1.cancel()\n\n    // That connection is pooled, and it works.\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(\"def\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // Clean up the connection.\n    response.close()\n  }\n\n  @Test\n  fun noRecoveryFromOneRefusedStream() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n    val call = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<StreamResetException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.errorCode).isEqualTo(ErrorCode.REFUSED_STREAM)\n    }\n  }\n\n  @Test\n  fun recoverFromRefusedStreamWhenAnotherRouteExists() {\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns()) // Two routes!\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n\n    val request = Request(server.url(\"/\"))\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // Note that although we have two routes available, we only use one. The retry is permitted\n    // because there are routes available, but it chooses the existing connection since it isn't\n    // yet considered unhealthy.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun noRecoveryWhenRoutesExhausted() {\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns()) // Two routes!\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n\n    val request = Request(server.url(\"/\"))\n    assertFailsWith<StreamResetException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.errorCode).isEqualTo(ErrorCode.REFUSED_STREAM)\n    }\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0) // New connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1) // Pooled connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0) // New connection.\n  }\n\n  @Test\n  fun connectionWithOneRefusedStreamIsPooled() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n    val request = Request(server.url(\"/\"))\n\n    // First call fails because it only has one route.\n    assertFailsWith<StreamResetException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.errorCode).isEqualTo(ErrorCode.REFUSED_STREAM)\n    }\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // Second call succeeds on the pooled connection.\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun connectionWithTwoRefusedStreamsIsNotPooled() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n    server.enqueue(MockResponse(body = \"def\"))\n    val request = Request(server.url(\"/\"))\n\n    // First call makes a new connection and fails because it is the only route.\n    assertFailsWith<StreamResetException> {\n      client.newCall(request).execute()\n    }.also { expected ->\n      assertThat(expected.errorCode).isEqualTo(ErrorCode.REFUSED_STREAM)\n    }\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0) // New connection.\n\n    // Second call attempts the pooled connection, and it fails. Then it retries a new route which\n    // succeeds.\n    val response2 = client.newCall(request).execute()\n    assertThat(response2.body.string()).isEqualTo(\"abc\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1) // Pooled connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0) // New connection.\n\n    // Third call reuses the second connection.\n    val response3 = client.newCall(request).execute()\n    assertThat(response3.body.string()).isEqualTo(\"def\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1) // New connection.\n  }\n\n  /**\n   * We had a bug where we'd perform infinite retries of route that fail with connection shutdown\n   * errors. The problem was that the logic that decided whether to reuse a route didn't track\n   * certain HTTP/2 errors. https://github.com/square/okhttp/issues/5547\n   */\n  @Test\n  fun noRecoveryFromTwoRefusedStreams() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse(body = \"abc\"),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<StreamResetException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.errorCode).isEqualTo(ErrorCode.REFUSED_STREAM)\n    }\n  }\n\n  @Test\n  fun recoverFromOneInternalErrorRequiresNewConnection() {\n    recoverFromOneHttp2ErrorRequiresNewConnection(ErrorCode.INTERNAL_ERROR)\n  }\n\n  @Test\n  fun recoverFromOneCancelRequiresNewConnection() {\n    recoverFromOneHttp2ErrorRequiresNewConnection(ErrorCode.CANCEL)\n  }\n\n  private fun recoverFromOneHttp2ErrorRequiresNewConnection(errorCode: ErrorCode?) {\n    server.enqueue(\n      MockResponse.Builder().onRequestStart(CloseStream(errorCode!!.httpCode)).build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n\n    // New connection.\n    val c0e0 = server.takeRequest()\n    assertThat(c0e0.connectionIndex).isEqualTo(0)\n    assertThat(c0e0.exchangeIndex).isEqualTo(0)\n    // New connection.\n    val c1e0 = server.takeRequest()\n    assertThat(c1e0.connectionIndex).isEqualTo(1)\n    assertThat(c1e0.exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun recoverFromMultipleRefusedStreamsRequiresNewConnection() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.REFUSED_STREAM.httpCode))\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n\n    // New connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Reused connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    // New connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun recoverFromCancelReusesConnection() {\n    val responseDequeuedLatches =\n      listOf(\n        // No synchronization for the last request, which is not canceled:\n        CountDownLatch(1),\n        CountDownLatch(0),\n      )\n    val requestCanceledLatches =\n      listOf(\n        CountDownLatch(1),\n        CountDownLatch(0),\n      )\n    val dispatcher = RespondAfterCancelDispatcher(responseDequeuedLatches, requestCanceledLatches)\n    dispatcher.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(10, TimeUnit.SECONDS)\n        .body(\"abc\")\n        .build(),\n    )\n    dispatcher.enqueue(\n      MockResponse(body = \"def\"),\n    )\n    server.dispatcher = dispatcher\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n    callAndCancel(0, responseDequeuedLatches[0], requestCanceledLatches[0])\n\n    // Make a second request to ensure the connection is reused.\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"def\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  @Test\n  fun recoverFromMultipleCancelReusesConnection() {\n    val responseDequeuedLatches =\n      Arrays.asList(\n        CountDownLatch(1),\n        // No synchronization for the last request, which is not canceled:\n        CountDownLatch(1),\n        CountDownLatch(0),\n      )\n    val requestCanceledLatches =\n      Arrays.asList(\n        CountDownLatch(1),\n        CountDownLatch(1),\n        CountDownLatch(0),\n      )\n    val dispatcher = RespondAfterCancelDispatcher(responseDequeuedLatches, requestCanceledLatches)\n    dispatcher.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(10, TimeUnit.SECONDS)\n        .body(\"abc\")\n        .build(),\n    )\n    dispatcher.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(10, TimeUnit.SECONDS)\n        .body(\"def\")\n        .build(),\n    )\n    dispatcher.enqueue(\n      MockResponse(body = \"ghi\"),\n    )\n    server.dispatcher = dispatcher\n    client =\n      client\n        .newBuilder()\n        .dns(DoubleInetAddressDns())\n        .build()\n    callAndCancel(0, responseDequeuedLatches[0], requestCanceledLatches[0])\n    callAndCancel(1, responseDequeuedLatches[1], requestCanceledLatches[1])\n\n    // Make a third request to ensure the connection is reused.\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ghi\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  private class RespondAfterCancelDispatcher(\n    private val responseDequeuedLatches: List<CountDownLatch>,\n    private val requestCanceledLatches: List<CountDownLatch>,\n  ) : QueueDispatcher() {\n    private var responseIndex = 0\n\n    @Synchronized\n    override fun dispatch(request: RecordedRequest): MockResponse {\n      // This guarantees a deterministic sequence when handling the canceled request:\n      // 1. Server reads request and dequeues first response\n      // 2. Client cancels request\n      // 3. Server tries to send response on the canceled stream\n      // Otherwise, there is no guarantee for the sequence. For example, the server may use the\n      // first mocked response to respond to the second request.\n      val response = super.dispatch(request)\n      responseDequeuedLatches[responseIndex].countDown()\n      requestCanceledLatches[responseIndex].await()\n      responseIndex++\n      return response\n    }\n  }\n\n  /** Make a call and canceling it as soon as it's accepted by the server.  */\n  private fun callAndCancel(\n    expectedSequenceNumber: Int,\n    responseDequeuedLatch: CountDownLatch?,\n    requestCanceledLatch: CountDownLatch?,\n  ) {\n    val call = client.newCall(Request(server.url(\"/\")))\n    val latch = CountDownLatch(1)\n    call.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          latch.countDown()\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          fail(\"\")\n        }\n      },\n    )\n    assertThat(server.takeRequest().exchangeIndex)\n      .isEqualTo(expectedSequenceNumber)\n    responseDequeuedLatch!!.await()\n    call.cancel()\n    // Avoid flaky race conditions\n    Thread.sleep(100)\n    requestCanceledLatch!!.countDown()\n    latch.await()\n  }\n\n  @Test\n  fun noRecoveryFromRefusedStreamWithRetryDisabled() {\n    noRecoveryFromErrorWithRetryDisabled(ErrorCode.REFUSED_STREAM)\n  }\n\n  @Test\n  fun noRecoveryFromInternalErrorWithRetryDisabled() {\n    noRecoveryFromErrorWithRetryDisabled(ErrorCode.INTERNAL_ERROR)\n  }\n\n  @Test\n  fun noRecoveryFromCancelWithRetryDisabled() {\n    noRecoveryFromErrorWithRetryDisabled(ErrorCode.CANCEL)\n  }\n\n  private fun noRecoveryFromErrorWithRetryDisabled(errorCode: ErrorCode?) {\n    server.enqueue(\n      MockResponse.Builder().onRequestStart(CloseStream(errorCode!!.httpCode)).build(),\n    )\n    server.enqueue(MockResponse(body = \"abc\"))\n    client =\n      client\n        .newBuilder()\n        .retryOnConnectionFailure(false)\n        .build()\n    val call = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<StreamResetException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.errorCode).isEqualTo(errorCode)\n    }\n  }\n\n  @Test\n  fun recoverFromConnectionNoNewStreamsOnFollowUp() {\n    server.enqueue(MockResponse(code = 401))\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseStream(ErrorCode.INTERNAL_ERROR.httpCode))\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"DEF\"))\n    server.enqueue(\n      MockResponse(\n        code = 301,\n        headers = headersOf(\"Location\", \"/foo\"),\n      ),\n    )\n    server.enqueue(MockResponse(body = \"ABC\"))\n    val latch = CountDownLatch(1)\n    val responses: BlockingQueue<String?> = SynchronousQueue()\n    val authenticator =\n      okhttp3.Authenticator { route: Route?, response: Response? ->\n        responses.offer(response!!.body.string())\n        try {\n          latch.await()\n        } catch (e: InterruptedException) {\n          throw AssertionError()\n        }\n        response.request\n      }\n    val blockingAuthClient =\n      client\n        .newBuilder()\n        .authenticator(authenticator)\n        .build()\n    val callback: Callback =\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          fail(\"\")\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          responses.offer(response.body.string())\n        }\n      }\n\n    // Make the first request waiting until we get our auth challenge.\n    val request = Request(server.url(\"/\"))\n    blockingAuthClient.newCall(request).enqueue(callback)\n    val response1 = responses.take()\n    assertThat(response1).isEqualTo(\"\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // Now make the second request which will restrict the first HTTP/2 connection from creating new\n    // streams.\n    client.newCall(request).enqueue(callback)\n    val response2 = responses.take()\n    assertThat(response2).isEqualTo(\"DEF\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n\n    // Let the first request proceed. It should discard the the held HTTP/2 connection and get a new\n    // one.\n    latch.countDown()\n    val response3 = responses.take()\n    assertThat(response3).isEqualTo(\"ABC\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  @Test\n  fun nonAsciiResponseHeader() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeaderLenient(\"Alpha\", \"α\")\n        .addHeaderLenient(\"β\", \"Beta\")\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    response.close()\n    assertThat(response.header(\"Alpha\")).isEqualTo(\"α\")\n    assertThat(response.header(\"β\")).isEqualTo(\"Beta\")\n  }\n\n  @Test\n  fun serverSendsPushPromise_GET() {\n    val pushPromise =\n      PushPromise(\n        \"GET\",\n        \"/foo/bar\",\n        headersOf(\"foo\", \"bar\"),\n        MockResponse(body = \"bar\"),\n      )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABCDE\")\n        .addPush(pushPromise)\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/foo\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABCDE\")\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.message).isEqualTo(\"\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET /foo HTTP/2\")\n    assertThat(request.headers[\":scheme\"]).isEqualTo(scheme)\n    assertThat(request.headers[\":authority\"]).isEqualTo(\n      server.hostName + \":\" + server.port,\n    )\n    val pushedRequest = server.takeRequest()\n    assertThat(pushedRequest.requestLine).isEqualTo(\"GET /foo/bar HTTP/2\")\n    assertThat(pushedRequest.headers[\"foo\"]).isEqualTo(\"bar\")\n  }\n\n  @Test\n  fun serverSendsPushPromise_HEAD() {\n    val pushPromise =\n      PushPromise(\n        \"HEAD\",\n        \"/foo/bar\",\n        headersOf(\"foo\", \"bar\"),\n        MockResponse(code = 204),\n      )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABCDE\")\n        .addPush(pushPromise)\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/foo\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABCDE\")\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.message).isEqualTo(\"\")\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET /foo HTTP/2\")\n    assertThat(request.headers[\":scheme\"]).isEqualTo(scheme)\n    assertThat(request.headers[\":authority\"]).isEqualTo(\n      server.hostName + \":\" + server.port,\n    )\n    val pushedRequest = server.takeRequest()\n    assertThat(pushedRequest.requestLine).isEqualTo(\"HEAD /foo/bar HTTP/2\")\n    assertThat(pushedRequest.headers[\"foo\"]).isEqualTo(\"bar\")\n  }\n\n  @Test\n  fun noDataFramesSentWithNullRequestBody() {\n    server.enqueue(MockResponse(body = \"ABC\"))\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .method(\"DELETE\", null)\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABC\")\n    assertThat(response.protocol).isEqualTo(protocol)\n    val logs = testLogHandler.takeAll()\n    assertThat(firstFrame(logs, \"HEADERS\")!!, \"header logged\")\n      .contains(\"HEADERS       END_STREAM|END_HEADERS\")\n  }\n\n  @Test\n  fun emptyDataFrameSentWithEmptyBody() {\n    server.enqueue(MockResponse(body = \"ABC\"))\n    val call =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .method(\"DELETE\", RequestBody.EMPTY)\n          .build(),\n      )\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABC\")\n    assertThat(response.protocol).isEqualTo(protocol)\n    val logs = testLogHandler.takeAll()\n    assertThat(firstFrame(logs, \"HEADERS\")!!, \"header logged\")\n      .contains(\"HEADERS       END_HEADERS\")\n    // While MockWebServer waits to read the client's HEADERS frame before sending the response, it\n    // doesn't wait to read the client's DATA frame and may send a DATA frame before the client\n    // does. So we can't assume the client's empty DATA will be logged first.\n    assertThat(countFrames(logs, \"FINE: >> 0x00000003     0 DATA          END_STREAM\"))\n      .isEqualTo(1)\n    assertThat(countFrames(logs, \"FINE: >> 0x00000003     3 DATA          END_STREAM\"))\n      .isEqualTo(1)\n  }\n\n  @Test\n  fun pingsTransmitted() {\n    // Ping every 500 ms, starting at 500 ms.\n    client =\n      client\n        .newBuilder()\n        .pingInterval(Duration.ofMillis(500))\n        .build()\n\n    // Delay the response to give 1 ping enough time to be sent and replied to.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(750, TimeUnit.MILLISECONDS)\n        .body(\"ABC\")\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABC\")\n    assertThat(response.protocol).isEqualTo(protocol)\n\n    // Confirm a single ping was sent and received, and its reply was sent and received.\n    val logs = testLogHandler.takeAll()\n    assertThat(countFrames(logs, \"FINE: >> 0x00000000     8 PING          \"))\n      .isEqualTo(1)\n    assertThat(countFrames(logs, \"FINE: << 0x00000000     8 PING          \"))\n      .isEqualTo(1)\n    assertThat(countFrames(logs, \"FINE: >> 0x00000000     8 PING          ACK\"))\n      .isEqualTo(1)\n    assertThat(countFrames(logs, \"FINE: << 0x00000000     8 PING          ACK\"))\n      .isEqualTo(1)\n  }\n\n  @Test\n  fun missingPongsFailsConnection() {\n    if (protocol === Protocol.HTTP_2) {\n      // https://github.com/square/okhttp/issues/5221\n      platform.expectFailureOnJdkVersion(12)\n    }\n\n    // Ping every 500 ms, starting at 500 ms.\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofSeconds(10)) // Confirm we fail before the read timeout.\n        .pingInterval(Duration.ofMillis(500))\n        .build()\n\n    // Set up the server to ignore the socket. It won't respond to pings!\n    server.enqueue(MockResponse.Builder().onRequestStart(Stall).build())\n\n    // Make a call. It'll fail as soon as our pings detect a problem.\n    val call = client.newCall(Request(server.url(\"/\")))\n    val executeAtNanos = System.nanoTime()\n    assertFailsWith<StreamResetException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"stream was reset: PROTOCOL_ERROR\",\n      )\n    }\n    val elapsedUntilFailure = System.nanoTime() - executeAtNanos\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedUntilFailure).toDouble())\n      .isCloseTo(1000.0, 250.0)\n\n    // Confirm a single ping was sent but not acknowledged.\n    val logs = testLogHandler.takeAll()\n    assertThat(countFrames(logs, \"FINE: >> 0x00000000     8 PING          \"))\n      .isEqualTo(1)\n    assertThat(countFrames(logs, \"FINE: << 0x00000000     8 PING          ACK\"))\n      .isEqualTo(0)\n  }\n\n  @Test\n  fun streamTimeoutDegradesConnectionAfterNoPong() {\n    assumeNotWindows()\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(500))\n        .build()\n\n    // Stalling the socket will cause TWO requests to time out!\n    server.enqueue(MockResponse.Builder().onRequestStart(Stall).build())\n\n    // The 3rd request should be sent to a fresh connection.\n    server.enqueue(\n      MockResponse(body = \"fresh connection\"),\n    )\n\n    // The first call times out.\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<IOException> {\n      call1.execute()\n    }.also { expected ->\n      when (expected) {\n        is SocketTimeoutException, is SSLException -> {}\n\n        else -> {\n          throw expected\n        }\n      }\n    }\n\n    // The second call times out because it uses the same bad connection.\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<SocketTimeoutException> {\n      call2.execute()\n    }\n\n    // But after the degraded pong timeout, that connection is abandoned.\n    Thread.sleep(TimeUnit.NANOSECONDS.toMillis(Http2Connection.DEGRADED_PONG_TIMEOUT_NS.toLong()))\n    val call3 = client.newCall(Request(server.url(\"/\")))\n    call3.execute().use { response ->\n      assertThat(\n        response.body.string(),\n      ).isEqualTo(\"fresh connection\")\n    }\n  }\n\n  @Test\n  fun oneStreamTimeoutDoesNotBreakConnection() {\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ofMillis(500))\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(1000, TimeUnit.MILLISECONDS)\n        .body(\"a\")\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"b\"))\n    server.enqueue(MockResponse(body = \"c\"))\n\n    // The first call times out.\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    assertFailsWith<SocketTimeoutException> {\n      call1.execute().use { response ->\n        response.body.string()\n      }\n    }\n\n    // The second call succeeds.\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    call2.execute().use { response ->\n      assertThat(\n        response.body.string(),\n      ).isEqualTo(\"b\")\n    }\n\n    // Calls succeed after the degraded pong timeout because the degraded pong was received.\n    Thread.sleep(TimeUnit.NANOSECONDS.toMillis(Http2Connection.DEGRADED_PONG_TIMEOUT_NS.toLong()))\n    val call3 = client.newCall(Request(server.url(\"/\")))\n    call3.execute().use { response ->\n      assertThat(\n        response.body.string(),\n      ).isEqualTo(\"c\")\n    }\n\n    // All calls share a connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n  }\n\n  private fun firstFrame(\n    logs: List<String>,\n    type: String,\n  ): String? {\n    for (log in logs) {\n      if (type in log) {\n        return log\n      }\n    }\n    return null\n  }\n\n  private fun countFrames(\n    logs: List<String>,\n    message: String,\n  ): Int {\n    var result = 0\n    for (log in logs) {\n      if (log == message) {\n        result++\n      }\n    }\n    return result\n  }\n\n  /**\n   * Push a setting that permits up to 2 concurrent streams, then make 3 concurrent requests and\n   * confirm that the third concurrent request prepared a new connection.\n   */\n  @Test\n  fun settingsLimitsMaxConcurrentStreams() {\n    val settings = Settings()\n    settings[Settings.MAX_CONCURRENT_STREAMS] = 2\n\n    // Read & write a full request to confirm settings are accepted.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .settings(settings)\n        .build(),\n    )\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"\")\n    server.enqueue(\n      MockResponse(body = \"ABC\"),\n    )\n    server.enqueue(\n      MockResponse(body = \"DEF\"),\n    )\n    server.enqueue(\n      MockResponse(body = \"GHI\"),\n    )\n    val call1 = client.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    val call2 = client.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    val call3 = client.newCall(Request(server.url(\"/\")))\n    val response3 = call3.execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n    assertThat(response2.body.string()).isEqualTo(\"DEF\")\n    assertThat(response3.body.string()).isEqualTo(\"GHI\")\n    // Settings connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    // Reuse settings connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(1)\n    // Reuse settings connection.\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(2)\n    // New connection!\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun connectionNotReusedAfterShutdown() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"DEF\"))\n    // Enqueue an additional response that show if we burnt a good prior response.\n    server.enqueue(\n      MockResponse(body = \"XXX\"),\n    )\n    val connections: MutableList<RealConnection?> = ArrayList()\n    val localClient =\n      client\n        .newBuilder()\n        .eventListener(\n          object : EventListener() {\n            override fun connectionAcquired(\n              call: Call,\n              connection: Connection,\n            ) {\n              connections.add(connection as RealConnection)\n            }\n          },\n        ).build()\n    val call1 = localClient.newCall(Request(server.url(\"/\")))\n    val response1 = call1.execute()\n    assertThat(response1.body.string()).isEqualTo(\"ABC\")\n\n    // Add delays for DISCONNECT_AT_END to propogate\n    waitForConnectionShutdown(connections[0])\n    val call2 = localClient.newCall(Request(server.url(\"/\")))\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(\"DEF\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Throws(InterruptedException::class, TimeoutException::class)\n  private fun waitForConnectionShutdown(connection: RealConnection?) {\n    if (connection!!.isHealthy(false)) {\n      Thread.sleep(100L)\n    }\n    if (connection.isHealthy(false)) {\n      Thread.sleep(2000L)\n    }\n    if (connection.isHealthy(false)) {\n      throw TimeoutException(\"connection didn't shutdown within timeout\")\n    }\n  }\n\n  /**\n   * This simulates a race condition where we receive a healthy HTTP/2 connection and just prior to\n   * writing our request, we get a GOAWAY frame from the server.\n   */\n  @Test\n  fun connectionShutdownAfterHealthCheck() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"ABC\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"DEF\"))\n    val client2 =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          object : Interceptor {\n            var executedCall = false\n\n            override fun intercept(chain: Interceptor.Chain): Response {\n              if (!executedCall) {\n                // At this point, we have a healthy HTTP/2 connection. This call will trigger the\n                // server to send a GOAWAY frame, leaving the connection in a shutdown state.\n                executedCall = true\n                val call =\n                  client.newCall(\n                    Request\n                      .Builder()\n                      .url(server.url(\"/\"))\n                      .build(),\n                  )\n                val response = call.execute()\n                assertThat(response.body.string()).isEqualTo(\"ABC\")\n                // Wait until the GOAWAY has been processed.\n                val connection = chain.connection() as RealConnection?\n                while (connection!!.isHealthy(false));\n              }\n              return chain.proceed(chain.request())\n            }\n          },\n        ).build()\n    val call = client2.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"DEF\")\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(server.takeRequest().exchangeIndex).isEqualTo(0)\n  }\n\n  @Test\n  fun responseHeadersAfterGoaway() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(1, TimeUnit.SECONDS)\n        .body(\"ABC\")\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"DEF\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val latch = CountDownLatch(2)\n    val errors = ArrayList<IOException?>()\n    val bodies: BlockingQueue<String?> = LinkedBlockingQueue()\n    val callback: Callback =\n      object : Callback {\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          bodies.add(response.body.string())\n          latch.countDown()\n        }\n\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          errors.add(e)\n          latch.countDown()\n        }\n      }\n    client.newCall(Request.Builder().url(server.url(\"/\")).build()).enqueue(\n      callback,\n    )\n    client.newCall(Request.Builder().url(server.url(\"/\")).build()).enqueue(\n      callback,\n    )\n    latch.await()\n    assertThat(bodies.remove()).isEqualTo(\"DEF\")\n    if (errors.isEmpty()) {\n      assertThat(bodies.remove()).isEqualTo(\"ABC\")\n      assertThat(server.requestCount).isEqualTo(2)\n    } else {\n      // https://github.com/square/okhttp/issues/4836\n      // As documented in SocketEffect, this is known to be flaky.\n      val error = errors[0]\n      if (error !is StreamResetException) {\n        throw error!!\n      }\n    }\n  }\n\n  /**\n   * We don't know if the connection will support HTTP/2 until after we've connected. When multiple\n   * connections are requested concurrently OkHttp will pessimistically connect multiple times, then\n   * close any unnecessary connections. This test confirms that behavior works as intended.\n   *\n   * This test uses proxy tunnels to get a hook while a connection is being established.\n   */\n  @Test\n  fun concurrentHttp2ConnectionsDeduplicated() {\n    assumeTrue(protocol === Protocol.HTTP_2)\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    val queueDispatcher = QueueDispatcher()\n    queueDispatcher.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    queueDispatcher.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    queueDispatcher.enqueue(MockResponse(body = \"call2 response\"))\n    queueDispatcher.enqueue(MockResponse(body = \"call1 response\"))\n\n    // We use a re-entrant dispatcher to initiate one HTTPS connection while the other is in flight.\n    server.dispatcher =\n      object : Dispatcher() {\n        var requestCount = 0\n\n        override fun dispatch(request: RecordedRequest): MockResponse {\n          val result = queueDispatcher.dispatch(request)\n          requestCount++\n          if (requestCount == 1) {\n            // Before handling call1's CONNECT we do all of call2. This part re-entrant!\n            try {\n              val call2 =\n                client.newCall(\n                  Request\n                    .Builder()\n                    .url(\"https://android.com/call2\")\n                    .build(),\n                )\n              val response2 = call2.execute()\n              assertThat(response2.body.string()).isEqualTo(\"call2 response\")\n            } catch (e: IOException) {\n              throw RuntimeException(e)\n            }\n          }\n          return result\n        }\n\n        override fun peek(): MockResponse = queueDispatcher.peek()\n\n        override fun close() {\n          queueDispatcher.close()\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .build()\n    val call1 = client.newCall(Request(\"https://android.com/call1\".toHttpUrl()))\n    val response2 = call1.execute()\n    assertThat(response2.body.string()).isEqualTo(\"call1 response\")\n    val call1Connect = server.takeRequest()\n    assertThat(call1Connect.method).isEqualTo(\"CONNECT\")\n    assertThat(call1Connect.exchangeIndex).isEqualTo(0)\n    val call2Connect = server.takeRequest()\n    assertThat(call2Connect.method).isEqualTo(\"CONNECT\")\n    assertThat(call2Connect.exchangeIndex).isEqualTo(0)\n    val call2Get = server.takeRequest()\n    assertThat(call2Get.method).isEqualTo(\"GET\")\n    assertThat(call2Get.url.encodedPath).isEqualTo(\"/call2\")\n    assertThat(call2Get.exchangeIndex).isEqualTo(0)\n    val call1Get = server.takeRequest()\n    assertThat(call1Get.method).isEqualTo(\"GET\")\n    assertThat(call1Get.url.encodedPath).isEqualTo(\"/call1\")\n    assertThat(call1Get.exchangeIndex).isEqualTo(1)\n    assertThat(client.connectionPool.connectionCount()).isEqualTo(1)\n  }\n\n  /** https://github.com/square/okhttp/issues/3103  */\n  @Test\n  fun domainFronting() {\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            val request =\n              chain!!\n                .request()\n                .newBuilder()\n                .header(\"Host\", \"privateobject.com\")\n                .build()\n            chain.proceed(request)\n          },\n        ).build()\n    server.enqueue(MockResponse())\n    val call = client.newCall(Request(server.url(\"/\")))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"\")\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.headers[\":authority\"]).isEqualTo(\"privateobject.com\")\n  }\n\n  private fun gzip(bytes: String): Buffer {\n    val bytesOut = Buffer()\n    val sink = GzipSink(bytesOut).buffer()\n    sink.writeUtf8(bytes)\n    sink.close()\n    return bytesOut\n  }\n\n  internal inner class AsyncRequest(\n    val path: String,\n    val countDownLatch: CountDownLatch,\n  ) : Runnable {\n    override fun run() {\n      try {\n        val call =\n          client.newCall(\n            Request\n              .Builder()\n              .url(server.url(path))\n              .build(),\n          )\n        val response = call.execute()\n        assertThat(response.body.string()).isEqualTo(\"A\")\n        countDownLatch.countDown()\n      } catch (e: Exception) {\n        throw RuntimeException(e)\n      }\n    }\n  }\n\n  /** https://github.com/square/okhttp/issues/4875  */\n  @Test\n  fun shutdownAfterLateCoalescing() {\n    val latch = CountDownLatch(2)\n    val callback: Callback =\n      object : Callback {\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          fail(\"\")\n        }\n\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          latch.countDown()\n        }\n      }\n    client =\n      client\n        .newBuilder()\n        .eventListenerFactory(\n          clientTestRule.wrap(\n            object : EventListener() {\n              var callCount = 0\n\n              override fun connectionAcquired(\n                call: Call,\n                connection: Connection,\n              ) {\n                try {\n                  if (callCount++ == 1) {\n                    server.close()\n                  }\n                } catch (e: IOException) {\n                  fail(\"\")\n                }\n              }\n            },\n          ),\n        ).build()\n    client.newCall(Request.Builder().url(server.url(\"\")).build()).enqueue(\n      callback,\n    )\n    client.newCall(Request.Builder().url(server.url(\"\")).build()).enqueue(\n      callback,\n    )\n    latch.await()\n  }\n\n  @Test\n  fun cancelWhileWritingRequestBodySendsCancelToServer() {\n    server.enqueue(MockResponse())\n    val callReference = AtomicReference<Call?>()\n    val call =\n      client.newCall(\n        Request(\n          url = server.url(\"/\"),\n          body =\n            object : RequestBody() {\n              override fun contentType() = \"text/plain; charset=utf-8\".toMediaType()\n\n              override fun writeTo(sink: BufferedSink) {\n                callReference.get()!!.cancel()\n              }\n            },\n        ),\n      )\n    callReference.set(call)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      assertThat(call.isCanceled()).isTrue()\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.failure!!).hasMessage(\"stream was reset: CANCEL\")\n  }\n\n  @Test\n  fun http2WithProxy() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"ABCDE\"))\n    val client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .build()\n\n    val url = server.url(\"/\").resolve(\"//android.com/foo\")!!\n    val port =\n      when (url.scheme) {\n        \"https\" -> 443\n        \"http\" -> 80\n        else -> error(\"unexpected scheme\")\n      }\n\n    val call = client.newCall(Request(url))\n    val response = call.execute()\n    assertThat(response.body.string()).isEqualTo(\"ABCDE\")\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.message).isEqualTo(\"\")\n    assertThat(response.protocol).isEqualTo(protocol)\n\n    val tunnelRequest = server.takeRequest()\n    assertThat(tunnelRequest.requestLine).isEqualTo(\"CONNECT android.com:$port HTTP/1.1\")\n\n    val request = server.takeRequest()\n    assertThat(request.requestLine).isEqualTo(\"GET /foo HTTP/2\")\n    assertThat(request.headers[\":scheme\"]).isEqualTo(scheme)\n    assertThat(request.headers[\":authority\"]).isEqualTo(\"android.com\")\n  }\n\n  /** Respond to a proxy authorization challenge.  */\n  @Test\n  fun proxyAuthenticateOnConnect() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .code(407)\n        .headers(headersOf(\"Proxy-Authenticate\", \"Basic realm=\\\"localhost\\\"\"))\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .inTunnel()\n        .build(),\n    )\n    server.enqueue(MockResponse(body = \"response body\"))\n    val client =\n      client\n        .newBuilder()\n        .proxy(server.proxyAddress)\n        .proxyAuthenticator(RecordingOkAuthenticator(\"password\", \"Basic\"))\n        .build()\n\n    val url = server.url(\"/\").resolve(\"//android.com/foo\")!!\n    val port =\n      when (url.scheme) {\n        \"https\" -> 443\n        \"http\" -> 80\n        else -> error(\"unexpected scheme\")\n      }\n\n    val request = Request(url)\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"response body\")\n\n    val connect1 = server.takeRequest()\n    assertThat(connect1.requestLine).isEqualTo(\"CONNECT android.com:$port HTTP/1.1\")\n    assertThat(connect1.headers[\"Proxy-Authorization\"]).isNull()\n\n    val connect2 = server.takeRequest()\n    assertThat(connect2.requestLine).isEqualTo(\"CONNECT android.com:$port HTTP/1.1\")\n    assertThat(connect2.headers[\"Proxy-Authorization\"]).isEqualTo(\"password\")\n\n    val get = server.takeRequest()\n    assertThat(get.requestLine).isEqualTo(\"GET /foo HTTP/2\")\n    assertThat(get.headers[\"Proxy-Authorization\"]).isNull()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/HuffmanTest.kt",
    "content": "/*\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.util.Random\nimport okhttp3.internal.http2.Huffman.decode\nimport okhttp3.internal.http2.Huffman.encode\nimport okhttp3.internal.http2.Huffman.encodedLength\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\n/** Original version of this class was lifted from `com.twitter.hpack.HuffmanTest`.  */\nclass HuffmanTest {\n  @Test\n  fun roundTripForRequestAndResponse() {\n    val s = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\"\n    for (i in s.indices) {\n      assertRoundTrip(s.substring(0, i).encodeUtf8())\n    }\n    val random = Random(123456789L)\n    val buf = ByteArray(4096)\n    random.nextBytes(buf)\n    assertRoundTrip(buf.toByteString())\n  }\n\n  private fun assertRoundTrip(data: ByteString) {\n    val encodeBuffer = Buffer()\n    encode(data, encodeBuffer)\n    assertThat(encodedLength(data).toLong()).isEqualTo(encodeBuffer.size)\n    val decodeBuffer = Buffer()\n    decode(encodeBuffer, encodeBuffer.size, decodeBuffer)\n    assertEquals(data, decodeBuffer.readByteString())\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/MockHttp2Peer.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport java.io.Closeable\nimport java.io.IOException\nimport java.net.InetSocketAddress\nimport java.net.ServerSocket\nimport java.net.Socket\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.Executors\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.logging.Logger\nimport okhttp3.TestUtil.threadFactory\nimport okhttp3.internal.closeQuietly\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.buffer\nimport okio.source\n\n/** Replays prerecorded outgoing frames and records incoming frames.  */\nclass MockHttp2Peer : Closeable {\n  private var frameCount = 0\n  private var client = false\n  private val bytesOut = Buffer()\n  private var writer = Http2Writer(bytesOut, client)\n  private val outFrames: MutableList<OutFrame> = ArrayList()\n  private val inFrames: BlockingQueue<InFrame> = LinkedBlockingQueue()\n  private var port = 0\n  private val executor = Executors.newSingleThreadExecutor(threadFactory(\"MockHttp2Peer\"))\n  private var serverSocket: ServerSocket? = null\n  private var socket: Socket? = null\n\n  fun setClient(client: Boolean) {\n    if (this.client == client) return\n    this.client = client\n    writer = Http2Writer(bytesOut, client)\n  }\n\n  fun acceptFrame() {\n    frameCount++\n  }\n\n  /** Maximum length of an outbound data frame.  */\n  fun maxOutboundDataLength(): Int = writer.maxDataLength()\n\n  /** Count of frames sent or received.  */\n  fun frameCount(): Int = frameCount\n\n  fun sendFrame(): Http2Writer {\n    outFrames.add(OutFrame(frameCount++, bytesOut.size, false))\n    return writer\n  }\n\n  /**\n   * Shortens the last frame from its original length to `length`. This will cause the peer to\n   * close the socket as soon as this frame has been written; otherwise the peer stays open until\n   * explicitly closed.\n   */\n  fun truncateLastFrame(length: Int): Http2Writer {\n    val lastFrame = outFrames.removeAt(outFrames.size - 1)\n    require(length < bytesOut.size - lastFrame.start)\n\n    // Move everything from bytesOut into a new buffer.\n    val fullBuffer = Buffer()\n    bytesOut.read(fullBuffer, bytesOut.size)\n\n    // Copy back all but what we're truncating.\n    fullBuffer.read(bytesOut, lastFrame.start + length)\n    outFrames.add(OutFrame(lastFrame.sequence, lastFrame.start, true))\n    return writer\n  }\n\n  fun takeFrame(): InFrame = inFrames.take()\n\n  fun play() {\n    check(serverSocket == null)\n    serverSocket = ServerSocket()\n    serverSocket!!.reuseAddress = false\n    serverSocket!!.bind(InetSocketAddress(\"localhost\", 0), 1)\n    port = serverSocket!!.localPort\n    executor.execute {\n      try {\n        readAndWriteFrames()\n      } catch (e: IOException) {\n        this@MockHttp2Peer.closeQuietly()\n        logger.info(\"${this@MockHttp2Peer} done: ${e.message}\")\n      }\n    }\n  }\n\n  private fun readAndWriteFrames() {\n    check(socket == null)\n    val socket = serverSocket!!.accept()!!\n    this.socket = socket\n\n    // Bail out now if this instance was closed while waiting for the socket to accept.\n    synchronized(this) {\n      if (executor.isShutdown) {\n        socket.close()\n        return\n      }\n    }\n    val outputStream = socket.getOutputStream()\n    val inputStream = socket.getInputStream()\n    val reader = Http2Reader(inputStream.source().buffer(), client)\n    val outFramesIterator: Iterator<OutFrame> = outFrames.iterator()\n    val outBytes = bytesOut.readByteArray()\n    var nextOutFrame: OutFrame? = null\n    for (i in 0 until frameCount) {\n      if (nextOutFrame == null && outFramesIterator.hasNext()) {\n        nextOutFrame = outFramesIterator.next()\n      }\n\n      if (nextOutFrame != null && nextOutFrame.sequence == i) {\n        val start = nextOutFrame.start\n        var truncated: Boolean\n        var end: Long\n        if (outFramesIterator.hasNext()) {\n          nextOutFrame = outFramesIterator.next()\n          end = nextOutFrame.start\n          truncated = false\n        } else {\n          end = outBytes.size.toLong()\n          truncated = nextOutFrame.truncated\n        }\n\n        // Write a frame.\n        val length = (end - start).toInt()\n        outputStream.write(outBytes, start.toInt(), length)\n\n        // If the last frame was truncated, immediately close the connection.\n        if (truncated) {\n          socket.close()\n        }\n      } else {\n        // read a frame\n        val inFrame = InFrame(i, reader)\n        reader.nextFrame(false, inFrame)\n        inFrames.add(inFrame)\n      }\n    }\n  }\n\n  fun openSocket(): Socket = Socket(\"localhost\", port)\n\n  @Synchronized override fun close() {\n    executor.shutdown()\n    socket?.closeQuietly()\n    serverSocket?.closeQuietly()\n  }\n\n  override fun toString(): String = \"MockHttp2Peer[$port]\"\n\n  private class OutFrame(\n    val sequence: Int,\n    val start: Long,\n    val truncated: Boolean,\n  )\n\n  class InFrame(\n    val sequence: Int,\n    val reader: Http2Reader,\n  ) : Http2Reader.Handler {\n    @JvmField var type = -1\n    var clearPrevious = false\n\n    @JvmField var outFinished = false\n\n    @JvmField var inFinished = false\n\n    @JvmField var streamId = 0\n\n    @JvmField var associatedStreamId = 0\n\n    @JvmField var errorCode: ErrorCode? = null\n\n    @JvmField var windowSizeIncrement: Long = 0\n\n    @JvmField var headerBlock: List<Header>? = null\n\n    @JvmField var data: ByteArray? = null\n\n    @JvmField var settings: Settings? = null\n\n    @JvmField var ack = false\n\n    @JvmField var payload1 = 0\n\n    @JvmField var payload2 = 0\n\n    override fun settings(\n      clearPrevious: Boolean,\n      settings: Settings,\n    ) {\n      check(type == -1)\n      this.type = Http2.TYPE_SETTINGS\n      this.clearPrevious = clearPrevious\n      this.settings = settings\n    }\n\n    override fun ackSettings() {\n      check(type == -1)\n      this.type = Http2.TYPE_SETTINGS\n      this.ack = true\n    }\n\n    override fun headers(\n      inFinished: Boolean,\n      streamId: Int,\n      associatedStreamId: Int,\n      headerBlock: List<Header>,\n    ) {\n      check(type == -1)\n      this.type = Http2.TYPE_HEADERS\n      this.inFinished = inFinished\n      this.streamId = streamId\n      this.associatedStreamId = associatedStreamId\n      this.headerBlock = headerBlock\n    }\n\n    override fun data(\n      inFinished: Boolean,\n      streamId: Int,\n      source: BufferedSource,\n      length: Int,\n    ) {\n      check(type == -1)\n      this.type = Http2.TYPE_DATA\n      this.inFinished = inFinished\n      this.streamId = streamId\n      this.data = source.readByteString(length.toLong()).toByteArray()\n    }\n\n    override fun rstStream(\n      streamId: Int,\n      errorCode: ErrorCode,\n    ) {\n      check(type == -1)\n      this.type = Http2.TYPE_RST_STREAM\n      this.streamId = streamId\n      this.errorCode = errorCode\n    }\n\n    override fun ping(\n      ack: Boolean,\n      payload1: Int,\n      payload2: Int,\n    ) {\n      check(type == -1)\n      type = Http2.TYPE_PING\n      this.ack = ack\n      this.payload1 = payload1\n      this.payload2 = payload2\n    }\n\n    override fun goAway(\n      lastGoodStreamId: Int,\n      errorCode: ErrorCode,\n      debugData: ByteString,\n    ) {\n      check(type == -1)\n      this.type = Http2.TYPE_GOAWAY\n      this.streamId = lastGoodStreamId\n      this.errorCode = errorCode\n      this.data = debugData.toByteArray()\n    }\n\n    override fun windowUpdate(\n      streamId: Int,\n      windowSizeIncrement: Long,\n    ) {\n      check(type == -1)\n      this.type = Http2.TYPE_WINDOW_UPDATE\n      this.streamId = streamId\n      this.windowSizeIncrement = windowSizeIncrement\n    }\n\n    override fun priority(\n      streamId: Int,\n      streamDependency: Int,\n      weight: Int,\n      exclusive: Boolean,\n    ): Unit = throw UnsupportedOperationException()\n\n    override fun pushPromise(\n      streamId: Int,\n      associatedStreamId: Int,\n      headerBlock: List<Header>,\n    ) {\n      this.type = Http2.TYPE_PUSH_PROMISE\n      this.streamId = streamId\n      this.associatedStreamId = associatedStreamId\n      this.headerBlock = headerBlock\n    }\n\n    override fun alternateService(\n      streamId: Int,\n      origin: String,\n      protocol: ByteString,\n      host: String,\n      port: Int,\n      maxAge: Long,\n    ): Unit = throw UnsupportedOperationException()\n  }\n\n  companion object {\n    private val logger = Logger.getLogger(MockHttp2Peer::class.java.name)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/SettingsTest.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isTrue\nimport org.junit.jupiter.api.Test\n\nclass SettingsTest {\n  @Test\n  fun unsetField() {\n    val settings = Settings()\n    assertThat(settings.isSet(Settings.MAX_CONCURRENT_STREAMS)).isFalse()\n    assertThat(settings.getMaxConcurrentStreams()).isEqualTo(Int.MAX_VALUE)\n  }\n\n  @Test\n  fun setFields() {\n    val settings = Settings()\n    settings[Settings.HEADER_TABLE_SIZE] = 8096\n    assertThat(settings.headerTableSize).isEqualTo(8096)\n    assertThat(settings.getEnablePush(true)).isTrue()\n    settings[Settings.ENABLE_PUSH] = 1\n    assertThat(settings.getEnablePush(false)).isTrue()\n    settings.clear()\n    assertThat(settings.getMaxConcurrentStreams()).isEqualTo(Int.MAX_VALUE)\n    settings[Settings.MAX_CONCURRENT_STREAMS] = 75\n    assertThat(settings.getMaxConcurrentStreams()).isEqualTo(75)\n    settings.clear()\n    assertThat(settings.getMaxFrameSize(16384)).isEqualTo(16384)\n    settings[Settings.MAX_FRAME_SIZE] = 16777215\n    assertThat(settings.getMaxFrameSize(16384)).isEqualTo(16777215)\n    assertThat(settings.getMaxHeaderListSize(-1)).isEqualTo(-1)\n    settings[Settings.MAX_HEADER_LIST_SIZE] = 16777215\n    assertThat(settings.getMaxHeaderListSize(-1)).isEqualTo(16777215)\n    assertThat(settings.initialWindowSize).isEqualTo(\n      Settings.DEFAULT_INITIAL_WINDOW_SIZE,\n    )\n    settings[Settings.INITIAL_WINDOW_SIZE] = 108\n    assertThat(settings.initialWindowSize).isEqualTo(108)\n  }\n\n  @Test\n  fun merge() {\n    val a = Settings()\n    a[Settings.HEADER_TABLE_SIZE] = 10000\n    a[Settings.MAX_HEADER_LIST_SIZE] = 20000\n    a[Settings.INITIAL_WINDOW_SIZE] = 30000\n    val b = Settings()\n    b[Settings.MAX_HEADER_LIST_SIZE] = 40000\n    b[Settings.INITIAL_WINDOW_SIZE] = 50000\n    b[Settings.MAX_CONCURRENT_STREAMS] = 60000\n    a.merge(b)\n    assertThat(a.headerTableSize).isEqualTo(10000)\n    assertThat(a.getMaxHeaderListSize(-1)).isEqualTo(40000)\n    assertThat(a.initialWindowSize).isEqualTo(50000)\n    assertThat(a.getMaxConcurrentStreams()).isEqualTo(60000)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/idn/IdnaMappingTableTest.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isGreaterThan\nimport assertk.assertions.isLessThan\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\nimport okio.Buffer\nimport okio.FileSystem\nimport okio.Path.Companion.toPath\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\n/** Confirm we get the expected table whether we build it from the .txt file or compact that. */\nclass IdnaMappingTableTest {\n  private lateinit var table: SimpleIdnaMappingTable\n  private lateinit var compactTable: IdnaMappingTable\n\n  @BeforeEach\n  fun setUp() {\n    val path = \"/okhttp3/internal/idna/IdnaMappingTable.txt\".toPath()\n    val plainTable =\n      FileSystem.RESOURCES.read(path) {\n        readPlainTextIdnaMappingTable()\n      }\n    table = plainTable\n    val data = buildIdnaMappingTableData(plainTable)\n    compactTable =\n      IdnaMappingTable(\n        sections = data.sections,\n        ranges = data.ranges,\n        mappings = data.mappings,\n      )\n  }\n\n  @Test fun regularMappings() {\n    assertThat(\"hello\".map()).isEqualTo(\"hello\")\n    assertThat(\"hello-world\".map()).isEqualTo(\"hello-world\")\n    assertThat(\"HELLO\".map()).isEqualTo(\"hello\")\n    assertThat(\"Hello\".map()).isEqualTo(\"hello\")\n\n    // These compound characters map their its components.\n    assertThat(\"¼\".map()).isEqualTo(\"1⁄4\")\n    assertThat(\"™\".map()).isEqualTo(\"tm\")\n  }\n\n  /** Confirm the compact table satisfies is documented invariants. */\n  @Test fun validateCompactTableInvariants() {\n    // Less than 16,834 bytes, because we binary search on a 14-bit index.\n    assertThat(compactTable.sections.length).isLessThan(1 shl 14)\n\n    // Less than 65,536 bytes, because we binary search on a 14-bit index with a stride of 4 bytes.\n    assertThat(compactTable.ranges.length).isLessThan((1 shl 14) * 4)\n\n    // Less than 16,384 chars, because we index on a 14-bit index in the ranges table.\n    assertThat(compactTable.mappings.length).isLessThan(1 shl 14)\n\n    // Confirm the data strings are ASCII.\n    for (dataString in listOf<String>(compactTable.sections, compactTable.ranges)) {\n      for (codePoint in dataString.codePoints()) {\n        assertThat(codePoint and 0x7f).isEqualTo(codePoint)\n      }\n    }\n\n    // Confirm the sections are increasing.\n    val rangesIndices = mutableListOf<Int>()\n    val rangesOffsets = mutableListOf<Int>()\n    for (i in 0 until compactTable.sections.length step 4) {\n      rangesIndices += compactTable.sections.read14BitInt(i)\n      rangesOffsets += compactTable.sections.read14BitInt(i + 2)\n    }\n    assertThat(rangesIndices).isEqualTo(rangesIndices.sorted())\n\n    // Check the ranges.\n    for (r in 0 until rangesOffsets.size) {\n      val rangePos = rangesOffsets[r] * 4\n      val rangeLimit =\n        when {\n          r + 1 < rangesOffsets.size -> rangesOffsets[r + 1] * 4\n          else -> rangesOffsets.size * 4\n        }\n\n      // Confirm this range starts with byte 0.\n      assertThat(compactTable.ranges[rangePos].code).isEqualTo(0)\n\n      // Confirm this range's index byte is increasing.\n      val rangeStarts = mutableListOf<Int>()\n      for (i in rangePos until rangeLimit step 4) {\n        rangeStarts += compactTable.ranges[i].code\n      }\n      assertThat(rangeStarts).isEqualTo(rangeStarts.sorted())\n    }\n  }\n\n  @Test fun deviations() {\n    assertThat(\"ß\".map()).isEqualTo(\"ß\")\n    assertThat(\"ς\".map()).isEqualTo(\"ς\")\n    assertThat(\"\\u200c\".map()).isEqualTo(\"\\u200c\")\n    assertThat(\"\\u200d\".map()).isEqualTo(\"\\u200d\")\n  }\n\n  @Test fun ignored() {\n    assertThat(\"\\u200b\".map()).isEqualTo(\"\")\n    assertThat(\"\\ufeff\".map()).isEqualTo(\"\")\n  }\n\n  @Test fun disallowed() {\n    assertThat(\"\\u0080\".mapExpectingErrors()).isEqualTo(\"\\u0080\")\n  }\n\n  @Test fun disallowedStd3Valid() {\n    assertThat(\"_\".map()).isEqualTo(\"_\")\n    assertThat(\"/\".map()).isEqualTo(\"/\")\n    assertThat(\"≠\".map()).isEqualTo(\"≠\")\n  }\n\n  @Test fun disallowedStd3Mapped() {\n    assertThat(\"\\u00b8\".map()).isEqualTo(\"\\u0020\\u0327\")\n    assertThat(\"⑴\".map()).isEqualTo(\"(1)\")\n  }\n\n  @Test fun outOfBounds() {\n    assertFailsWith<IllegalArgumentException> {\n      table.map(-1, Buffer())\n    }\n    table.map(0, Buffer()) // Lowest legal code point.\n    table.map(0x10ffff, Buffer()) // Highest legal code point.\n    assertFailsWith<IllegalArgumentException> {\n      table.map(0x110000, Buffer())\n    }\n  }\n\n  @Test fun binarySearchEvenSizedRange() {\n    val table = listOf(1, 3, 5, 7, 9, 11)\n\n    // Search for matches.\n    assertEquals(0, binarySearch(0, 6) { index -> 1.compareTo(table[index]) })\n    assertEquals(1, binarySearch(0, 6) { index -> 3.compareTo(table[index]) })\n    assertEquals(2, binarySearch(0, 6) { index -> 5.compareTo(table[index]) })\n    assertEquals(3, binarySearch(0, 6) { index -> 7.compareTo(table[index]) })\n    assertEquals(4, binarySearch(0, 6) { index -> 9.compareTo(table[index]) })\n    assertEquals(5, binarySearch(0, 6) { index -> 11.compareTo(table[index]) })\n\n    // Search for misses.\n    assertEquals(-1, binarySearch(0, 6) { index -> 0.compareTo(table[index]) })\n    assertEquals(-2, binarySearch(0, 6) { index -> 2.compareTo(table[index]) })\n    assertEquals(-3, binarySearch(0, 6) { index -> 4.compareTo(table[index]) })\n    assertEquals(-4, binarySearch(0, 6) { index -> 6.compareTo(table[index]) })\n    assertEquals(-5, binarySearch(0, 6) { index -> 8.compareTo(table[index]) })\n    assertEquals(-6, binarySearch(0, 6) { index -> 10.compareTo(table[index]) })\n    assertEquals(-7, binarySearch(0, 6) { index -> 12.compareTo(table[index]) })\n  }\n\n  @Test fun binarySearchOddSizedRange() {\n    val table = listOf(1, 3, 5, 7, 9)\n\n    // Search for matches.\n    assertEquals(0, binarySearch(0, 5) { index -> 1.compareTo(table[index]) })\n    assertEquals(1, binarySearch(0, 5) { index -> 3.compareTo(table[index]) })\n    assertEquals(2, binarySearch(0, 5) { index -> 5.compareTo(table[index]) })\n    assertEquals(3, binarySearch(0, 5) { index -> 7.compareTo(table[index]) })\n    assertEquals(4, binarySearch(0, 5) { index -> 9.compareTo(table[index]) })\n\n    // Search for misses.\n    assertEquals(-1, binarySearch(0, 5) { index -> 0.compareTo(table[index]) })\n    assertEquals(-2, binarySearch(0, 5) { index -> 2.compareTo(table[index]) })\n    assertEquals(-3, binarySearch(0, 5) { index -> 4.compareTo(table[index]) })\n    assertEquals(-4, binarySearch(0, 5) { index -> 6.compareTo(table[index]) })\n    assertEquals(-5, binarySearch(0, 5) { index -> 8.compareTo(table[index]) })\n    assertEquals(-6, binarySearch(0, 5) { index -> 10.compareTo(table[index]) })\n  }\n\n  @Test fun binarySearchSingleElementRange() {\n    val table = listOf(1)\n\n    // Search for matches.\n    assertEquals(0, binarySearch(0, 1) { index -> 1.compareTo(table[index]) })\n\n    // Search for misses.\n    assertEquals(-1, binarySearch(0, 1) { index -> 0.compareTo(table[index]) })\n    assertEquals(-2, binarySearch(0, 1) { index -> 2.compareTo(table[index]) })\n  }\n\n  @Test fun binarySearchEmptyRange() {\n    assertEquals(-1, binarySearch(0, 0) { error(\"unexpected call\") })\n  }\n\n  /** Confirm the compact table has the exact same behavior as the plain table. */\n  @Test fun comparePlainAndCompactTables() {\n    val buffer = Buffer()\n    for (codePoint in 0..0x10ffff) {\n      val allowedByTable = table.map(codePoint, buffer)\n      val tableMappedTo = buffer.readUtf8()\n\n      val allowedByCompactTable = compactTable.map(codePoint, buffer)\n      val compactTableMappedTo = buffer.readUtf8()\n\n      assertThat(allowedByCompactTable).isEqualTo(allowedByTable)\n      assertThat(compactTableMappedTo).isEqualTo(tableMappedTo)\n    }\n  }\n\n  /** Confirm we didn't corrupt any data in code generation. */\n  @Test fun compareConstructedAndGeneratedCompactTables() {\n    assertThat(IDNA_MAPPING_TABLE.sections).isEqualTo(compactTable.sections)\n    assertThat(IDNA_MAPPING_TABLE.ranges).isEqualTo(compactTable.ranges)\n    assertThat(IDNA_MAPPING_TABLE.mappings).isEqualTo(compactTable.mappings)\n  }\n\n  private fun String.map(): String {\n    val buffer = Buffer()\n    for (codePoint in codePoints()) {\n      require(table.map(codePoint, buffer))\n    }\n    return buffer.readUtf8()\n  }\n\n  private fun String.mapExpectingErrors(): String {\n    val buffer = Buffer()\n    var errorCount = 0\n    for (codePoint in codePoints()) {\n      if (!table.map(codePoint, buffer)) errorCount++\n    }\n    assertThat(errorCount).isGreaterThan(0)\n    return buffer.readUtf8()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/idn/PunycodeTest.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertNull\n\nclass PunycodeTest {\n  /** https://datatracker.ietf.org/doc/html/rfc3492#section-7.1 */\n  @Test fun rfc3492Samples() {\n    // (A) Arabic (Egyptian)\n    testEncodeDecode(\n      unicode = \"ليهمابتكلموشعربي؟\",\n      punycode = \"xn--egbpdaj6bu4bxfgehfvwxn\",\n    )\n\n    // (B) Chinese (simplified)\n    testEncodeDecode(\n      unicode = \"他们为什么不说中文\",\n      punycode = \"xn--ihqwcrb4cv8a8dqg056pqjye\",\n    )\n\n    // (C) Chinese (traditional)\n    testEncodeDecode(\n      unicode = \"他們爲什麽不說中文\",\n      punycode = \"xn--ihqwctvzc91f659drss3x8bo0yb\",\n    )\n\n    // (D) Czech\n    testEncodeDecode(\n      unicode = \"Pročprostěnemluvíčesky\",\n      punycode = \"xn--Proprostnemluvesky-uyb24dma41a\",\n    )\n\n    // (E) Hebrew:\n    testEncodeDecode(\n      unicode = \"למההםפשוטלאמדבריםעברית\",\n      punycode = \"xn--4dbcagdahymbxekheh6e0a7fei0b\",\n    )\n\n    // (F) Hindi (Devanagari)\n    testEncodeDecode(\n      unicode = \"यहलोगहिन्दीक्योंनहींबोलसकतेहैं\",\n      punycode = \"xn--i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd\",\n    )\n\n    // (G) Japanese (kanji and hiragana)\n    testEncodeDecode(\n      unicode = \"なぜみんな日本語を話してくれないのか\",\n      punycode = \"xn--n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa\",\n    )\n\n    // (H) Korean (Hangul syllables)\n    testEncodeDecode(\n      unicode = \"세계의모든사람들이한국어를이해한다면얼마나좋을까\",\n      punycode = \"xn--989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c\",\n    )\n\n    // (I) Russian (Cyrillic)\n    testEncodeDecode(\n      unicode = \"почемужеонинеговорятпорусски\",\n      punycode = \"xn--b1abfaaepdrnnbgefbadotcwatmq2g4l\",\n    )\n\n    // (J) Spanish\n    testEncodeDecode(\n      unicode = \"PorquénopuedensimplementehablarenEspañol\",\n      punycode = \"xn--PorqunopuedensimplementehablarenEspaol-fmd56a\",\n    )\n\n    // (K) Vietnamese\n    testEncodeDecode(\n      unicode = \"TạisaohọkhôngthểchỉnóitiếngViệt\",\n      punycode = \"xn--TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g\",\n    )\n  }\n\n  @Test fun multipleLabels() {\n    testEncodeDecode(\n      unicode = \"☃.net\",\n      punycode = \"xn--n3h.net\",\n    )\n    testEncodeDecode(\n      unicode = \"ålgård.no\",\n      punycode = \"xn--lgrd-poac.no\",\n    )\n    testEncodeDecode(\n      unicode = \"個人.香港\",\n      punycode = \"xn--gmqw5a.xn--j6w193g\",\n    )\n    testEncodeDecode(\n      unicode = \"упр.срб\",\n      punycode = \"xn--o1ach.xn--90a3ac\",\n    )\n  }\n\n  @Test fun nonBasicCodePointInPrefix() {\n    assertNull(Punycode.decode(\"xn--cåt-n3h\"))\n  }\n\n  @Test fun nonBasicCodePointInInsertionCoding() {\n    assertNull(Punycode.decode(\"xn--cat-ñ3h\"))\n  }\n\n  @Test fun unterminatedCodePoint() {\n    assertNull(Punycode.decode(\"xn--cat-n\"))\n  }\n\n  @Test fun overflowI() {\n    assertNull(Punycode.decode(\"xn--99999999\"))\n  }\n\n  @Test fun overflowMaxCodePoint() {\n    assertNull(Punycode.decode(\"xn--a-b.net\"))\n    assertNull(Punycode.decode(\"xn--a-9b.net\"))\n    assertEquals(\"a՚.net\", Punycode.decode(\"xn--a-99b.net\"))\n    assertEquals(\"a溠.net\", Punycode.decode(\"xn--a-999b.net\"))\n    assertEquals(\"a\\uD8E2\\uDF5C.net\", Punycode.decode(\"xn--a-9999b.net\"))\n    assertNull(Punycode.decode(\"xn--a-99999b.net\"))\n  }\n\n  @Test fun dashInPrefix() {\n    testEncodeDecode(\n      unicode = \"klmnöpqrst-uvwxy\",\n      punycode = \"xn--klmnpqrst-uvwxy-ctb\",\n    )\n  }\n\n  @Test fun uppercasePunycode() {\n    testDecodeOnly(\n      unicode = \"ليهمابتكلموشعربي؟\",\n      punycode = \"XN--EGBPDAJ6BU4BXFGEHFVWXN\",\n    )\n  }\n\n  @Test fun mixedCasePunycode() {\n    testDecodeOnly(\n      unicode = \"ليهمابتكلموشعربي؟\",\n      punycode = \"Xn--EgBpDaJ6Bu4bXfGeHfVwXn\",\n    )\n  }\n\n  /**\n   * It's invalid to have a label longer than 63 characters. If that's requested, the encoder may\n   * overflow and return null.\n   */\n  @Test fun overflowEncodingOversizedLabel() {\n    val a1000 = \"a\".repeat(1000)\n    val a1000MaxCodePoint = a1000 + \"\\udbff\\udfff\"\n    testEncodeDecode(\n      a1000MaxCodePoint,\n      \"xn--$a1000-nc89312g\",\n    )\n    assertNull(\n      Punycode.encode(a1000MaxCodePoint.repeat(2)),\n    )\n  }\n\n  @Test fun invalidPunycode() {\n    assertNull(Punycode.decode(\"xn--ls8h=\"))\n  }\n\n  private fun testEncodeDecode(\n    unicode: String,\n    punycode: String,\n  ) {\n    assertEquals(unicode, Punycode.decode(punycode))\n    assertEquals(punycode, Punycode.encode(unicode))\n  }\n\n  private fun testDecodeOnly(\n    unicode: String,\n    punycode: String,\n  ) {\n    assertEquals(unicode, Punycode.decode(punycode))\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/io/FaultyFileSystem.kt",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.io\n\nimport java.io.IOException\nimport okio.Buffer\nimport okio.FileSystem\nimport okio.ForwardingFileSystem\nimport okio.ForwardingSink\nimport okio.Path\nimport okio.Sink\n\nclass FaultyFileSystem constructor(\n  delegate: FileSystem?,\n) : ForwardingFileSystem(delegate!!) {\n  private val writeFaults: MutableSet<Path> = LinkedHashSet()\n  private val deleteFaults: MutableSet<Path> = LinkedHashSet()\n  private val renameFaults: MutableSet<Path> = LinkedHashSet()\n\n  fun setFaultyWrite(\n    file: Path,\n    faulty: Boolean,\n  ) {\n    if (faulty) {\n      writeFaults.add(file)\n    } else {\n      writeFaults.remove(file)\n    }\n  }\n\n  fun setFaultyDelete(\n    file: Path,\n    faulty: Boolean,\n  ) {\n    if (faulty) {\n      deleteFaults.add(file)\n    } else {\n      deleteFaults.remove(file)\n    }\n  }\n\n  fun setFaultyRename(\n    file: Path,\n    faulty: Boolean,\n  ) {\n    if (faulty) {\n      renameFaults.add(file)\n    } else {\n      renameFaults.remove(file)\n    }\n  }\n\n  @Throws(IOException::class)\n  override fun atomicMove(\n    source: Path,\n    target: Path,\n  ) {\n    if (renameFaults.contains(source) || renameFaults.contains(target)) throw IOException(\"boom!\")\n    super.atomicMove(source, target)\n  }\n\n  @Throws(IOException::class)\n  override fun delete(\n    path: Path,\n    mustExist: Boolean,\n  ) {\n    if (deleteFaults.contains(path)) throw IOException(\"boom!\")\n    super.delete(path, mustExist)\n  }\n\n  @Throws(IOException::class)\n  override fun deleteRecursively(\n    fileOrDirectory: Path,\n    mustExist: Boolean,\n  ) {\n    if (deleteFaults.contains(fileOrDirectory)) throw IOException(\"boom!\")\n    super.deleteRecursively(fileOrDirectory, mustExist)\n  }\n\n  override fun appendingSink(\n    file: Path,\n    mustExist: Boolean,\n  ): Sink = FaultySink(super.appendingSink(file, mustExist), file)\n\n  override fun sink(\n    file: Path,\n    mustCreate: Boolean,\n  ): Sink = FaultySink(super.sink(file, mustCreate), file)\n\n  inner class FaultySink(\n    sink: Sink,\n    private val file: Path,\n  ) : ForwardingSink(sink) {\n    override fun write(\n      source: Buffer,\n      byteCount: Long,\n    ) {\n      if (writeFaults.contains(file)) {\n        throw IOException(\"boom!\")\n      } else {\n        super.write(source, byteCount)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/platform/Jdk8WithJettyBootPlatformTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport assertk.assertThat\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport okhttp3.internal.platform.Jdk8WithJettyBootPlatform.Companion.buildIfSupported\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.Assumptions.assumeFalse\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass Jdk8WithJettyBootPlatformTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @Test\n  fun testBuildsWithJettyBoot() {\n    assumeTrue(System.getProperty(\"java.specification.version\") == \"1.8\")\n    platform.assumeJettyBootEnabled()\n    assertThat(buildIfSupported()).isNotNull()\n  }\n\n  @Test\n  fun testNotBuildWithOther() {\n    assumeFalse(System.getProperty(\"java.specification.version\") == \"1.8\")\n    assertThat(buildIfSupported()).isNull()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/platform/Jdk9PlatformTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport javax.net.ssl.SSLSocket\nimport okhttp3.DelegatingSSLSocket\nimport okhttp3.internal.platform.Jdk9Platform.Companion.buildIfSupported\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass Jdk9PlatformTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @Test\n  fun buildsWhenJdk9() {\n    platform.assumeJdk9()\n    assertThat(buildIfSupported()).isNotNull()\n  }\n\n  @Test\n  fun buildsWhenJdk8() {\n    platform.assumeJdk8()\n    try {\n      SSLSocket::class.java.getMethod(\"getApplicationProtocol\")\n      // also present on JDK8 after build 252.\n      assertThat(buildIfSupported()).isNotNull()\n    } catch (nsme: NoSuchMethodException) {\n      assertThat(buildIfSupported()).isNull()\n    }\n  }\n\n  @Test\n  fun testToStringIsClassname() {\n    assertThat(Jdk9Platform().toString()).isEqualTo(\"Jdk9Platform\")\n  }\n\n  @Test\n  fun selectedProtocolIsNullWhenSslSocketThrowsExceptionForApplicationProtocol() {\n    platform.assumeJdk9()\n    val applicationProtocolUnsupported =\n      object : DelegatingSSLSocket(null) {\n        override fun getApplicationProtocol(): String = throw UnsupportedOperationException(\"Mock exception\")\n      }\n    assertThat(Jdk9Platform().getSelectedProtocol(applicationProtocolUnsupported)).isNull()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/platform/PlatformTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.platform\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport okhttp3.internal.platform.Platform.Companion.isAndroid\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass PlatformTest {\n  @RegisterExtension\n  var platform = PlatformRule()\n\n  @Test\n  fun alwaysBuilds() {\n    Platform()\n  }\n\n  /** Guard against the default value changing by accident.  */\n  @Test\n  fun defaultPrefix() {\n    assertThat(Platform().getPrefix()).isEqualTo(\"OkHttp\")\n  }\n\n  @Test\n  fun testToStringIsClassname() {\n    assertThat(Platform().toString()).isEqualTo(\"Platform\")\n  }\n\n  @Test\n  fun testNotAndroid() {\n    platform.assumeNotAndroid()\n\n    // This is tautological so just confirms that it runs.\n    assertThat(isAndroid).isEqualTo(false)\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/publicsuffix/PublicSuffixListGenerator.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport java.util.SortedSet\nimport java.util.TreeSet\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.coroutines.executeAsync\nimport okhttp3.internal.publicsuffix.ResourcePublicSuffixList.Companion.PUBLIC_SUFFIX_RESOURCE\nimport okio.BufferedSink\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.FileSystem\nimport okio.Path\nimport okio.Path.Companion.toPath\nimport okio.buffer\n\n/**\n * Downloads the public suffix list from https://publicsuffix.org/list/public_suffix_list.dat and\n * transforms the file into an efficient format used by OkHttp.\n *\n *\n * The intent is to use this class to update the list periodically by manually running the main\n * method. This should be run from the top-level okhttp directory.\n *\n *\n * The resulting file is used by [PublicSuffixDatabase].\n */\nclass PublicSuffixListGenerator(\n  projectRoot: Path = \".\".toPath(),\n  val fileSystem: FileSystem = FileSystem.SYSTEM,\n  val client: OkHttpClient = OkHttpClient(),\n) {\n  private val testResources = projectRoot / \"okhttp/src/jvmTest/resources\"\n  private val publicSuffixListDotDat = testResources / \"okhttp3/internal/publicsuffix/public_suffix_list.dat\"\n  private val outputFile = testResources / PUBLIC_SUFFIX_RESOURCE\n\n  val request = Request(\"https://publicsuffix.org/list/public_suffix_list.dat\".toHttpUrl())\n\n  suspend fun import() {\n    check(fileSystem.metadata(testResources).isDirectory)\n\n    updateLocalFile()\n\n    val importResults = readImportResults()\n\n    writeOutputFile(importResults)\n  }\n\n  private suspend fun updateLocalFile() =\n    withContext(Dispatchers.IO) {\n      client.newCall(request).executeAsync().use { response ->\n        fileSystem.sink(publicSuffixListDotDat).buffer().use { sink ->\n          sink.writeAll(response.body.source())\n        }\n      }\n    }\n\n  private suspend fun readImportResults(): ImportResults =\n    withContext(Dispatchers.IO) {\n      val sortedRules: SortedSet<ByteString> = TreeSet()\n      val sortedExceptionRules: SortedSet<ByteString> = TreeSet()\n      var totalRuleBytes = 0\n      var totalExceptionRuleBytes = 0\n\n      fileSystem.source(publicSuffixListDotDat).buffer().use { source ->\n        while (!source.exhausted()) {\n          var rule: ByteString = source.readUtf8LineStrict().toRule() ?: continue\n\n          if (rule.startsWith(EXCEPTION_RULE_MARKER)) {\n            rule = rule.substring(1)\n            // We use '\\n' for end of value.\n            totalExceptionRuleBytes += rule.size + 1\n            sortedExceptionRules.add(rule)\n          } else {\n            totalRuleBytes += rule.size + 1 // We use '\\n' for end of value.\n            sortedRules.add(rule)\n          }\n        }\n      }\n\n      ImportResults(sortedRules, sortedExceptionRules, totalRuleBytes, totalExceptionRuleBytes)\n    }\n\n  private fun String.toRule(): ByteString? {\n    if (trim { it <= ' ' }.isEmpty() || startsWith(\"//\")) return null\n    if (contains(WILDCARD_CHAR)) {\n      assertWildcardRule(this)\n    }\n    return encodeUtf8()\n  }\n\n  data class ImportResults(\n    val sortedRules: SortedSet<ByteString>,\n    val sortedExceptionRules: SortedSet<ByteString>,\n    val totalRuleBytes: Int,\n    val totalExceptionRuleBytes: Int,\n  ) {\n    fun writeOut(sink: BufferedSink) {\n      with(sink) {\n        writeInt(totalRuleBytes)\n        for (domain in sortedRules) {\n          write(domain).writeByte(NEWLINE)\n        }\n        writeInt(totalExceptionRuleBytes)\n        for (domain in sortedExceptionRules) {\n          write(domain).writeByte(NEWLINE)\n        }\n      }\n    }\n  }\n\n  private suspend fun writeOutputFile(importResults: ImportResults) =\n    withContext(Dispatchers.IO) {\n      fileSystem.write(outputFile) {\n        importResults.writeOut(this)\n      }\n    }\n\n  /**\n   * These assertions ensure the [PublicSuffixDatabase] remains correct. The specification is\n   * very flexible regarding wildcard rules, but this flexibility is not something currently used\n   * in practice. To simplify the implementation, we've avoided implementing the flexible rules in\n   * favor of supporting what's actually used in practice. That means if these assertions ever fail,\n   * the implementation will need to be revisited to support a more flexible rule.\n   */\n  private fun assertWildcardRule(rule: String) {\n    check(rule.startsWith(WILDCARD_CHAR)) {\n      \"\"\"Wildcard Assertion Failure: '$rule'\nA wildcard rule was added with a wildcard that is not in leftmost position! We'll need to change the ${PublicSuffixDatabase::class.java.name} to handle this.\"\"\"\n    }\n    check(rule.indexOf(WILDCARD_CHAR, 1) == -1) {\n      \"\"\"Wildcard Assertion Failure: '$rule'\nA wildcard rule was added with multiple wildcards! We'll need to change ${PublicSuffixDatabase::class.java.name} to handle this.\"\"\"\n    }\n    check(rule.length != 1) {\n      \"\"\"Wildcard Assertion Failure: '$rule'\nA wildcard rule was added that wildcards the first level! We'll need to change the ${PublicSuffixDatabase::class.java.name} to handle this.\"\"\"\n    }\n  }\n\n  companion object {\n    private const val NEWLINE = '\\n'.code\n    private const val WILDCARD_CHAR = \"*\"\n    private val EXCEPTION_RULE_MARKER: ByteString = \"!\".encodeUtf8()\n  }\n}\n\nsuspend fun main() {\n  val publicSuffixListGenerator = PublicSuffixListGenerator()\n\n  try {\n    publicSuffixListGenerator.import()\n  } finally {\n    publicSuffixListGenerator.client.run {\n      connectionPool.evictAll()\n      dispatcher.executorService.shutdownNow()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/publicsuffix/PublicSuffixTesting.jvm.kt",
    "content": "/*\n * Copyright (C) 2024 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.publicsuffix\n\nimport org.junit.runner.Runner\nimport org.junit.runner.notification.RunNotifier\nimport org.junit.runners.JUnit4\n\nactual class PublicSuffixTestRunner(\n  klass: Class<*>,\n) : Runner() {\n  private val delegate = JUnit4(klass)\n\n  override fun getDescription() = delegate.description\n\n  override fun run(notifier: RunNotifier?) = delegate.run(notifier)\n\n  override fun testCount() = delegate.testCount()\n}\n\nactual fun beforePublicSuffixTest() {\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/tls/CertificatePinnerChainValidationTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.tls\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.startsWith\nimport java.io.IOException\nimport java.security.SecureRandom\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.KeyManager\nimport javax.net.ssl.SSLHandshakeException\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManager\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect.ShutdownConnection\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CertificatePinner\nimport okhttp3.CertificatePinner.Companion.pin\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.RecordingHostnameVerifier\nimport okhttp3.Request\nimport okhttp3.internal.platform.Platform.Companion.get\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport okhttp3.tls.internal.TlsUtil.newKeyManager\nimport okhttp3.tls.internal.TlsUtil.newTrustManager\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass CertificatePinnerChainValidationTest {\n  @RegisterExtension\n  var platform = PlatformRule()\n\n  @RegisterExtension\n  var clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach\n  fun setup() {\n    platform.assumeNotBouncyCastle()\n  }\n\n  /**\n   * The pinner should pull the root certificate from the trust manager.\n   */\n  @Test\n  fun pinRootNotPresentInChain() {\n    // Fails on 11.0.1 https://github.com/square/okhttp/issues/4703\n    val rootCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .commonName(\"root\")\n        .build()\n    val intermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(rootCa)\n        .certificateAuthority(0)\n        .serialNumber(2L)\n        .commonName(\"intermediate_ca\")\n        .build()\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(intermediateCa)\n        .serialNumber(3L)\n        .commonName(server.hostName)\n        .build()\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(server.hostName, pin(rootCa.certificate))\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(rootCa.certificate)\n        .build()\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .certificatePinner(certificatePinner)\n        .build()\n    val serverHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(certificate, intermediateCa.certificate)\n        .build()\n    server.useHttps(serverHandshakeCertificates.sslSocketFactory())\n\n    // The request should complete successfully.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call1 =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response1 = call1.execute()\n    assertThat(response1.body.string()).isEqualTo(\"abc\")\n  }\n\n  /**\n   * The pinner should accept an intermediate from the server's chain.\n   */\n  @Test\n  fun pinIntermediatePresentInChain() {\n    // Fails on 11.0.1 https://github.com/square/okhttp/issues/4703\n    val rootCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .commonName(\"root\")\n        .build()\n    val intermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(rootCa)\n        .certificateAuthority(0)\n        .serialNumber(2L)\n        .commonName(\"intermediate_ca\")\n        .build()\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(intermediateCa)\n        .serialNumber(3L)\n        .commonName(server.hostName)\n        .build()\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(server.hostName, pin(intermediateCa.certificate))\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(rootCa.certificate)\n        .build()\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .certificatePinner(certificatePinner)\n        .build()\n    val serverHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(certificate, intermediateCa.certificate)\n        .build()\n    server.useHttps(serverHandshakeCertificates.sslSocketFactory())\n\n    // The request should complete successfully.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val call1 =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response1 = call1.execute()\n    assertThat(response1.body.string()).isEqualTo(\"abc\")\n    response1.close()\n\n    // Force a fresh connection for the next request.\n    client.connectionPool.evictAll()\n\n    // Confirm that a second request also succeeds. This should detect caching problems.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"def\")\n        .onResponseEnd(ShutdownConnection)\n        .build(),\n    )\n    val call2 =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response2 = call2.execute()\n    assertThat(response2.body.string()).isEqualTo(\"def\")\n    response2.close()\n  }\n\n  @Test\n  fun unrelatedPinnedLeafCertificateInChain() {\n    // https://github.com/square/okhttp/issues/4729\n    platform.expectFailureOnConscryptPlatform()\n    platform.expectFailureOnCorrettoPlatform()\n    platform.expectFailureOnLoomPlatform()\n\n    // Start with a trusted root CA certificate.\n    val rootCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .commonName(\"root\")\n        .build()\n\n    // Add a good intermediate CA, and have that issue a good certificate to localhost. Prepare an\n    // SSL context for an HTTP client under attack. It includes the trusted CA and a pinned\n    // certificate.\n    val goodIntermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(rootCa)\n        .certificateAuthority(0)\n        .serialNumber(2L)\n        .commonName(\"good_intermediate_ca\")\n        .build()\n    val goodCertificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(goodIntermediateCa)\n        .serialNumber(3L)\n        .commonName(server.hostName)\n        .build()\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(server.hostName, pin(goodCertificate.certificate))\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(rootCa.certificate)\n        .build()\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .certificatePinner(certificatePinner)\n        .build()\n\n    // Add a bad intermediate CA and have that issue a rogue certificate for localhost. Prepare\n    // an SSL context for an attacking webserver. It includes both these rogue certificates plus the\n    // trusted good certificate above. The attack is that by including the good certificate in the\n    // chain, we may trick the certificate pinner into accepting the rouge certificate.\n    val compromisedIntermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(rootCa)\n        .certificateAuthority(0)\n        .serialNumber(4L)\n        .commonName(\"bad_intermediate_ca\")\n        .build()\n    val rogueCertificate =\n      HeldCertificate\n        .Builder()\n        .serialNumber(5L)\n        .signedBy(compromisedIntermediateCa)\n        .commonName(server.hostName)\n        .build()\n    val socketFactory =\n      newServerSocketFactory(\n        rogueCertificate,\n        compromisedIntermediateCa.certificate,\n        goodCertificate.certificate,\n      )\n    server.useHttps(socketFactory)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .addHeader(\"Content-Type: text/plain\")\n        .build(),\n    )\n\n    // Make a request from client to server. It should succeed certificate checks (unfortunately the\n    // rogue CA is trusted) but it should fail certificate pinning.\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    assertFailsWith<SSLPeerUnverifiedException> {\n      call.execute()\n    }.also { expected ->\n      // Certificate pinning fails!\n      assertThat(expected.message!!).startsWith(\"Certificate pinning failure!\")\n    }\n  }\n\n  @Test\n  fun unrelatedPinnedIntermediateCertificateInChain() {\n    // https://github.com/square/okhttp/issues/4729\n    platform.expectFailureOnConscryptPlatform()\n    platform.expectFailureOnCorrettoPlatform()\n    platform.expectFailureOnLoomPlatform()\n\n    // Start with two root CA certificates, one is good and the other is compromised.\n    val rootCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .commonName(\"root\")\n        .build()\n    val compromisedRootCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .certificateAuthority(1)\n        .commonName(\"compromised_root\")\n        .build()\n\n    // Add a good intermediate CA, and have that issue a good certificate to localhost. Prepare an\n    // SSL context for an HTTP client under attack. It includes the trusted CA and a pinned\n    // certificate.\n    val goodIntermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(rootCa)\n        .certificateAuthority(0)\n        .serialNumber(3L)\n        .commonName(\"intermediate_ca\")\n        .build()\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(server.hostName, pin(goodIntermediateCa.certificate))\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(rootCa.certificate)\n        .addTrustedCertificate(compromisedRootCa.certificate)\n        .build()\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .certificatePinner(certificatePinner)\n        .build()\n\n    // The attacker compromises the root CA, issues an intermediate with the same common name\n    // \"intermediate_ca\" as the good CA. This signs a rogue certificate for localhost. The server\n    // serves the good CAs certificate in the chain, which means the certificate pinner sees a\n    // different set of certificates than the SSL verifier.\n    val compromisedIntermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(compromisedRootCa)\n        .certificateAuthority(0)\n        .serialNumber(4L)\n        .commonName(\"intermediate_ca\")\n        .build()\n    val rogueCertificate =\n      HeldCertificate\n        .Builder()\n        .serialNumber(5L)\n        .signedBy(compromisedIntermediateCa)\n        .commonName(server.hostName)\n        .build()\n    val socketFactory =\n      newServerSocketFactory(\n        rogueCertificate,\n        goodIntermediateCa.certificate,\n        compromisedIntermediateCa.certificate,\n      )\n    server.useHttps(socketFactory)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .addHeader(\"Content-Type: text/plain\")\n        .build(),\n    )\n\n    // Make a request from client to server. It should succeed certificate checks (unfortunately the\n    // rogue CA is trusted) but it should fail certificate pinning.\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      when (expected) {\n        is SSLHandshakeException -> {\n          // On Android, the handshake fails before the certificate pinner runs.\n          assertThat(expected.message!!).contains(\"Could not validate certificate\")\n        }\n\n        is SSLPeerUnverifiedException -> {\n          // On OpenJDK, the handshake succeeds but the certificate pinner fails.\n          assertThat(expected.message!!).startsWith(\"Certificate pinning failure!\")\n        }\n\n        else -> {\n          throw expected\n        }\n      }\n    }\n  }\n\n  /**\n   * Not checking the CA bit created a vulnerability in old OkHttp releases. It is exploited by\n   * triggering different chains to be discovered by the TLS engine and our chain cleaner. In this\n   * attack there's several different chains.\n   *\n   *\n   * The victim's gets a non-CA certificate signed by a CA, and pins the CA root and/or\n   * intermediate. This is business as usual.\n   *\n   * ```\n   *   pinnedRoot (trusted by CertificatePinner)\n   *     -> pinnedIntermediate (trusted by CertificatePinner)\n   *       -> realVictim\n   * ```\n   *\n   * The attacker compromises a CA. They take the public key from an intermediate certificate\n   * signed by the compromised CA's certificate and uses it in a non-CA certificate. They ask the\n   * pinned CA above to sign it for non-certificate-authority uses:\n   *\n   * ```\n   *   pinnedRoot (trusted by CertificatePinner)\n   *     -> pinnedIntermediate (trusted by CertificatePinner)\n   *         -> attackerSwitch\n   * ```\n   *\n   * The attacker serves a set of certificates that yields a too-long chain in our certificate\n   * pinner. The served certificates (incorrectly) formed a single chain to the pinner:\n   *\n   * ```\n   *   attackerCa\n   *     -> attackerIntermediate\n   *         -> pinnedRoot (trusted by CertificatePinner)\n   *             -> pinnedIntermediate (trusted by CertificatePinner)\n   *                 -> attackerSwitch (not a CA certificate!)\n   *                     -> phonyVictim\n   * ```\n   *\n   * But this chain is wrong because the attackerSwitch certificate is being used in a CA role even\n   * though it is not a CA certificate. There are pinned certificates in the chain! The correct\n   * chain is much shorter because it skips the non-CA certificate.\n   *\n   * ```\n   *   attackerCa\n   *     -> attackerIntermediate\n   *         -> phonyVictim\n   * ```\n   *\n   * Some implementations fail the TLS handshake when they see the long chain, and don't give\n   * CertificatePinner the opportunity to produce a different chain from their own. This includes\n   * the OpenJDK 11 TLS implementation, which itself fails the handshake when it encounters a non-CA\n   * certificate.\n   */\n  @Test\n  fun signersMustHaveCaBitSet() {\n    val attackerCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(4)\n        .commonName(\"attacker ca\")\n        .build()\n    val attackerIntermediate =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .certificateAuthority(3)\n        .commonName(\"attacker\")\n        .signedBy(attackerCa)\n        .build()\n    val pinnedRoot =\n      HeldCertificate\n        .Builder()\n        .serialNumber(3L)\n        .certificateAuthority(2)\n        .commonName(\"pinned root\")\n        .signedBy(attackerIntermediate)\n        .build()\n    val pinnedIntermediate =\n      HeldCertificate\n        .Builder()\n        .serialNumber(4L)\n        .certificateAuthority(1)\n        .commonName(\"pinned intermediate\")\n        .signedBy(pinnedRoot)\n        .build()\n    val attackerSwitch =\n      HeldCertificate\n        .Builder()\n        .serialNumber(5L)\n        .keyPair(attackerIntermediate.keyPair) // share keys between compromised CA and leaf!\n        .commonName(\"attacker\")\n        .addSubjectAlternativeName(\"attacker.com\")\n        .signedBy(pinnedIntermediate)\n        .build()\n    val phonyVictim =\n      HeldCertificate\n        .Builder()\n        .serialNumber(6L)\n        .signedBy(attackerSwitch)\n        .addSubjectAlternativeName(\"victim.com\")\n        .commonName(\"victim\")\n        .build()\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(server.hostName, pin(pinnedRoot.certificate))\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(pinnedRoot.certificate)\n        .addTrustedCertificate(attackerCa.certificate)\n        .build()\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .certificatePinner(certificatePinner)\n        .build()\n    val serverHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(\n          phonyVictim,\n          attackerSwitch.certificate,\n          pinnedIntermediate.certificate,\n          pinnedRoot.certificate,\n          attackerIntermediate.certificate,\n        ).build()\n    server.useHttps(serverHandshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse())\n\n    // Make a request from client to server. It should succeed certificate checks (unfortunately the\n    // rogue CA is trusted) but it should fail certificate pinning.\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      when (expected) {\n        is SSLPeerUnverifiedException -> {\n          // Certificate pinning fails!\n          assertThat(expected.message!!).startsWith(\"Certificate pinning failure!\")\n        }\n\n        is SSLHandshakeException -> {\n          // We didn't have the opportunity to do certificate pinning because the handshake failed.\n          assertThat(expected.message!!).contains(\"this is not a CA certificate\")\n        }\n\n        else -> {\n          throw expected\n        }\n      }\n    }\n  }\n\n  /**\n   * Attack the CA intermediates check by presenting unrelated chains to the handshake vs.\n   * certificate pinner.\n   *\n   * This chain is valid but not pinned:\n   *\n   * ```\n   *   attackerCa\n   *    -> phonyVictim\n   * ```\n   *\n   *\n   * This chain is pinned but not valid:\n   *\n   * ```\n   *   attackerCa\n   *     -> pinnedRoot (trusted by CertificatePinner)\n   *         -> compromisedIntermediate (max intermediates: 0)\n   *             -> attackerIntermediate (max intermediates: 0)\n   *                 -> phonyVictim\n   * ```\n   */\n  @Test\n  fun intermediateMustNotHaveMoreIntermediatesThanSigner() {\n    val attackerCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(2)\n        .commonName(\"attacker ca\")\n        .build()\n    val pinnedRoot =\n      HeldCertificate\n        .Builder()\n        .serialNumber(2L)\n        .certificateAuthority(1)\n        .commonName(\"pinned root\")\n        .signedBy(attackerCa)\n        .build()\n    val compromisedIntermediate =\n      HeldCertificate\n        .Builder()\n        .serialNumber(3L)\n        .certificateAuthority(0)\n        .commonName(\"compromised intermediate\")\n        .signedBy(pinnedRoot)\n        .build()\n    val attackerIntermediate =\n      HeldCertificate\n        .Builder()\n        .keyPair(attackerCa.keyPair) // Share keys between compromised CA and intermediate!\n        .serialNumber(4L)\n        .certificateAuthority(0) // More intermediates than permitted by signer!\n        .commonName(\"attacker intermediate\")\n        .signedBy(compromisedIntermediate)\n        .build()\n    val phonyVictim =\n      HeldCertificate\n        .Builder()\n        .serialNumber(5L)\n        .signedBy(attackerIntermediate)\n        .addSubjectAlternativeName(\"victim.com\")\n        .commonName(\"victim\")\n        .build()\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(server.hostName, pin(pinnedRoot.certificate))\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(pinnedRoot.certificate)\n        .addTrustedCertificate(attackerCa.certificate)\n        .build()\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .certificatePinner(certificatePinner)\n        .build()\n    val serverHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(\n          phonyVictim,\n          attackerIntermediate.certificate,\n          compromisedIntermediate.certificate,\n          pinnedRoot.certificate,\n        ).build()\n    server.useHttps(serverHandshakeCertificates.sslSocketFactory())\n    server.enqueue(MockResponse())\n\n    // Make a request from client to server. It should not succeed certificate checks.\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .build()\n    val call = client.newCall(request)\n    assertFailsWith<SSLHandshakeException> {\n      call.execute()\n    }\n  }\n\n  @Test\n  fun lonePinnedCertificate() {\n    val onlyCertificate =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .commonName(\"root\")\n        .build()\n    val certificatePinner =\n      CertificatePinner\n        .Builder()\n        .add(server.hostName, pin(onlyCertificate.certificate))\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(onlyCertificate.certificate)\n        .build()\n    val client =\n      clientTestRule\n        .newClientBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .certificatePinner(certificatePinner)\n        .build()\n    val serverHandshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .heldCertificate(onlyCertificate)\n        .build()\n    server.useHttps(serverHandshakeCertificates.sslSocketFactory())\n\n    // The request should complete successfully.\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call1 =\n      client.newCall(\n        Request\n          .Builder()\n          .url(server.url(\"/\"))\n          .build(),\n      )\n    val response1 = call1.execute()\n    assertThat(response1.body.string()).isEqualTo(\"abc\")\n  }\n\n  private fun newServerSocketFactory(\n    heldCertificate: HeldCertificate,\n    vararg intermediates: X509Certificate,\n  ): SSLSocketFactory {\n    // Test setup fails on JDK9\n    // java.security.KeyStoreException: Certificate chain is not valid\n    // at sun.security.pkcs12.PKCS12KeyStore.setKeyEntry\n    // http://openjdk.java.net/jeps/229\n    // http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/2c1c21d11e58/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java#l596\n    val keystoreType = if (platform.isJdk9()) \"JKS\" else null\n    val x509KeyManager = newKeyManager(keystoreType, heldCertificate, *intermediates)\n    val trustManager =\n      newTrustManager(\n        keystoreType,\n        emptyList(),\n        emptyList(),\n      )\n    val sslContext = get().newSSLContext()\n    sslContext.init(\n      arrayOf<KeyManager>(x509KeyManager),\n      arrayOf<TrustManager>(trustManager),\n      SecureRandom(),\n    )\n    return sslContext.socketFactory\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/tls/ClientAuthTest.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.tls\n\nimport assertk.assertThat\nimport assertk.assertions.endsWith\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.startsWith\nimport java.io.IOException\nimport java.net.SocketException\nimport java.security.GeneralSecurityException\nimport java.security.SecureRandom\nimport java.security.cert.X509Certificate\nimport java.util.Arrays\nimport javax.net.ssl.KeyManager\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLException\nimport javax.net.ssl.SSLHandshakeException\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManager\nimport javax.security.auth.x500.X500Principal\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CallEvent.CallFailed\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.SecureConnectStart\nimport okhttp3.EventRecorder\nimport okhttp3.OkHttpClient\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Request\nimport okhttp3.internal.http2.ConnectionShutdownException\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport okhttp3.tls.internal.TlsUtil.newKeyManager\nimport okhttp3.tls.internal.TlsUtil.newTrustManager\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junitpioneer.jupiter.RetryingTest\n\n@Tag(\"Slowish\")\nclass ClientAuthTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private lateinit var serverRootCa: HeldCertificate\n  private lateinit var serverIntermediateCa: HeldCertificate\n  private lateinit var serverCert: HeldCertificate\n  private lateinit var clientRootCa: HeldCertificate\n  private lateinit var clientIntermediateCa: HeldCertificate\n  private lateinit var clientCert: HeldCertificate\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n    platform.assumeNotBouncyCastle()\n    serverRootCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .commonName(\"root\")\n        .addSubjectAlternativeName(\"root_ca.com\")\n        .build()\n    serverIntermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(serverRootCa)\n        .certificateAuthority(0)\n        .serialNumber(2L)\n        .commonName(\"intermediate_ca\")\n        .addSubjectAlternativeName(\"intermediate_ca.com\")\n        .build()\n    serverCert =\n      HeldCertificate\n        .Builder()\n        .signedBy(serverIntermediateCa)\n        .serialNumber(3L)\n        .commonName(\"Local Host\")\n        .addSubjectAlternativeName(server.hostName)\n        .build()\n    clientRootCa =\n      HeldCertificate\n        .Builder()\n        .serialNumber(1L)\n        .certificateAuthority(1)\n        .commonName(\"root\")\n        .addSubjectAlternativeName(\"root_ca.com\")\n        .build()\n    clientIntermediateCa =\n      HeldCertificate\n        .Builder()\n        .signedBy(serverRootCa)\n        .certificateAuthority(0)\n        .serialNumber(2L)\n        .commonName(\"intermediate_ca\")\n        .addSubjectAlternativeName(\"intermediate_ca.com\")\n        .build()\n    clientCert =\n      HeldCertificate\n        .Builder()\n        .signedBy(clientIntermediateCa)\n        .serialNumber(4L)\n        .commonName(\"Jethro Willis\")\n        .addSubjectAlternativeName(\"jethrowillis.com\")\n        .build()\n  }\n\n  @Test\n  fun clientAuthForWants() {\n    val client = buildClient(clientCert, clientIntermediateCa.certificate)\n    val socketFactory = buildServerSslSocketFactory()\n    server.useHttps(socketFactory)\n    server.requestClientAuth()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    val response = call.execute()\n    assertThat(response.handshake!!.peerPrincipal)\n      .isEqualTo(X500Principal(\"CN=Local Host\"))\n    assertThat(response.handshake!!.localPrincipal)\n      .isEqualTo(X500Principal(\"CN=Jethro Willis\"))\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun clientAuthForNeeds() {\n    val client = buildClient(clientCert, clientIntermediateCa.certificate)\n    val socketFactory = buildServerSslSocketFactory()\n    server.useHttps(socketFactory)\n    server.requireClientAuth()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    val response = call.execute()\n    assertThat(response.handshake!!.peerPrincipal).isEqualTo(\n      X500Principal(\"CN=Local Host\"),\n    )\n    assertThat(response.handshake!!.localPrincipal).isEqualTo(\n      X500Principal(\"CN=Jethro Willis\"),\n    )\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun clientAuthSkippedForNone() {\n    val client = buildClient(clientCert, clientIntermediateCa.certificate)\n    val socketFactory = buildServerSslSocketFactory()\n    server.useHttps(socketFactory)\n    server.noClientAuth()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    val response = call.execute()\n    assertThat(response.handshake!!.peerPrincipal).isEqualTo(\n      X500Principal(\"CN=Local Host\"),\n    )\n    assertThat(response.handshake!!.localPrincipal).isNull()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n  }\n\n  @Test\n  fun missingClientAuthSkippedForWantsOnly() {\n    val client = buildClient(null, clientIntermediateCa.certificate)\n    val socketFactory = buildServerSslSocketFactory()\n    server.useHttps(socketFactory)\n    server.requestClientAuth()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    val response = call.execute()\n    assertThat(response.handshake!!.peerPrincipal).isEqualTo(\n      X500Principal(\"CN=Local Host\"),\n    )\n    assertThat(response.handshake!!.localPrincipal).isNull()\n    assertThat(response.body.string()).isEqualTo(\"abc\")\n  }\n\n  @Flaky\n  @RetryingTest(5)\n  fun missingClientAuthFailsForNeeds() {\n    // Fails with 11.0.1 https://github.com/square/okhttp/issues/4598\n    // StreamReset stream was reset: PROT...\n    val client = buildClient(null, clientIntermediateCa.certificate)\n    val socketFactory = buildServerSslSocketFactory()\n    server.useHttps(socketFactory)\n    server.requireClientAuth()\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      when (expected) {\n        is SSLHandshakeException -> {\n          // JDK 11+\n        }\n\n        is SSLException -> {\n          // javax.net.ssl.SSLException: readRecord\n        }\n\n        is SocketException -> {\n          // Conscrypt, JDK 8 (>= 292), JDK 9\n        }\n\n        else -> {\n          assertThat(expected.message).isEqualTo(\"exhausted all routes\")\n        }\n      }\n    }\n  }\n\n  @Test\n  fun commonNameIsNotTrusted() {\n    serverCert =\n      HeldCertificate\n        .Builder()\n        .signedBy(serverIntermediateCa)\n        .serialNumber(3L)\n        .commonName(server.hostName)\n        .addSubjectAlternativeName(\"different-host.com\")\n        .build()\n    val client = buildClient(clientCert, clientIntermediateCa.certificate)\n    val socketFactory = buildServerSslSocketFactory()\n    server.useHttps(socketFactory)\n    server.requireClientAuth()\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    assertFailsWith<SSLPeerUnverifiedException> {\n      call.execute()\n    }\n  }\n\n  @Test\n  fun invalidClientAuthFails() {\n    // Fails with https://github.com/square/okhttp/issues/4598\n    // StreamReset stream was reset: PROT...\n    val clientCert2 =\n      HeldCertificate\n        .Builder()\n        .serialNumber(4L)\n        .commonName(\"Jethro Willis\")\n        .build()\n    val client = buildClient(clientCert2)\n    val socketFactory = buildServerSslSocketFactory()\n    server.useHttps(socketFactory)\n    server.requireClientAuth()\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    assertFailsWith<IOException> {\n      call.execute()\n    }.also { expected ->\n      when (expected) {\n        is SSLHandshakeException -> {\n          // JDK 11+\n        }\n\n        is SSLException -> {\n          // javax.net.ssl.SSLException: readRecord\n        }\n\n        is SocketException -> {\n          // Conscrypt, JDK 8 (>= 292), JDK 9\n        }\n\n        is ConnectionShutdownException -> {\n          // It didn't fail until it reached the application layer.\n        }\n\n        else -> {\n          assertThat(expected.message).isEqualTo(\"exhausted all routes\")\n        }\n      }\n    }\n  }\n\n  @Test\n  fun invalidClientAuthEvents() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"abc\")\n        .build(),\n    )\n    clientCert =\n      HeldCertificate\n        .Builder()\n        .signedBy(clientIntermediateCa)\n        .serialNumber(4L)\n        .commonName(\"Jethro Willis\")\n        .addSubjectAlternativeName(\"jethrowillis.com\")\n        .validityInterval(1, 2)\n        .build()\n    var client = buildClient(clientCert, clientIntermediateCa.certificate)\n    val eventRecorder = EventRecorder()\n    client =\n      client\n        .newBuilder()\n        .eventListener(eventRecorder.eventListener)\n        .build()\n    val socketFactory = buildServerSslSocketFactory()\n    server.useHttps(socketFactory)\n    server.requireClientAuth()\n    val call = client.newCall(Request.Builder().url(server.url(\"/\")).build())\n    assertFailsWith<IOException> {\n      call.execute()\n    }\n\n    // Observed Events are variable\n    // JDK 14\n    // CallStart, ProxySelectStart, ProxySelectEnd, DnsStart, DnsEnd, ConnectStart, SecureConnectStart,\n    // SecureConnectEnd, ConnectEnd, ConnectionAcquired, RequestHeadersStart, RequestHeadersEnd,\n    // ResponseFailed, ConnectionReleased, CallFailed\n    // JDK 8\n    // CallStart, ProxySelectStart, ProxySelectEnd, DnsStart, DnsEnd, ConnectStart, SecureConnectStart,\n    // ConnectFailed, CallFailed\n    // Gradle - JDK 11\n    // CallStart, ProxySelectStart, ProxySelectEnd, DnsStart, DnsEnd, ConnectStart, SecureConnectStart,\n    // SecureConnectEnd, ConnectFailed, CallFailed\n    val recordedEventTypes = eventRecorder.recordedEventTypes()\n    assertThat(recordedEventTypes).startsWith(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      SecureConnectStart::class,\n    )\n    assertThat(recordedEventTypes).endsWith(CallFailed::class)\n  }\n\n  private fun buildClient(\n    heldCertificate: HeldCertificate?,\n    vararg intermediates: X509Certificate,\n  ): OkHttpClient {\n    val builder =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(serverRootCa.certificate)\n    if (heldCertificate != null) {\n      builder.heldCertificate(heldCertificate, *intermediates)\n    }\n    val handshakeCertificates = builder.build()\n    return clientTestRule\n      .newClientBuilder()\n      .sslSocketFactory(\n        handshakeCertificates.sslSocketFactory(),\n        handshakeCertificates.trustManager,\n      ).build()\n  }\n\n  private fun buildServerSslSocketFactory(): SSLSocketFactory {\n    // The test uses JDK default SSL Context instead of the Platform provided one\n    // as Conscrypt seems to have some differences, we only want to test client side here.\n    return try {\n      val keyManager =\n        newKeyManager(\n          null,\n          serverCert,\n          serverIntermediateCa.certificate,\n        )\n      val trustManager =\n        newTrustManager(\n          null,\n          Arrays.asList(serverRootCa.certificate, clientRootCa.certificate),\n          emptyList(),\n        )\n      val sslContext = SSLContext.getInstance(\"TLS\")\n      sslContext.init(\n        arrayOf<KeyManager>(keyManager),\n        arrayOf<TrustManager>(trustManager),\n        SecureRandom(),\n      )\n      sslContext.socketFactory\n    } catch (e: GeneralSecurityException) {\n      throw AssertionError(e)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/tls/HostnameVerifierTest.kt",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with this\n * work for additional information regarding copyright ownership. The ASF\n * licenses this file to You under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\npackage okhttp3.internal.tls\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isTrue\nimport java.io.ByteArrayInputStream\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SSLSession\nimport javax.security.auth.x500.X500Principal\nimport okhttp3.FakeSSLSession\nimport okhttp3.OkHttpClient\nimport okhttp3.internal.canParseAsIpAddress\nimport okhttp3.internal.platform.Platform.Companion.isAndroid\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HeldCertificate\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n/**\n * Tests for our hostname verifier. Most of these tests are from AOSP, which itself includes tests\n * from the Apache HTTP Client test suite.\n */\nclass HostnameVerifierTest {\n  private val verifier = OkHostnameVerifier\n\n  @RegisterExtension\n  var platform = PlatformRule()\n\n  @Test fun verify() {\n    val session = FakeSSLSession()\n    assertThat(verifier.verify(\"localhost\", session)).isFalse()\n  }\n\n  @Test fun verifyCn() {\n    // CN=foo.com\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aQMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzE0MVoXDTI4MTEwNTE1MzE0MVowgaQx\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n        cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n        aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n        ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n        lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n        zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n        07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n        BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n        JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n        hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n        FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n        yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQC3jRmEya6sQCkmieULcvx8zz1euCk9\n        fSez7BEtki8+dmfMXe3K7sH0lI8f4jJR0rbSCjpmCQLYmzC3NxBKeJOW0RcjNBpO\n        c2JlGO9auXv2GDP4IYiXElLJ6VSqc8WvDikv0JmCCWm0Zga+bZbR/EWN5DeEtFdF\n        815CLpJZNcYwiYwGy/CVQ7w2TnXlG+mraZOz+owr+cL6J/ZesbdEWfjoS1+cUEhE\n        HwlNrAu8jlZ2UqSgskSWlhYdMTAP9CPHiUv9N7FcT58Itv/I4fKREINQYjDpvQcx\n        SaTYb9dr5sB4WLNglk7zxDtM80H518VvihTcP7FHL+Gn6g4j5fkI98+S\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"bar.com\", session)).isFalse()\n  }\n\n  @Test fun verifyNonAsciiCn() {\n    // CN=&#x82b1;&#x5b50;.co.jp\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIESzCCAzOgAwIBAgIJAIz+EYMBU6aTMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1NDIxNVoXDTI4MTEwNTE1NDIxNVowgakx\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n        cnRpZmljYXRlczEVMBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkB\n        FhZqdWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n        MIIBCgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjU\n        g4pNjYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQc\n        wHf0ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t\n        7iu1JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAn\n        AxK6q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArD\n        qUYxqJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG\n        CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV\n        HQ4EFgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLS\n        rNuzA1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBALJ27i3okV/KvlDp6KMID3gd\n        ITl68PyItzzx+SquF8gahMh016NX73z/oVZoVUNdftla8wPUB1GwIkAnGkhQ9LHK\n        spBdbRiCj0gMmLCsX8SrjFvr7cYb2cK6J/fJe92l1tg/7Y4o7V/s4JBe/cy9U9w8\n        a0ctuDmEBCgC784JMDtT67klRfr/2LlqWhlOEq7pUFxRLbhpquaAHSOjmIcWnVpw\n        9BsO7qe46hidgn39hKh1WjKK2VcL/3YRsC4wUi0PBtFW6ScMCuMhgIRXSPU55Rae\n        UIlOdPjjr1SUNWGId1rD7W16Scpwnknn310FNxFMHVI0GTGFkNdkilNCFJcIoRA=\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"\\u82b1\\u5b50.co.jp\", session)).isFalse()\n    assertThat(verifier.verify(\"a.\\u82b1\\u5b50.co.jp\", session)).isFalse()\n  }\n\n  @Test fun verifySubjectAlt() {\n    // CN=foo.com, subjectAlt=bar.com\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIEXDCCA0SgAwIBAgIJAIz+EYMBU6aRMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzYyOVoXDTI4MTEwNTE1MzYyOVowgaQx\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n        cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n        aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n        ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n        lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n        zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n        07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n        BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n        JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCG\n        SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n        FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n        A1LKh6YNPg0wEgYDVR0RBAswCYIHYmFyLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA\n        dQyprNZBmVnvuVWjV42sey/PTfkYShJwy1j0/jcFZR/ypZUovpiHGDO1DgL3Y3IP\n        zVQ26uhUsSw6G0gGRiaBDe/0LUclXZoJzXX1qpS55OadxW73brziS0sxRgGrZE/d\n        3g5kkio6IED47OP6wYnlmZ7EKP9cqjWwlnvHnnUcZ2SscoLNYs9rN9ccp8tuq2by\n        88OyhKwGjJfhOudqfTNZcDzRHx4Fzm7UsVaycVw4uDmhEHJrAsmMPpj/+XRK9/42\n        2xq+8bc6HojdtbCyug/fvBZvZqQXSmU8m8IVcMmWMz0ZQO8ee3QkBHMZfCy7P/kr\n        VbWx/uETImUu+NZg22ewEw==\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"bar.com\", session)).isTrue()\n    assertThat(verifier.verify(\"a.bar.com\", session)).isFalse()\n  }\n\n  /**\n   * Ignored due to incompatibilities between Android and Java on how non-ASCII subject alt names\n   * are parsed. Android fails to parse these, which means we fall back to the CN. The RI does parse\n   * them, so the CN is unused.\n   */\n  @Test fun verifyNonAsciiSubjectAlt() {\n    // Expecting actual:\n    //  [\"bar.com\", \"è±å­.co.jp\"]\n    // to contain exactly (and in same order):\n    //  [\"bar.com\", \"������.co.jp\"]\n    platform.assumeNotBouncyCastle()\n\n    // CN=foo.com, subjectAlt=bar.com, subjectAlt=&#x82b1;&#x5b50;.co.jp\n    // (hanako.co.jp in kanji)\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIEajCCA1KgAwIBAgIJAIz+EYMBU6aSMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzgxM1oXDTI4MTEwNTE1MzgxM1owgaQx\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n        cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n        aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n        ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n        lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n        zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n        07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n        BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n        JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBnjCBmzAJBgNVHRMEAjAAMCwGCWCG\n        SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n        FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n        A1LKh6YNPg0wIAYDVR0RBBkwF4IHYmFyLmNvbYIM6Iqx5a2QLmNvLmpwMA0GCSqG\n        SIb3DQEBBQUAA4IBAQBeZs7ZIYyKtdnVxVvdLgwySEPOE4pBSXii7XYv0Q9QUvG/\n        ++gFGQh89HhABzA1mVUjH5dJTQqSLFvRfqTHqLpxSxSWqMHnvRM4cPBkIRp/XlMK\n        PlXadYtJLPTgpbgvulA1ickC9EwlNYWnowZ4uxnfsMghW4HskBqaV+PnQ8Zvy3L0\n        12c7Cg4mKKS5pb1HdRuiD2opZ+Hc77gRQLvtWNS8jQvd/iTbh6fuvTKfAOFoXw22\n        sWIKHYrmhCIRshUNohGXv50m2o+1w9oWmQ6Dkq7lCjfXfUB4wIbggJjpyEtbNqBt\n        j4MC2x5rfsLKKqToKmNE7pFEgqwe8//Aar1b+Qj+\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    val peerCertificate = session.peerCertificates[0] as X509Certificate\n\n    if (isAndroid || platform.isConscrypt()) {\n      assertThat(certificateSANs(peerCertificate)).containsExactly(\"bar.com\")\n    } else {\n      assertThat(certificateSANs(peerCertificate)).containsExactly(\"bar.com\", \"������.co.jp\")\n    }\n\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.foo.com\", session)).isFalse()\n    // these checks test alternative subjects. The test data contains an\n    // alternative subject starting with a japanese kanji character. This is\n    // not supported by Android because the underlying implementation from\n    // harmony follows the definition from rfc 1034 page 10 for alternative\n    // subject names. This causes the code to drop all alternative subjects.\n    assertThat(verifier.verify(\"bar.com\", session)).isTrue()\n    assertThat(verifier.verify(\"a.bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.\\u82b1\\u5b50.co.jp\", session)).isFalse()\n  }\n\n  @Test fun verifySubjectAltOnly() {\n    // subjectAlt=foo.com\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIESjCCAzKgAwIBAgIJAIz+EYMBU6aYMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MjYxMFoXDTI4MTEwNTE2MjYxMFowgZIx\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n        cnRpZmljYXRlczElMCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNv\n        bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMhjr5aCPoyp0R1iroWA\n        fnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2BlYho4O84X244QrZTRl8kQbYt\n        xnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRyzerA/ZtrlUqf+lKo0uWcocxe\n        Rc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY07hNKXAb2odnVqgzcYiDkLV8\n        ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8BqnGd87xQU3FVZI4tbtkB+Kz\n        jD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiVJTxpTKqym93whYk93l3ocEe5\n        5c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM\n        IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86tso4gkJIFiza\n        0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0wEgYDVR0RBAsw\n        CYIHZm9vLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAjl78oMjzFdsMy6F1sGg/IkO8\n        tF5yUgPgFYrs41yzAca7IQu6G9qtFDJz/7ehh/9HoG+oqCCIHPuIOmS7Sd0wnkyJ\n        Y7Y04jVXIb3a6f6AgBkEFP1nOT0z6kjT7vkA5LJ2y3MiDcXuRNMSta5PYVnrX8aZ\n        yiqVUNi40peuZ2R8mAUSBvWgD7z2qWhF8YgDb7wWaFjg53I36vWKn90ZEti3wNCw\n        qAVqixM+J0qJmQStgAc53i2aTMvAQu3A3snvH/PHTBo+5UL72n9S1kZyNCsVf1Qo\n        n8jKTiRriEM+fMFlcgQP284EBFzYHyCXFb9O/hMjK2+6mY9euMB1U1aFFzM/Bg==\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.com\", session)).isTrue()\n    assertThat(verifier.verify(\"a.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"foo.com\", session)).isTrue()\n    assertThat(verifier.verify(\"a.foo.com\", session)).isFalse()\n  }\n\n  @Test fun verifyMultipleCn() {\n    // CN=foo.com, CN=bar.com, CN=&#x82b1;&#x5b50;.co.jp\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIEbzCCA1egAwIBAgIJAIz+EYMBU6aXMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTk0NVoXDTI4MTEwNTE2MTk0NVowgc0x\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n        cnRpZmljYXRlczEQMA4GA1UEAwwHZm9vLmNvbTEQMA4GA1UEAwwHYmFyLmNvbTEV\n        MBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGOv\n        loI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pNjYGViGjg7zhf\n        bjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0ZHLN6sD9m2uV\n        Sp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1JVjTuE0pcBva\n        h2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6q/wGqcZ3zvFB\n        TcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYxqJUlPGlMqrKb\n        3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf\n        Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86\n        tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0w\n        DQYJKoZIhvcNAQEFBQADggEBAGuZb8ai1NO2j4v3y9TLZvd5s0vh5/TE7n7RX+8U\n        y37OL5k7x9nt0mM1TyAKxlCcY+9h6frue8MemZIILSIvMrtzccqNz0V1WKgA+Orf\n        uUrabmn+CxHF5gpy6g1Qs2IjVYWA5f7FROn/J+Ad8gJYc1azOWCLQqSyfpNRLSvY\n        EriQFEV63XvkJ8JrG62b+2OT2lqT4OO07gSPetppdlSa8NBSKP6Aro9RIX1ZjUZQ\n        SpQFCfo02NO0uNRDPUdJx2huycdNb+AXHaO7eXevDLJ+QnqImIzxWiY6zLOdzjjI\n        VBMkLHmnP7SjGSQ3XA4ByrQOxfOUTyLyE7NuemhHppuQPxE=\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"\\u82b1\\u5b50.co.jp\", session)).isFalse()\n    assertThat(verifier.verify(\"a.\\u82b1\\u5b50.co.jp\", session)).isFalse()\n  }\n\n  @Test fun verifyWilcardCn() {\n    // CN=*.foo.com\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIESDCCAzCgAwIBAgIJAIz+EYMBU6aUMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTU1NVoXDTI4MTEwNTE2MTU1NVowgaYx\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n        cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n        dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n        CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n        jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n        ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n        JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n        q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n        qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCG\n        SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n        FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n        A1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBAH0ipG6J561UKUfgkeW7GvYwW98B\n        N1ZooWX+JEEZK7+Pf/96d3Ij0rw9ACfN4bpfnCq0VUNZVSYB+GthQ2zYuz7tf/UY\n        A6nxVgR/IjG69BmsBl92uFO7JTNtHztuiPqBn59pt+vNx4yPvno7zmxsfI7jv0ww\n        yfs+0FNm7FwdsC1k47GBSOaGw38kuIVWqXSAbL4EX9GkryGGOKGNh0qvAENCdRSB\n        G9Z6tyMbmfRY+dLSh3a9JwoEcBUso6EWYBakLbq4nG/nvYdYvG9ehrnLVwZFL82e\n        l3Q/RK95bnA6cuRClGusLad0e6bjkBzx/VQ3VarDEpAkTLUGVAa0CLXtnyc=\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"www.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"\\u82b1\\u5b50.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.b.foo.com\", session)).isFalse()\n  }\n\n  @Test fun verifyWilcardCnOnTld() {\n    // It's the CA's responsibility to not issue broad-matching certificates!\n    // CN=*.co.jp\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aVMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTYzMFoXDTI4MTEwNTE2MTYzMFowgaQx\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n        cnRpZmljYXRlczEQMA4GA1UEAxQHKi5jby5qcDElMCMGCSqGSIb3DQEJARYWanVs\n        aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n        ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n        lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n        zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n        07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n        BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n        JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n        hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n        FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n        yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQA0sWglVlMx2zNGvUqFC73XtREwii53\n        CfMM6mtf2+f3k/d8KXhLNySrg8RRlN11zgmpPaLtbdTLrmG4UdAHHYr8O4y2BBmE\n        1cxNfGxxechgF8HX10QV4dkyzp6Z1cfwvCeMrT5G/V1pejago0ayXx+GPLbWlNeZ\n        S+Kl0m3p+QplXujtwG5fYcIpaGpiYraBLx3Tadih39QN65CnAh/zRDhLCUzKyt9l\n        UGPLEUDzRHMPHLnSqT1n5UU5UDRytbjJPXzF+l/+WZIsanefWLsxnkgAuZe/oMMF\n        EJMryEzOjg4Tfuc5qM0EXoPcQ/JlheaxZ40p2IyHqbsWV4MRYuFH4bkM\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.co.jp\", session)).isFalse()\n    assertThat(verifier.verify(\"\\u82b1\\u5b50.co.jp\", session)).isFalse()\n  }\n\n  /**\n   * Previously ignored due to incompatibilities between Android and Java on how non-ASCII subject\n   * alt names are parsed. Android fails to parse these, which means we fall back to the CN.\n   * The RI does parse them, so the CN is unused.\n   */\n  @Test fun testWilcardNonAsciiSubjectAlt() {\n    // Expecting actual:\n    //  [\"*.bar.com\", \"*.è±å­.co.jp\"]\n    // to contain exactly (and in same order):\n    //  [\"*.bar.com\", \"*.������.co.jp\"]\n    platform.assumeNotBouncyCastle()\n\n    // CN=*.foo.com, subjectAlt=*.bar.com, subjectAlt=*.&#x82b1;&#x5b50;.co.jp\n    // (*.hanako.co.jp in kanji)\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIEcDCCA1igAwIBAgIJAIz+EYMBU6aWMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n        VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n        ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n        FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n        ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTczMVoXDTI4MTEwNTE2MTczMVowgaYx\n        CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n        IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n        cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n        dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n        CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n        jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n        ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n        JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n        q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n        qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo4GiMIGfMAkGA1UdEwQCMAAwLAYJ\n        YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud\n        DgQWBBSfFHe/Pzq2yjiCQkgWLNrQy16H2DAfBgNVHSMEGDAWgBR7mtqPkJlOUtKs\n        27MDUsqHpg0+DTAkBgNVHREEHTAbggkqLmJhci5jb22CDiou6Iqx5a2QLmNvLmpw\n        MA0GCSqGSIb3DQEBBQUAA4IBAQBobWC+D5/lx6YhX64CwZ26XLjxaE0S415ajbBq\n        DK7lz+Rg7zOE3GsTAMi+ldUYnhyz0wDiXB8UwKXl0SDToB2Z4GOgqQjAqoMmrP0u\n        WB6Y6dpkfd1qDRUzI120zPYgSdsXjHW9q2H77iV238hqIU7qCvEz+lfqqWEY504z\n        hYNlknbUnR525ItosEVwXFBJTkZ3Yw8gg02c19yi8TAh5Li3Ad8XQmmSJMWBV4XK\n        qFr0AIZKBlg6NZZFf/0dP9zcKhzSriW27bY0XfzA6GSiRDXrDjgXq6baRT6YwgIg\n        pgJsDbJtZfHnV1nd3M6zOtQPm1TIQpNmMMMd/DPrGcUQerD3\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    val peerCertificate = session.peerCertificates[0] as X509Certificate\n    if (isAndroid || platform.isConscrypt()) {\n      assertThat(certificateSANs(peerCertificate)).containsExactly(\"*.bar.com\")\n    } else {\n      assertThat(certificateSANs(peerCertificate))\n        .containsExactly(\"*.bar.com\", \"*.������.co.jp\")\n    }\n\n    // try the foo.com variations\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"www.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"\\u82b1\\u5b50.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.b.foo.com\", session)).isFalse()\n    // these checks test alternative subjects. The test data contains an\n    // alternative subject starting with a japanese kanji character. This is\n    // not supported by Android because the underlying implementation from\n    // harmony follows the definition from rfc 1034 page 10 for alternative\n    // subject names. This causes the code to drop all alternative subjects.\n    assertThat(verifier.verify(\"bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"www.bar.com\", session)).isTrue()\n    assertThat(verifier.verify(\"\\u82b1\\u5b50.bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.b.bar.com\", session)).isFalse()\n  }\n\n  @Test fun subjectAltUsesLocalDomainAndIp() {\n    // cat cert.cnf\n    // [req]\n    // distinguished_name=distinguished_name\n    // req_extensions=req_extensions\n    // x509_extensions=x509_extensions\n    // [distinguished_name]\n    // [req_extensions]\n    // [x509_extensions]\n    // subjectAltName=DNS:localhost.localdomain,DNS:localhost,IP:127.0.0.1\n    //\n    // $ openssl req -x509 -nodes -days 36500 -subj '/CN=localhost' -config ./cert.cnf \\\n    //     -newkey rsa:512 -out cert.pem\n    val certificate =\n      certificate(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIBWDCCAQKgAwIBAgIJANS1EtICX2AZMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\n        BAMTCWxvY2FsaG9zdDAgFw0xMjAxMDIxOTA4NThaGA8yMTExMTIwOTE5MDg1OFow\n        FDESMBAGA1UEAxMJbG9jYWxob3N0MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPpt\n        atK8r4/hf4hSIs0os/BSlQLbRBaK9AfBReM4QdAklcQqe6CHsStKfI8pp0zs7Ptg\n        PmMdpbttL0O7mUboBC8CAwEAAaM1MDMwMQYDVR0RBCowKIIVbG9jYWxob3N0Lmxv\n        Y2FsZG9tYWlugglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEFBQADQQD0ntfL\n        DCzOCv9Ma6Lv5o5jcYWVxvBSTsnt22hsJpWD1K7iY9lbkLwl0ivn73pG2evsAn9G\n        X8YKH52fnHsCrhSD\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(certificate.subjectX500Principal).isEqualTo(\n      X500Principal(\"CN=localhost\"),\n    )\n    val session = FakeSSLSession(certificate)\n    assertThat(verifier.verify(\"localhost\", session)).isTrue()\n    assertThat(verifier.verify(\"localhost.localdomain\", session)).isTrue()\n    assertThat(verifier.verify(\"local.host\", session)).isFalse()\n    assertThat(verifier.verify(\"127.0.0.1\", session)).isTrue()\n    assertThat(verifier.verify(\"127.0.0.2\", session)).isFalse()\n  }\n\n  @Test fun wildcardsCannotMatchIpAddresses() {\n    // openssl req -x509 -nodes -days 36500 -subj '/CN=*.0.0.1' -newkey rsa:512 -out cert.pem\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIBkjCCATygAwIBAgIJAMdemqOwd/BEMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV\n        BAMUByouMC4wLjEwIBcNMTAxMjIwMTY0NDI1WhgPMjExMDExMjYxNjQ0MjVaMBIx\n        EDAOBgNVBAMUByouMC4wLjEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqY8c9Qrt\n        YPWCvb7lclI+aDHM6fgbJcHsS9Zg8nUOh5dWrS7AgeA25wyaokFl4plBbbHQe2j+\n        cCjsRiJIcQo9HwIDAQABo3MwcTAdBgNVHQ4EFgQUJ436TZPJvwCBKklZZqIvt1Yt\n        JjEwQgYDVR0jBDswOYAUJ436TZPJvwCBKklZZqIvt1YtJjGhFqQUMBIxEDAOBgNV\n        BAMUByouMC4wLjGCCQDHXpqjsHfwRDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB\n        BQUAA0EAk9i88xdjWoewqvE+iMC9tD2obMchgFDaHH0ogxxiRaIKeEly3g0uGxIt\n        fl2WRY8hb4x+zRrwsFaLEpdEvqcjOQ==\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"127.0.0.1\", session)).isFalse()\n  }\n\n  /**\n   * Earlier implementations of Android's hostname verifier required that wildcard names wouldn't\n   * match \"*.com\" or similar. This was a nonstandard check that we've since dropped. It is the CA's\n   * responsibility to not hand out certificates that match so broadly.\n   */\n  @Test fun wildcardsDoesNotNeedTwoDots() {\n    // openssl req -x509 -nodes -days 36500 -subj '/CN=*.com' -newkey rsa:512 -out cert.pem\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIBjDCCATagAwIBAgIJAOVulXCSu6HuMA0GCSqGSIb3DQEBBQUAMBAxDjAMBgNV\n        BAMUBSouY29tMCAXDTEwMTIyMDE2NDkzOFoYDzIxMTAxMTI2MTY0OTM4WjAQMQ4w\n        DAYDVQQDFAUqLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDJd8xqni+h7Iaz\n        ypItivs9kPuiJUqVz+SuJ1C05SFc3PmlRCvwSIfhyD67fHcbMdl+A/LrIjhhKZJe\n        1joO0+pFAgMBAAGjcTBvMB0GA1UdDgQWBBS4Iuzf5w8JdCp+EtBfdFNudf6+YzBA\n        BgNVHSMEOTA3gBS4Iuzf5w8JdCp+EtBfdFNudf6+Y6EUpBIwEDEOMAwGA1UEAxQF\n        Ki5jb22CCQDlbpVwkruh7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA0EA\n        U6LFxmZr31lFyis2/T68PpjAppc0DpNQuA2m/Y7oTHBDi55Fw6HVHCw3lucuWZ5d\n        qUYo4ES548JdpQtcLrW2sA==\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"google.com\", session)).isFalse()\n  }\n\n  @Test fun subjectAltName() {\n    // $ cat ./cert.cnf\n    // [req]\n    // distinguished_name=distinguished_name\n    // req_extensions=req_extensions\n    // x509_extensions=x509_extensions\n    // [distinguished_name]\n    // [req_extensions]\n    // [x509_extensions]\n    // subjectAltName=DNS:bar.com,DNS:baz.com\n    //\n    // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \\\n    //     -newkey rsa:512 -out cert.pem\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIBPTCB6KADAgECAgkA7zoHaaqNGHQwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n        AxMHZm9vLmNvbTAgFw0xMDEyMjAxODM5MzZaGA8yMTEwMTEyNjE4MzkzNlowEjEQ\n        MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+gmoSxF+8\n        hbV+rgRQqHIJd50216OWQJbU3BvdlPbca779NYO4+UZWTFdBM8BdQqs3H4B5Agvp\n        y7HeSff1F7XRAgMBAAGjHzAdMBsGA1UdEQQUMBKCB2Jhci5jb22CB2Jhei5jb20w\n        DQYJKoZIhvcNAQEFBQADQQBXpZZPOY2Dy1lGG81JTr8L4or9jpKacD7n51eS8iqI\n        oTznPNuXHU5bFN0AAGX2ij47f/EahqTpo5RdS95P4sVm\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"bar.com\", session)).isTrue()\n    assertThat(verifier.verify(\"baz.com\", session)).isTrue()\n    assertThat(verifier.verify(\"a.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"quux.com\", session)).isFalse()\n  }\n\n  @Test fun subjectAltNameWithWildcard() {\n    // $ cat ./cert.cnf\n    // [req]\n    // distinguished_name=distinguished_name\n    // req_extensions=req_extensions\n    // x509_extensions=x509_extensions\n    // [distinguished_name]\n    // [req_extensions]\n    // [x509_extensions]\n    // subjectAltName=DNS:bar.com,DNS:*.baz.com\n    //\n    // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \\\n    //     -newkey rsa:512 -out cert.pem\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIBPzCB6qADAgECAgkAnv/7Jv5r7pMwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n        AxMHZm9vLmNvbTAgFw0xMDEyMjAxODQ2MDFaGA8yMTEwMTEyNjE4NDYwMVowEjEQ\n        MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDAz2YXnyog\n        YdYLSFr/OEgSumtwqtZKJTB4wqTW/eKbBCEzxnyUMxWZIqUGu353PzwfOuWp2re3\n        nvVV+QDYQlh9AgMBAAGjITAfMB0GA1UdEQQWMBSCB2Jhci5jb22CCSouYmF6LmNv\n        bTANBgkqhkiG9w0BAQUFAANBAB8yrSl8zqy07i0SNYx2B/FnvQY734pxioaqFWfO\n        Bqo1ZZl/9aPHEWIwBrxYNVB0SGu/kkbt/vxqOjzzrkXukmI=\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"bar.com\", session)).isTrue()\n    assertThat(verifier.verify(\"a.baz.com\", session)).isTrue()\n    assertThat(verifier.verify(\"baz.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"a.bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"quux.com\", session)).isFalse()\n  }\n\n  @Test fun subjectAltNameWithIPAddresses() {\n    // $ cat ./cert.cnf\n    // [req]\n    // distinguished_name=distinguished_name\n    // req_extensions=req_extensions\n    // x509_extensions=x509_extensions\n    // [distinguished_name]\n    // [req_extensions]\n    // [x509_extensions]\n    // subjectAltName=IP:0:0:0:0:0:0:0:1,IP:2a03:2880:f003:c07:face:b00c::2,IP:0::5,IP:192.168.1.1\n    //\n    // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \\\n    //     -newkey rsa:512 -out cert.pem\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIBaDCCARKgAwIBAgIJALxN+AOBVGwQMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n        BAMMB2Zvby5jb20wIBcNMjAwMzIyMTEwNDI4WhgPMjEyMDAyMjcxMTA0MjhaMBIx\n        EDAOBgNVBAMMB2Zvby5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEAlnVbVfQ9\n        4aYjrPCcFuxOpjXuvyOc9Hcha4K7TfXyfsrjhAvCjCBIT/TiLOUVF3sx4yoCAtX8\n        wmt404tTbKD6UwIDAQABo0kwRzBFBgNVHREEPjA8hxAAAAAAAAAAAAAAAAAAAAAB\n        hxAqAyiA8AMMB/rOsAwAAAAChxAAAAAAAAAAAAAAAAAAAAAFhwTAqAEBMA0GCSqG\n        SIb3DQEBCwUAA0EAPSOYHJh7hB4ElBqTCAFW+T5Y7mXsv9nQjBJ7w0YIw83V2PEI\n        3KbBIyGTrqHD6lG8QGZy+yNkIcRlodG8OfQRUg==\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"::1\", session)).isTrue()\n    assertThat(verifier.verify(\"::2\", session)).isFalse()\n    assertThat(verifier.verify(\"::5\", session)).isTrue()\n    assertThat(verifier.verify(\"2a03:2880:f003:c07:face:b00c::2\", session)).isTrue()\n    assertThat(verifier.verify(\"2a03:2880:f003:c07:face:b00c:0:2\", session)).isTrue()\n    assertThat(verifier.verify(\"2a03:2880:f003:c07:FACE:B00C:0:2\", session)).isTrue()\n    assertThat(verifier.verify(\"2a03:2880:f003:c07:face:b00c:0:3\", session)).isFalse()\n    assertThat(verifier.verify(\"127.0.0.1\", session)).isFalse()\n    assertThat(verifier.verify(\"192.168.1.1\", session)).isTrue()\n    assertThat(verifier.verify(\"::ffff:192.168.1.1\", session)).isTrue()\n    assertThat(verifier.verify(\"0:0:0:0:0:FFFF:C0A8:0101\", session)).isTrue()\n  }\n\n  @Test fun generatedCertificate() {\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"Foo Corp\")\n        .addSubjectAlternativeName(\"foo.com\")\n        .build()\n    val session = session(heldCertificate.certificatePem())\n    assertThat(verifier.verify(\"foo.com\", session)).isTrue()\n    assertThat(verifier.verify(\"bar.com\", session)).isFalse()\n  }\n\n  @Test fun specialKInHostname() {\n    // https://github.com/apache/httpcomponents-client/commit/303e435d7949652ea77a6c50df1c548682476b6e\n    // https://www.gosecure.net/blog/2020/10/27/weakness-in-java-tls-host-verification/\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"Foo Corp\")\n        .addSubjectAlternativeName(\"k.com\")\n        .addSubjectAlternativeName(\"tel.com\")\n        .build()\n    val session = session(heldCertificate.certificatePem())\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"k.com\", session)).isTrue()\n    assertThat(verifier.verify(\"K.com\", session)).isTrue()\n    assertThat(verifier.verify(\"\\u2121.com\", session)).isFalse()\n    assertThat(verifier.verify(\"℡.com\", session)).isFalse()\n\n    // These should ideally be false, but we know that hostname is usually already checked by us\n    assertThat(verifier.verify(\"\\u212A.com\", session)).isFalse()\n    // Kelvin character below\n    assertThat(verifier.verify(\"K.com\", session)).isFalse()\n  }\n\n  @Test fun specialKInCert() {\n    // https://github.com/apache/httpcomponents-client/commit/303e435d7949652ea77a6c50df1c548682476b6e\n    // https://www.gosecure.net/blog/2020/10/27/weakness-in-java-tls-host-verification/\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"Foo Corp\")\n        .addSubjectAlternativeName(\"\\u2121.com\")\n        .addSubjectAlternativeName(\"\\u212A.com\")\n        .build()\n    val session = session(heldCertificate.certificatePem())\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"k.com\", session)).isFalse()\n    assertThat(verifier.verify(\"K.com\", session)).isFalse()\n    assertThat(verifier.verify(\"tel.com\", session)).isFalse()\n    assertThat(verifier.verify(\"k.com\", session)).isFalse()\n  }\n\n  @Test fun specialKInExternalCert() {\n    // OpenJDK related test.\n    platform.assumeNotConscrypt()\n\n    // Expecting actual:\n    //  [\"â¡.com\", \"âª.com\"]\n    // to contain exactly (and in same order):\n    //  [\"���.com\", \"���.com\"]\n    platform.assumeNotBouncyCastle()\n\n    // $ cat ./cert.cnf\n    // [req]\n    // distinguished_name=distinguished_name\n    // req_extensions=req_extensions\n    // x509_extensions=x509_extensions\n    // [distinguished_name]\n    // [req_extensions]\n    // [x509_extensions]\n    // subjectAltName=DNS:℡.com,DNS:K.com\n    //\n    // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \\\n    //     -newkey rsa:512 -out cert.pem\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIBSDCB86ADAgECAhRLR4TGgXBegg0np90FZ1KPeWpDtjANBgkqhkiG9w0BAQsF\n        ADASMRAwDgYDVQQDDAdmb28uY29tMCAXDTIwMTAyOTA2NTkwNVoYDzIxMjAxMDA1\n        MDY1OTA1WjASMRAwDgYDVQQDDAdmb28uY29tMFwwDQYJKoZIhvcNAQEBBQADSwAw\n        SAJBALQcTVW9aW++ClIV9/9iSzijsPvQGEu/FQOjIycSrSIheZyZmR8bluSNBq0C\n        9fpalRKZb0S2tlCTi5WoX8d3K30CAwEAAaMfMB0wGwYDVR0RBBQwEoIH4oShLmNv\n        bYIH4oSqLmNvbTANBgkqhkiG9w0BAQsFAANBAA1+/eDvSUGv78iEjNW+1w3OPAwt\n        Ij1qLQ/YI8OogZPMk7YY46/ydWWp7UpD47zy/vKmm4pOc8Glc8MoDD6UADs=\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n    val peerCertificate = session.peerCertificates[0] as X509Certificate\n    if (isAndroid) {\n      assertThat(certificateSANs(peerCertificate)).containsExactly()\n    } else {\n      assertThat(certificateSANs(peerCertificate)).containsExactly(\"���.com\", \"���.com\")\n    }\n    assertThat(verifier.verify(\"tel.com\", session)).isFalse()\n    assertThat(verifier.verify(\"k.com\", session)).isFalse()\n    assertThat(verifier.verify(\"foo.com\", session)).isFalse()\n    assertThat(verifier.verify(\"bar.com\", session)).isFalse()\n    assertThat(verifier.verify(\"k.com\", session)).isFalse()\n    assertThat(verifier.verify(\"K.com\", session)).isFalse()\n  }\n\n  private fun certificateSANs(peerCertificate: X509Certificate): List<String> =\n    when (val subjectAlternativeNames = peerCertificate.subjectAlternativeNames) {\n      null -> listOf()\n      else -> subjectAlternativeNames.map { c: List<*> -> c[1] as String }\n    }\n\n  @Test fun replacementCharacter() {\n    // $ cat ./cert.cnf\n    // [req]\n    // distinguished_name=distinguished_name\n    // req_extensions=req_extensions\n    // x509_extensions=x509_extensions\n    // [distinguished_name]\n    // [req_extensions]\n    // [x509_extensions]\n    // subjectAltName=DNS:℡.com,DNS:K.com\n    //\n    // $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \\\n    //     -newkey rsa:512 -out cert.pem\n    val session =\n      session(\n        \"\"\"\n        -----BEGIN CERTIFICATE-----\n        MIIBSDCB86ADAgECAhRLR4TGgXBegg0np90FZ1KPeWpDtjANBgkqhkiG9w0BAQsF\n        ADASMRAwDgYDVQQDDAdmb28uY29tMCAXDTIwMTAyOTA2NTkwNVoYDzIxMjAxMDA1\n        MDY1OTA1WjASMRAwDgYDVQQDDAdmb28uY29tMFwwDQYJKoZIhvcNAQEBBQADSwAw\n        SAJBALQcTVW9aW++ClIV9/9iSzijsPvQGEu/FQOjIycSrSIheZyZmR8bluSNBq0C\n        9fpalRKZb0S2tlCTi5WoX8d3K30CAwEAAaMfMB0wGwYDVR0RBBQwEoIH4oShLmNv\n        bYIH4oSqLmNvbTANBgkqhkiG9w0BAQsFAANBAA1+/eDvSUGv78iEjNW+1w3OPAwt\n        Ij1qLQ/YI8OogZPMk7YY46/ydWWp7UpD47zy/vKmm4pOc8Glc8MoDD6UADs=\n        -----END CERTIFICATE-----\n        \"\"\".trimIndent(),\n      )\n\n    // Replacement characters are deliberate, from certificate loading.\n    assertThat(verifier.verify(\"���.com\", session)).isFalse()\n    assertThat(verifier.verify(\"℡.com\", session)).isFalse()\n  }\n\n  @Test fun thatCatchesErrorsWithBadSession() {\n    val localVerifier = OkHttpClient().hostnameVerifier\n\n    // Since this is public API, okhttp3.internal.tls.OkHostnameVerifier.verify is also\n    assertThat(verifier).isInstanceOf<OkHostnameVerifier>()\n    val handshakeCertificates = platform.localhostHandshakeCertificates()\n    val session = handshakeCertificates.sslContext().createSSLEngine().session\n    assertThat(localVerifier.verify(\"\\uD83D\\uDCA9.com\", session)).isFalse()\n  }\n\n  @Test fun verifyAsIpAddress() {\n    // IPv4\n    assertThat(\"127.0.0.1\".canParseAsIpAddress()).isTrue()\n    assertThat(\"1.2.3.4\".canParseAsIpAddress()).isTrue()\n\n    // IPv6\n    assertThat(\"::1\".canParseAsIpAddress()).isTrue()\n    assertThat(\"2001:db8::1\".canParseAsIpAddress()).isTrue()\n    assertThat(\"::192.168.0.1\".canParseAsIpAddress()).isTrue()\n    assertThat(\"::ffff:192.168.0.1\".canParseAsIpAddress()).isTrue()\n    assertThat(\"FEDC:BA98:7654:3210:FEDC:BA98:7654:3210\".canParseAsIpAddress()).isTrue()\n    assertThat(\"1080:0:0:0:8:800:200C:417A\".canParseAsIpAddress()).isTrue()\n    assertThat(\"1080::8:800:200C:417A\".canParseAsIpAddress()).isTrue()\n    assertThat(\"FF01::101\".canParseAsIpAddress()).isTrue()\n    assertThat(\"0:0:0:0:0:0:13.1.68.3\".canParseAsIpAddress()).isTrue()\n    assertThat(\"0:0:0:0:0:FFFF:129.144.52.38\".canParseAsIpAddress()).isTrue()\n    assertThat(\"::13.1.68.3\".canParseAsIpAddress()).isTrue()\n    assertThat(\"::FFFF:129.144.52.38\".canParseAsIpAddress()).isTrue()\n\n    // Hostnames\n    assertThat(\"go\".canParseAsIpAddress()).isFalse()\n    assertThat(\"localhost\".canParseAsIpAddress()).isFalse()\n    assertThat(\"squareup.com\".canParseAsIpAddress()).isFalse()\n    assertThat(\"www.nintendo.co.jp\".canParseAsIpAddress()).isFalse()\n  }\n\n  private fun certificate(certificate: String): X509Certificate =\n    CertificateFactory\n      .getInstance(\"X.509\")\n      .generateCertificate(ByteArrayInputStream(certificate.toByteArray()))\n      as X509Certificate\n\n  private fun session(certificate: String): SSLSession = FakeSSLSession(certificate(certificate))\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/ws/MessageDeflaterInflaterTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isLessThan\nimport java.io.EOFException\nimport java.util.zip.Deflater\nimport kotlin.test.assertFailsWith\nimport okhttp3.TestUtil.fragmentBuffer\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.DeflaterSink\nimport okio.use\nimport org.junit.jupiter.api.Test\n\ninternal class MessageDeflaterInflaterTest {\n  @Test fun `inflate golden value`() {\n    val inflater = MessageInflater(false)\n    val message = \"f248cdc9c957c8cc4bcb492cc9cccf530400\".decodeHex()\n    assertThat(inflater.inflate(message)).isEqualTo(\"Hello inflation!\".encodeUtf8())\n  }\n\n  /**\n   * We had a bug where self-finishing inflater streams would infinite loop!\n   * https://github.com/square/okhttp/issues/8078\n   */\n  @Test fun `inflate returns finished before bytesRead reaches input length`() {\n    val inflater = MessageInflater(false)\n    val message = \"53621260020000\".decodeHex()\n    assertThat(inflater.inflate(message)).isEqualTo(\"22021002\".decodeHex())\n  }\n\n  @Test fun `deflate golden value`() {\n    val deflater = MessageDeflater(false)\n    val deflated = deflater.deflate(\"Hello deflate!\".encodeUtf8())\n    assertThat(deflated.hex()).isEqualTo(\"f248cdc9c95748494dcb492c49550400\")\n  }\n\n  @Test fun `inflate deflate`() {\n    val deflater = MessageDeflater(false)\n    val inflater = MessageInflater(false)\n\n    val goldenValue = \"Hello deflate!\".repeat(100).encodeUtf8()\n\n    val deflated = deflater.deflate(goldenValue)\n    assertThat(deflated.size).isLessThan(goldenValue.size)\n    val inflated = inflater.inflate(deflated)\n\n    assertThat(inflated).isEqualTo(goldenValue)\n  }\n\n  @Test fun `inflate deflate empty message`() {\n    val deflater = MessageDeflater(false)\n    val inflater = MessageInflater(false)\n\n    val goldenValue = \"\".encodeUtf8()\n\n    val deflated = deflater.deflate(goldenValue)\n    assertThat(deflated).isEqualTo(\"00\".decodeHex())\n    val inflated = inflater.inflate(deflated)\n\n    assertThat(inflated).isEqualTo(goldenValue)\n  }\n\n  @Test fun `inflate deflate with context takeover`() {\n    val deflater = MessageDeflater(false)\n    val inflater = MessageInflater(false)\n\n    val goldenValue1 = \"Hello deflate!\".repeat(100).encodeUtf8()\n    val deflatedValue1 = deflater.deflate(goldenValue1)\n    assertThat(inflater.inflate(deflatedValue1)).isEqualTo(goldenValue1)\n\n    val goldenValue2 = \"Hello deflate?\".repeat(100).encodeUtf8()\n    val deflatedValue2 = deflater.deflate(goldenValue2)\n    assertThat(inflater.inflate(deflatedValue2)).isEqualTo(goldenValue2)\n\n    assertThat(deflatedValue2.size).isLessThan(deflatedValue1.size)\n  }\n\n  @Test fun `inflate deflate with no context takeover`() {\n    val deflater = MessageDeflater(true)\n    val inflater = MessageInflater(true)\n\n    val goldenValue1 = \"Hello deflate!\".repeat(100).encodeUtf8()\n    val deflatedValue1 = deflater.deflate(goldenValue1)\n    assertThat(inflater.inflate(deflatedValue1)).isEqualTo(goldenValue1)\n\n    val goldenValue2 = \"Hello deflate!\".repeat(100).encodeUtf8()\n    val deflatedValue2 = deflater.deflate(goldenValue2)\n    assertThat(inflater.inflate(deflatedValue2)).isEqualTo(goldenValue2)\n\n    assertThat(deflatedValue2).isEqualTo(deflatedValue1)\n  }\n\n  @Test fun `deflate after close`() {\n    val deflater = MessageDeflater(true)\n    deflater.close()\n\n    assertFailsWith<Exception> {\n      deflater.deflate(\"Hello deflate!\".encodeUtf8())\n    }\n  }\n\n  @Test fun `inflate after close`() {\n    val inflater = MessageInflater(false)\n\n    inflater.close()\n\n    assertFailsWith<Exception> {\n      inflater.inflate(\"f240e30300\".decodeHex())\n    }\n  }\n\n  /**\n   * Test for an [EOFException] caused by mishandling of fragmented buffers in web socket\n   * compression. https://github.com/square/okhttp/issues/5965\n   */\n  @Test fun `inflate golden value in buffer that has been fragmented`() {\n    val inflater = MessageInflater(false)\n    val buffer = fragmentBuffer(Buffer().write(\"f248cdc9c957c8cc4bcb492cc9cccf530400\".decodeHex()))\n    inflater.inflate(buffer)\n    assertThat(buffer.readUtf8()).isEqualTo(\"Hello inflation!\")\n  }\n\n  /**\n   * It's possible a self-terminating deflated message will contain trailing data that won't be\n   * processed during inflation. If this happens, we need to either reject the message or discard\n   * the unreachable data. We choose to discard it!\n   *\n   * In practice this could happen if the encoder doesn't strip the [0x00, 0x00, 0xff, 0xff] suffix\n   * and that ends up repeated.\n   *\n   * https://github.com/square/okhttp/issues/8551\n   */\n  @Test\n  fun `deflated data has too many bytes`() {\n    val inflater = MessageInflater(true)\n    val buffer = Buffer()\n\n    val message1 = \"hello\".encodeUtf8()\n    val message2 = \"hello 2\".encodeUtf8()\n\n    DeflaterSink(buffer, Deflater(Deflater.DEFAULT_COMPRESSION, true)).use { sink ->\n      sink.write(Buffer().write(message1), message1.size.toLong())\n    }\n    buffer.writeByte(0x00)\n    // Trailing data. We use the Okio segment size to make sure it's still in the input buffer.\n    buffer.write(ByteArray(8192))\n    inflater.inflate(buffer)\n    assertThat(buffer.readByteString()).isEqualTo(message1)\n\n    DeflaterSink(buffer, Deflater(Deflater.DEFAULT_COMPRESSION, true)).use { sink ->\n      sink.write(Buffer().write(message2), message2.size.toLong())\n    }\n    buffer.writeByte(0x00)\n    inflater.inflate(buffer)\n    assertThat(buffer.readByteString()).isEqualTo(message2)\n  }\n\n  private fun MessageDeflater.deflate(byteString: ByteString): ByteString {\n    val buffer = Buffer()\n    buffer.write(byteString)\n    deflate(buffer)\n    return buffer.readByteString()\n  }\n\n  private fun MessageInflater.inflate(byteString: ByteString): ByteString {\n    val buffer = Buffer()\n    buffer.write(byteString)\n    inflate(buffer)\n    return buffer.readByteString()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/ws/RealWebSocketTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isGreaterThan\nimport assertk.assertions.isLessThan\nimport assertk.assertions.isTrue\nimport java.io.EOFException\nimport java.io.IOException\nimport java.net.ProtocolException\nimport java.net.SocketTimeoutException\nimport java.util.Random\nimport kotlin.test.assertFailsWith\nimport okhttp3.FailingCall\nimport okhttp3.Headers\nimport okhttp3.Headers.Companion.headersOf\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.TestUtil.repeat\nimport okhttp3.internal.concurrent.TaskFaker\nimport okhttp3.internal.connection.BufferedSocket\nimport okhttp3.internal.ws.WebSocketExtensions.Companion.parse\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.ForwardingSink\nimport okio.ForwardingSource\nimport okio.Socket\nimport okio.buffer\nimport okio.inMemorySocketPair\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\n\n@Tag(\"Slow\")\nclass RealWebSocketTest {\n  // NOTE: Fields are named 'client' and 'server' for cognitive simplicity. This differentiation has\n  // zero effect on the behavior of the WebSocket API which is why tests are only written once\n  // from the perspective of a single peer.\n  private val random = Random(0)\n  private val taskFaker = TaskFaker()\n  private val sockets = inMemorySocketPair(8192L)\n  private val client = TestStreams(taskFaker, sockets[0], client = true)\n  private val server = TestStreams(taskFaker, sockets[1], client = false)\n\n  @BeforeEach\n  fun setUp() {\n    client.initWebSocket(random)\n    server.initWebSocket(random)\n  }\n\n  @AfterEach\n  @Throws(Exception::class)\n  fun tearDown() {\n    client.listener.assertExhausted()\n    server.listener.assertExhausted()\n    server.source.close()\n    client.source.close()\n    taskFaker.runTasks()\n    server.webSocket!!.tearDown()\n    client.webSocket!!.tearDown()\n    taskFaker.close()\n  }\n\n  @Test\n  fun close() {\n    client.webSocket!!.close(1000, \"Hello!\")\n    // This will trigger a close response.\n    assertThat(server.processNextFrame()).isFalse()\n    server.listener.assertClosing(1000, \"Hello!\")\n    server.webSocket!!.finishReader()\n    server.webSocket!!.close(1000, \"Goodbye!\")\n    assertThat(client.processNextFrame()).isFalse()\n    client.listener.assertClosing(1000, \"Goodbye!\")\n    client.webSocket!!.finishReader()\n    server.listener.assertClosed(1000, \"Hello!\")\n    client.listener.assertClosed(1000, \"Goodbye!\")\n  }\n\n  @Test\n  fun clientCloseThenMethodsReturnFalse() {\n    client.webSocket!!.close(1000, \"Hello!\")\n    assertThat(client.webSocket!!.close(1000, \"Hello!\")).isFalse()\n    assertThat(client.webSocket!!.send(\"Hello!\")).isFalse()\n  }\n\n  @Test\n  fun clientCloseWith0Fails() {\n    assertFailsWith<IllegalArgumentException> {\n      client.webSocket!!.close(0, null)\n    }.also { expected ->\n      assertThat(\"Code must be in range [1000,5000): 0\")\n        .isEqualTo(expected.message)\n    }\n  }\n\n  @Test\n  fun afterSocketClosedPingFailsWebSocket() {\n    server.source.close()\n    client.webSocket!!.pong(\"Ping!\".encodeUtf8())\n    taskFaker.runTasks()\n    client.listener.assertFailure(IOException::class.java, \"source is closed\")\n    assertThat(client.webSocket!!.send(\"Hello!\")).isFalse()\n  }\n\n  @Test\n  fun socketClosedDuringMessageKillsWebSocket() {\n    server.source.close()\n    assertThat(client.webSocket!!.send(\"Hello!\")).isTrue()\n    taskFaker.runTasks()\n    client.listener.assertFailure(IOException::class.java, \"source is closed\")\n\n    // A failed write prevents further use of the WebSocket instance.\n    assertThat(client.webSocket!!.send(\"Hello!\")).isFalse()\n    assertThat(client.webSocket!!.pong(\"Ping!\".encodeUtf8())).isFalse()\n  }\n\n  @Test\n  fun serverCloseThenWritingPingSucceeds() {\n    server.webSocket!!.close(1000, \"Hello!\")\n    client.processNextFrame()\n    client.listener.assertClosing(1000, \"Hello!\")\n    assertThat(client.webSocket!!.pong(\"Pong?\".encodeUtf8())).isTrue()\n  }\n\n  @Test\n  fun clientCanWriteMessagesAfterServerClose() {\n    server.webSocket!!.close(1000, \"Hello!\")\n    client.processNextFrame()\n    client.listener.assertClosing(1000, \"Hello!\")\n    assertThat(client.webSocket!!.send(\"Hi!\")).isTrue()\n    server.processNextFrame()\n    server.listener.assertTextMessage(\"Hi!\")\n  }\n\n  @Test\n  fun serverCloseThenClientClose() {\n    server.webSocket!!.close(1000, \"Hello!\")\n    client.processNextFrame()\n    client.listener.assertClosing(1000, \"Hello!\")\n    assertThat(client.webSocket!!.close(1000, \"Bye!\")).isTrue()\n    client.webSocket!!.finishReader()\n    taskFaker.runTasks()\n    client.listener.assertClosed(1000, \"Hello!\")\n    server.processNextFrame()\n    server.listener.assertClosing(1000, \"Bye!\")\n    server.webSocket!!.finishReader()\n    server.listener.assertClosed(1000, \"Bye!\")\n  }\n\n  @Test\n  fun emptyCloseInitiatesShutdown() {\n    server.sink.write(\"8800\".decodeHex()).emit() // Close without code.\n    client.processNextFrame()\n    client.listener.assertClosing(1005, \"\")\n    client.webSocket!!.finishReader()\n    assertThat(client.webSocket!!.close(1000, \"Bye!\")).isTrue()\n    server.processNextFrame()\n    server.listener.assertClosing(1000, \"Bye!\")\n    server.webSocket!!.finishReader()\n    client.listener.assertClosed(1005, \"\")\n  }\n\n  @Test\n  fun clientCloseClosesConnection() {\n    client.webSocket!!.close(1000, \"Hello!\")\n    taskFaker.runTasks()\n    assertThat(client.closed).isFalse()\n    server.processNextFrame() // Read client closing, send server close.\n    server.listener.assertClosing(1000, \"Hello!\")\n    server.webSocket!!.finishReader()\n    server.webSocket!!.close(1000, \"Goodbye!\")\n    client.processNextFrame() // Read server closing, close connection.\n    taskFaker.runTasks()\n    client.listener.assertClosing(1000, \"Goodbye!\")\n    client.webSocket!!.finishReader()\n    taskFaker.runTasks()\n    assertThat(client.closed).isTrue()\n\n    // Server and client both finished closing, connection is closed.\n    server.listener.assertClosed(1000, \"Hello!\")\n    client.listener.assertClosed(1000, \"Goodbye!\")\n  }\n\n  @Test\n  fun clientCloseCancelsConnectionAfterTimeout() {\n    client.webSocket!!.close(1000, \"Hello!\")\n    taskFaker.runTasks()\n    // Note: we don't process server frames so our client 'close' doesn't receive a server 'close'.\n    assertThat(client.canceled).isFalse()\n\n    taskFaker.advanceUntil(ns(RealWebSocket.CANCEL_AFTER_CLOSE_MILLIS - 1))\n    assertThat(client.canceled).isFalse()\n\n    taskFaker.advanceUntil(ns(RealWebSocket.CANCEL_AFTER_CLOSE_MILLIS))\n    assertThat(client.canceled).isTrue()\n\n    client.processNextFrame() // This won't get a frame, but it will get a closed pipe.\n    client.listener.assertFailure(IOException::class.java, \"canceled\")\n    taskFaker.runTasks()\n  }\n\n  @Test\n  fun clientCloseCancelsConnectionAfterCustomTimeout() {\n    client.initWebSocket(random, webSocketCloseTimeout = 5_000)\n    client.webSocket!!.close(1000, \"Hello!\")\n    taskFaker.runTasks()\n    // Note: we don't process server frames so our client 'close' doesn't receive a server 'close'.\n    assertThat(client.canceled).isFalse()\n\n    taskFaker.advanceUntil(ns(4_999))\n    assertThat(client.canceled).isFalse()\n\n    taskFaker.advanceUntil(ns(5_000))\n    assertThat(client.canceled).isTrue()\n\n    client.processNextFrame() // This won't get a frame, but it will get a closed pipe.\n    client.listener.assertFailure(IOException::class.java, \"canceled\")\n    taskFaker.runTasks()\n  }\n\n  @Test\n  fun serverCloseClosesConnection() {\n    server.webSocket!!.close(1000, \"Hello!\")\n    client.processNextFrame() // Read server close, send client close, close connection.\n    assertThat(client.closed).isFalse()\n    client.listener.assertClosing(1000, \"Hello!\")\n    client.webSocket!!.finishReader()\n    client.webSocket!!.close(1000, \"Hello!\")\n    server.processNextFrame()\n    server.listener.assertClosing(1000, \"Hello!\")\n    server.webSocket!!.finishReader()\n    client.listener.assertClosed(1000, \"Hello!\")\n    server.listener.assertClosed(1000, \"Hello!\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun clientAndServerCloseClosesConnection() {\n    // Send close from both sides at the same time.\n    server.webSocket!!.close(1000, \"Hello!\")\n    client.processNextFrame() // Read close, close connection close.\n    assertThat(client.closed).isFalse()\n    client.webSocket!!.close(1000, \"Hi!\")\n    server.processNextFrame()\n    client.listener.assertClosing(1000, \"Hello!\")\n    server.listener.assertClosing(1000, \"Hi!\")\n    client.webSocket!!.finishReader()\n    server.webSocket!!.finishReader()\n    client.listener.assertClosed(1000, \"Hello!\")\n    server.listener.assertClosed(1000, \"Hi!\")\n    taskFaker.runTasks()\n    assertThat(client.closed).isTrue()\n    server.listener.assertExhausted() // Client should not have sent second close.\n    client.listener.assertExhausted() // Server should not have sent second close.\n  }\n\n  @Test\n  fun serverCloseBreaksReadMessageLoop() {\n    server.webSocket!!.send(\"Hello!\")\n    server.webSocket!!.close(1000, \"Bye!\")\n    assertThat(client.processNextFrame()).isTrue()\n    client.listener.assertTextMessage(\"Hello!\")\n    assertThat(client.processNextFrame()).isFalse()\n    client.listener.assertClosing(1000, \"Bye!\")\n  }\n\n  @Test\n  fun protocolErrorBeforeCloseSendsFailure() {\n    server.sink.write(\"0a00\".decodeHex()).emit() // Invalid non-final ping frame.\n    client.processNextFrame() // Detects error, send close, close connection.\n    taskFaker.runTasks()\n    client.webSocket!!.finishReader()\n    assertThat(client.closed).isTrue()\n    client.listener.assertFailure(\n      ProtocolException::class.java,\n      \"Control frames must be final.\",\n    )\n    server.processNextFrame()\n    taskFaker.runTasks()\n    server.listener.assertFailure()\n  }\n\n  @Test\n  fun protocolErrorInCloseResponseClosesConnection() {\n    client.webSocket!!.close(1000, \"Hello\")\n    server.processNextFrame()\n    // Not closed until close reply is received.\n    assertThat(client.closed).isFalse()\n\n    // Manually write an invalid masked close frame.\n    server.sink.write(\"888760b420bb635c68de0cd84f\".decodeHex()).emit()\n    client.processNextFrame() // Detects error, disconnects immediately since close already sent.\n    client.webSocket!!.finishReader()\n    taskFaker.runTasks()\n    assertThat(client.closed).isTrue()\n    client.listener.assertFailure(\n      ProtocolException::class.java,\n      \"Server-sent frames must not be masked.\",\n    )\n    server.listener.assertClosing(1000, \"Hello\")\n    server.listener.assertExhausted() // Client should not have sent second close.\n  }\n\n  @Test\n  fun protocolErrorAfterCloseDoesNotSendClose() {\n    client.webSocket!!.close(1000, \"Hello!\")\n    server.processNextFrame()\n\n    // Not closed until close reply is received.\n    assertThat(client.closed).isFalse()\n    server.sink.write(\"0a00\".decodeHex()).emit() // Invalid non-final ping frame.\n    client.processNextFrame() // Detects error, disconnects immediately since close already sent.\n    client.webSocket!!.finishReader()\n    taskFaker.runTasks()\n    assertThat(client.closed).isTrue()\n    client.listener.assertFailure(\n      ProtocolException::class.java,\n      \"Control frames must be final.\",\n    )\n    server.listener.assertClosing(1000, \"Hello!\")\n    server.listener.assertExhausted() // Client should not have sent second close.\n  }\n\n  @Test\n  fun networkErrorReportedAsFailure() {\n    server.sink.close()\n    client.processNextFrame()\n    taskFaker.runTasks()\n    client.listener.assertFailure(EOFException::class.java)\n  }\n\n  @Test\n  fun closeThrowingFailsConnection() {\n    server.source.close()\n    client.webSocket!!.close(1000, null)\n    taskFaker.runTasks()\n    client.listener.assertFailure(IOException::class.java, \"source is closed\")\n  }\n\n  @Test\n  fun closeMessageAndConnectionCloseThrowingDoesNotMaskOriginal() {\n    // So when the client sends close it throws an IOException.\n    server.source.close()\n    client.webSocket!!.close(1000, \"Bye!\")\n    taskFaker.runTasks()\n    client.webSocket!!.finishReader()\n    client.listener.assertFailure(IOException::class.java, \"source is closed\")\n    assertThat(client.closed).isTrue()\n  }\n\n  @Test\n  fun pingOnInterval() {\n    client.initWebSocket(random, pingIntervalMillis = 500)\n    taskFaker.advanceUntil(ns(500L))\n    server.processNextFrame() // Ping.\n    client.processNextFrame() // Pong.\n    taskFaker.advanceUntil(ns(1000L))\n    server.processNextFrame() // Ping.\n    client.processNextFrame() // Pong.\n    taskFaker.advanceUntil(ns(1500L))\n    server.processNextFrame() // Ping.\n    client.processNextFrame() // Pong.\n  }\n\n  @Test\n  fun unacknowledgedPingFailsConnection() {\n    client.initWebSocket(random, pingIntervalMillis = 500)\n\n    // Don't process the ping and pong frames!\n    taskFaker.advanceUntil(ns(500L))\n    taskFaker.advanceUntil(ns(1000L))\n    client.listener.assertFailure(\n      SocketTimeoutException::class.java,\n      \"sent ping but didn't receive pong within 500ms (after 0 successful ping/pongs)\",\n    )\n  }\n\n  @Test\n  fun unexpectedPongsDoNotInterfereWithFailureDetection() {\n    client.initWebSocket(random, pingIntervalMillis = 500)\n\n    // At 0ms the server sends 3 unexpected pongs. The client accepts 'em and ignores em.\n    server.webSocket!!.pong(\"pong 1\".encodeUtf8())\n    client.processNextFrame()\n    server.webSocket!!.pong(\"pong 2\".encodeUtf8())\n    client.processNextFrame()\n    taskFaker.runTasks()\n    server.webSocket!!.pong(\"pong 3\".encodeUtf8())\n    client.processNextFrame()\n\n    // After 500ms the client automatically pings and the server pongs back.\n    taskFaker.advanceUntil(ns(500L))\n    server.processNextFrame() // Ping.\n    client.processNextFrame() // Pong.\n\n    // After 1000ms the client will attempt a ping 2, but we don't process it. That'll cause the\n    // client to fail at 1500ms when it's time to send ping 3 because pong 2 hasn't been received.\n    taskFaker.advanceUntil(ns(1000L))\n    taskFaker.advanceUntil(ns(1500L))\n    client.listener.assertFailure(\n      SocketTimeoutException::class.java,\n      \"sent ping but didn't receive pong within 500ms (after 1 successful ping/pongs)\",\n    )\n  }\n\n  @Test\n  fun messagesNotCompressedWhenNotConfigured() {\n    val message = repeat('a', RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE.toInt())\n    server.webSocket!!.send(message)\n    taskFaker.runTasks()\n    assertThat(client.clientSourceBufferSize())\n      .isGreaterThan(message.length.toLong()) // Not compressed.\n    assertThat(client.processNextFrame()).isTrue()\n    client.listener.assertTextMessage(message)\n  }\n\n  @Test\n  fun messagesCompressedWhenConfigured() {\n    val headers = headersOf(\"Sec-WebSocket-Extensions\", \"permessage-deflate\")\n    client.initWebSocket(random, responseHeaders = headers)\n    server.initWebSocket(random, responseHeaders = headers)\n    val message = repeat('a', RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE.toInt())\n    server.webSocket!!.send(message)\n    taskFaker.runTasks()\n    assertThat(client.clientSourceBufferSize())\n      .isLessThan(message.length.toLong()) // Compressed!\n    assertThat(client.processNextFrame()).isTrue()\n    client.listener.assertTextMessage(message)\n  }\n\n  @Test\n  fun smallMessagesNotCompressed() {\n    val headers = headersOf(\"Sec-WebSocket-Extensions\", \"permessage-deflate\")\n    client.initWebSocket(random, responseHeaders = headers)\n    server.initWebSocket(random, responseHeaders = headers)\n    val message = repeat('a', RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE.toInt() - 1)\n    server.webSocket!!.send(message)\n    taskFaker.runTasks()\n    assertThat(client.clientSourceBufferSize())\n      .isGreaterThan(message.length.toLong()) // Not compressed.\n    assertThat(client.processNextFrame()).isTrue()\n    client.listener.assertTextMessage(message)\n  }\n\n  /** One peer's streams, listener, and web socket in the test.  */\n  private class TestStreams(\n    private val taskFaker: TaskFaker,\n    private val delegate: Socket,\n    private val client: Boolean,\n  ) : BufferedSocket {\n    private val name = if (client) \"client\" else \"server\"\n    val listener = WebSocketRecorder(name)\n    var webSocket: RealWebSocket? = null\n    var sourceClosed = false\n    var sinkClosed = false\n    val closed: Boolean\n      get() = sourceClosed && sinkClosed\n    var canceled = false\n\n    override val source: BufferedSource =\n      object : ForwardingSource(delegate.source) {\n        override fun close() {\n          sourceClosed = true\n          super.close()\n        }\n      }.buffer()\n\n    override val sink: BufferedSink =\n      object : ForwardingSink(delegate.sink) {\n        override fun close() {\n          sinkClosed = true\n          super.close()\n        }\n      }.buffer()\n\n    fun initWebSocket(\n      random: Random?,\n      pingIntervalMillis: Int = 0,\n      responseHeaders: Headers? = headersOf(),\n      webSocketCloseTimeout: Long = RealWebSocket.CANCEL_AFTER_CLOSE_MILLIS,\n    ) {\n      val url = \"http://example.com/websocket\"\n      val response =\n        Response\n          .Builder()\n          .code(101)\n          .message(\"OK\")\n          .request(Request.Builder().url(url).build())\n          .headers(responseHeaders!!)\n          .protocol(Protocol.HTTP_1_1)\n          .build()\n      webSocket =\n        RealWebSocket(\n          taskFaker.taskRunner,\n          response.request,\n          listener,\n          random!!,\n          pingIntervalMillis.toLong(),\n          parse(\n            responseHeaders,\n          ),\n          RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE,\n          webSocketCloseTimeout,\n        ).apply {\n          if (client) {\n            call =\n              object : FailingCall() {\n                override fun cancel() {\n                  this@TestStreams.cancel()\n                }\n              }\n          }\n        }\n      webSocket!!.initReaderAndWriter(name, this, client)\n    }\n\n    /**\n     * Peeks the number of bytes available for the client to read immediately. This doesn't block so\n     * it requires that bytes have already been flushed by the server.\n     */\n    fun clientSourceBufferSize(): Long {\n      source.request(1L)\n      return source.buffer.size\n    }\n\n    fun processNextFrame(): Boolean {\n      taskFaker.runTasks()\n      return webSocket!!.processNextFrame()\n    }\n\n    override fun cancel() {\n      canceled = true\n      delegate.cancel()\n    }\n  }\n\n  companion object {\n    private fun ns(millis: Long): Long = millis * 1000000L\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/ws/WebSocketExtensionsTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport okhttp3.Headers.Companion.headersOf\nimport org.junit.jupiter.api.Test\n\nclass WebSocketExtensionsTest {\n  @Test\n  fun emptyHeader() {\n    assertThat(parse(\"\")).isEqualTo(WebSocketExtensions())\n  }\n\n  @Test\n  fun noExtensionHeader() {\n    assertThat(WebSocketExtensions.parse(headersOf()))\n      .isEqualTo(WebSocketExtensions())\n  }\n\n  @Test\n  fun emptyExtension() {\n    assertThat(parse(\", permessage-deflate\"))\n      .isEqualTo(WebSocketExtensions(perMessageDeflate = true, unknownValues = true))\n  }\n\n  @Test\n  fun unknownExtension() {\n    assertThat(parse(\"unknown-ext\"))\n      .isEqualTo(WebSocketExtensions(unknownValues = true))\n  }\n\n  @Test\n  fun perMessageDeflate() {\n    assertThat(parse(\"permessage-deflate\"))\n      .isEqualTo(WebSocketExtensions(perMessageDeflate = true))\n  }\n\n  @Test\n  fun emptyParameters() {\n    assertThat(parse(\"permessage-deflate;\"))\n      .isEqualTo(WebSocketExtensions(perMessageDeflate = true))\n  }\n\n  @Test\n  fun repeatedPerMessageDeflate() {\n    assertThat(parse(\"permessage-deflate, permessage-deflate; server_no_context_takeover\"))\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n          serverNoContextTakeover = true,\n          unknownValues = true,\n        ),\n      )\n  }\n\n  @Test\n  fun multiplePerMessageDeflateHeaders() {\n    val extensions =\n      WebSocketExtensions.parse(\n        headersOf(\n          \"Sec-WebSocket-Extensions\",\n          \"\",\n          \"Sec-WebSocket-Extensions\",\n          \"permessage-deflate\",\n        ),\n      )\n    assertThat(extensions)\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n        ),\n      )\n  }\n\n  @Test\n  fun noContextTakeoverServerAndClient() {\n    assertThat(parse(\"permessage-deflate; server_no_context_takeover; client_no_context_takeover\"))\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n          clientNoContextTakeover = true,\n          serverNoContextTakeover = true,\n        ),\n      )\n  }\n\n  @Test\n  fun everything() {\n    assertThat(\n      parse(\n        \"permessage-deflate; client_max_window_bits=15; client_no_context_takeover; \" +\n          \"server_max_window_bits=8; server_no_context_takeover\",\n      ),\n    ).isEqualTo(\n      WebSocketExtensions(\n        perMessageDeflate = true,\n        clientMaxWindowBits = 15,\n        clientNoContextTakeover = true,\n        serverMaxWindowBits = 8,\n        serverNoContextTakeover = true,\n      ),\n    )\n  }\n\n  @Test\n  fun noWhitespace() {\n    assertThat(parse(\"permessage-deflate;server_no_context_takeover;client_no_context_takeover\"))\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n          clientNoContextTakeover = true,\n          serverNoContextTakeover = true,\n        ),\n      )\n  }\n\n  @Test\n  fun excessWhitespace() {\n    assertThat(\n      parse(\n        \"  permessage-deflate\\t ; \\tserver_no_context_takeover\\t ;  client_no_context_takeover  \",\n      ),\n    ).isEqualTo(\n      WebSocketExtensions(\n        perMessageDeflate = true,\n        clientNoContextTakeover = true,\n        serverNoContextTakeover = true,\n      ),\n    )\n  }\n\n  @Test\n  fun noContextTakeoverClientAndServer() {\n    assertThat(parse(\"permessage-deflate; client_no_context_takeover; server_no_context_takeover\"))\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n          clientNoContextTakeover = true,\n          serverNoContextTakeover = true,\n        ),\n      )\n  }\n\n  @Test\n  fun noContextTakeoverClient() {\n    assertThat(parse(\"permessage-deflate; client_no_context_takeover\"))\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n          clientNoContextTakeover = true,\n        ),\n      )\n  }\n\n  @Test\n  fun noContextTakeoverServer() {\n    assertThat(parse(\"permessage-deflate; server_no_context_takeover\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, serverNoContextTakeover = true),\n    )\n  }\n\n  @Test\n  fun clientMaxWindowBits() {\n    assertThat(parse(\"permessage-deflate; client_max_window_bits=8\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, clientMaxWindowBits = 8),\n    )\n    assertThat(parse(\"permessage-deflate; client_max_window_bits=\\\"8\\\"\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, clientMaxWindowBits = 8),\n    )\n    assertThat(parse(\"permessage-deflate; client_max_window_bits=15\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, clientMaxWindowBits = 15),\n    )\n    assertThat(parse(\"permessage-deflate; client_max_window_bits=\\\"15\\\"\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, clientMaxWindowBits = 15),\n    )\n    assertThat(parse(\"permessage-deflate; client_max_window_bits\\t =\\t 8\\t \")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, clientMaxWindowBits = 8),\n    )\n    assertThat(parse(\"permessage-deflate; client_max_window_bits\\t =\\t \\\"8\\\"\\t \")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, clientMaxWindowBits = 8),\n    )\n  }\n\n  @Test\n  fun serverMaxWindowBits() {\n    assertThat(parse(\"permessage-deflate; server_max_window_bits=8\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, serverMaxWindowBits = 8),\n    )\n    assertThat(parse(\"permessage-deflate; server_max_window_bits=\\\"8\\\"\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, serverMaxWindowBits = 8),\n    )\n    assertThat(parse(\"permessage-deflate; server_max_window_bits=15\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, serverMaxWindowBits = 15),\n    )\n    assertThat(parse(\"permessage-deflate; server_max_window_bits=\\\"15\\\"\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, serverMaxWindowBits = 15),\n    )\n    assertThat(parse(\"permessage-deflate; server_max_window_bits\\t =\\t 8\\t \")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, serverMaxWindowBits = 8),\n    )\n    assertThat(parse(\"permessage-deflate; server_max_window_bits\\t =\\t \\\"8\\\"\\t \")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, serverMaxWindowBits = 8),\n    )\n  }\n\n  @Test\n  fun unknownParameters() {\n    assertThat(parse(\"permessage-deflate; unknown\"))\n      .isEqualTo(WebSocketExtensions(perMessageDeflate = true, unknownValues = true))\n    assertThat(parse(\"permessage-deflate; unknown_parameter=15\"))\n      .isEqualTo(WebSocketExtensions(perMessageDeflate = true, unknownValues = true))\n    assertThat(parse(\"permessage-deflate; unknown_parameter=15; unknown_parameter=15\"))\n      .isEqualTo(WebSocketExtensions(perMessageDeflate = true, unknownValues = true))\n  }\n\n  @Test\n  fun unexpectedValue() {\n    assertThat(parse(\"permessage-deflate; client_no_context_takeover=true\"))\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n          clientNoContextTakeover = true,\n          unknownValues = true,\n        ),\n      )\n    assertThat(parse(\"permessage-deflate; server_max_window_bits=true\"))\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n          unknownValues = true,\n        ),\n      )\n  }\n\n  @Test\n  fun absentValue() {\n    assertThat(parse(\"permessage-deflate; server_max_window_bits\")).isEqualTo(\n      WebSocketExtensions(perMessageDeflate = true, unknownValues = true),\n    )\n  }\n\n  @Test\n  fun uppercase() {\n    assertThat(parse(\"PERMESSAGE-DEFLATE; SERVER_NO_CONTEXT_TAKEOVER; CLIENT_NO_CONTEXT_TAKEOVER\"))\n      .isEqualTo(\n        WebSocketExtensions(\n          perMessageDeflate = true,\n          clientNoContextTakeover = true,\n          serverNoContextTakeover = true,\n        ),\n      )\n  }\n\n  private fun parse(extension: String) = WebSocketExtensions.parse(headersOf(\"Sec-WebSocket-Extensions\", extension))\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/ws/WebSocketHttpTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport assertk.assertThat\nimport assertk.assertions.isBetween\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isLessThan\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport java.io.EOFException\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.HttpURLConnection\nimport java.net.ProtocolException\nimport java.net.SocketTimeoutException\nimport java.time.Duration\nimport java.util.Arrays\nimport java.util.Collections\nimport java.util.Random\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicInteger\nimport kotlin.test.assertFailsWith\nimport mockwebserver3.Dispatcher\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.RecordedRequest\nimport mockwebserver3.SocketEffect.CloseSocket\nimport mockwebserver3.SocketEffect.Stall\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.EventRecorder\nimport okhttp3.Interceptor\nimport okhttp3.OkHttpClient\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Protocol\nimport okhttp3.RecordingHostnameVerifier\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.TestLogHandler\nimport okhttp3.TestUtil.assumeNotWindows\nimport okhttp3.TestUtil.repeat\nimport okhttp3.WebSocket\nimport okhttp3.WebSocketListener\nimport okhttp3.internal.UnreadableResponseBody\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.ws.WebSocketProtocol.acceptHeader\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Flaky\n@Tag(\"Slow\")\nclass WebSocketHttpTest {\n  // Flaky https://github.com/square/okhttp/issues/4515\n  // Flaky https://github.com/square/okhttp/issues/4953\n  @RegisterExtension\n  var clientTestRule = configureClientTestRule()\n\n  @RegisterExtension\n  var platform = PlatformRule()\n\n  @RegisterExtension\n  var testLogHandler = TestLogHandler(OkHttpClient::class.java)\n\n  @StartStop\n  private val webServer = MockWebServer()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private val clientListener = WebSocketRecorder(\"client\")\n  private val serverListener = WebSocketRecorder(\"server\")\n  private val random = Random(0)\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .writeTimeout(Duration.ofMillis(500))\n      .readTimeout(Duration.ofMillis(500))\n      .addInterceptor(\n        Interceptor { chain: Interceptor.Chain ->\n          val response = chain.proceed(chain.request())\n          // Ensure application interceptors never see a null body.\n          assertThat(response.body).isNotNull()\n          response\n        },\n      ).build()\n\n  private fun configureClientTestRule(): OkHttpClientTestRule {\n    val clientTestRule = OkHttpClientTestRule()\n    clientTestRule.recordTaskRunner = true\n    return clientTestRule\n  }\n\n  @BeforeEach\n  fun setUp() {\n    platform.assumeNotOpenJSSE()\n  }\n\n  @AfterEach\n  @Throws(InterruptedException::class)\n  fun tearDown() {\n    clientListener.assertExhausted()\n  }\n\n  @Test\n  fun textMessage() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    webSocket.send(\"Hello, WebSockets!\")\n    serverListener.assertTextMessage(\"Hello, WebSockets!\")\n    closeWebSockets(webSocket, server)\n  }\n\n  @Test\n  fun binaryMessage() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    webSocket.send(\"Hello!\".encodeUtf8())\n    serverListener.assertBinaryMessage(\"Hello!\".encodeUtf8())\n    closeWebSockets(webSocket, server)\n  }\n\n  @Test\n  fun serverMessage() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    server.send(\"Hello, WebSockets!\")\n    clientListener.assertTextMessage(\"Hello, WebSockets!\")\n    closeWebSockets(webSocket, server)\n  }\n\n  @Test\n  fun throwingOnOpenFailsImmediately() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val e = RuntimeException()\n    clientListener.setNextEventDelegate(\n      object : WebSocketListener() {\n        override fun onOpen(\n          webSocket: WebSocket,\n          response: Response,\n        ): Unit = throw e\n      },\n    )\n    newWebSocket()\n    serverListener.assertOpen()\n    serverListener.assertFailure(EOFException::class.java)\n    serverListener.assertExhausted()\n    clientListener.assertFailure(e)\n  }\n\n  @Disabled(\"AsyncCall currently lets runtime exceptions propagate.\")\n  @Test\n  @Throws(\n    Exception::class,\n  )\n  fun throwingOnFailLogs() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(200)\n        .body(\"Body\")\n        .build(),\n    )\n    val e = RuntimeException(\"boom\")\n    clientListener.setNextEventDelegate(\n      object : WebSocketListener() {\n        override fun onFailure(\n          webSocket: WebSocket,\n          t: Throwable,\n          response: Response?,\n        ): Unit = throw e\n      },\n    )\n    newWebSocket()\n    assertThat(testLogHandler.take()).isEqualTo(\"INFO: [WS client] onFailure\")\n  }\n\n  @Test\n  fun throwingOnMessageClosesImmediatelyAndFails() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    val e = RuntimeException()\n    clientListener.setNextEventDelegate(\n      object : WebSocketListener() {\n        override fun onMessage(\n          webSocket: WebSocket,\n          text: String,\n        ): Unit = throw e\n      },\n    )\n    server.send(\"Hello, WebSockets!\")\n    clientListener.assertFailure(e)\n    serverListener.assertFailure(EOFException::class.java)\n    serverListener.assertExhausted()\n  }\n\n  @Test\n  fun throwingOnClosingClosesImmediatelyAndFails() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    val e = RuntimeException()\n    clientListener.setNextEventDelegate(\n      object : WebSocketListener() {\n        override fun onClosing(\n          webSocket: WebSocket,\n          code: Int,\n          reason: String,\n        ): Unit = throw e\n      },\n    )\n    server.close(1000, \"bye\")\n    clientListener.assertFailure(e)\n    serverListener.assertFailure()\n    serverListener.assertExhausted()\n  }\n\n  @Test\n  fun unplannedCloseHandledByCloseWithoutFailure() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    clientListener.setNextEventDelegate(\n      object : WebSocketListener() {\n        override fun onClosing(\n          webSocket: WebSocket,\n          code: Int,\n          reason: String,\n        ) {\n          webSocket.close(1000, null)\n        }\n      },\n    )\n    server.close(1001, \"bye\")\n    clientListener.assertClosed(1001, \"bye\")\n    clientListener.assertExhausted()\n    serverListener.assertClosing(1000, \"\")\n    serverListener.assertClosed(1000, \"\")\n    serverListener.assertExhausted()\n  }\n\n  @Test\n  fun unplannedCloseHandledWithoutFailure() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    newWebSocket()\n    val webSocket = clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    closeWebSockets(webSocket, server)\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun non101RetainsBody() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(200)\n        .body(\"Body\")\n        .build(),\n    )\n    newWebSocket()\n    clientListener.assertFailure(\n      200,\n      \"Body\",\n      ProtocolException::class.java,\n      \"Expected HTTP 101 response but was '200 OK'\",\n    )\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun notFound() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 404 Not Found\")\n        .build(),\n    )\n    newWebSocket()\n    clientListener.assertFailure(\n      404,\n      null,\n      ProtocolException::class.java,\n      \"Expected HTTP 101 response but was '404 Not Found'\",\n    )\n  }\n\n  @Test\n  fun clientTimeoutClosesBody() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(408)\n        .build(),\n    )\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    webSocket.send(\"abc\")\n    serverListener.assertTextMessage(\"abc\")\n    server.send(\"def\")\n    clientListener.assertTextMessage(\"def\")\n    closeWebSockets(webSocket, server)\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun missingConnectionHeader() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(101)\n        .setHeader(\"Upgrade\", \"websocket\")\n        .setHeader(\"Sec-WebSocket-Accept\", \"ujmZX4KXZqjwy6vi1aQFH5p4Ygk=\")\n        .build(),\n    )\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseSocket())\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertFailure(\n      101,\n      null,\n      ProtocolException::class.java,\n      \"Expected 'Connection' header value 'Upgrade' but was 'null'\",\n    )\n    webSocket.cancel()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun wrongConnectionHeader() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(101)\n        .setHeader(\"Upgrade\", \"websocket\")\n        .setHeader(\"Connection\", \"Downgrade\")\n        .setHeader(\"Sec-WebSocket-Accept\", \"ujmZX4KXZqjwy6vi1aQFH5p4Ygk=\")\n        .build(),\n    )\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseSocket())\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertFailure(\n      101,\n      null,\n      ProtocolException::class.java,\n      \"Expected 'Connection' header value 'Upgrade' but was 'Downgrade'\",\n    )\n    webSocket.cancel()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun missingUpgradeHeader() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(101)\n        .setHeader(\"Connection\", \"Upgrade\")\n        .setHeader(\"Sec-WebSocket-Accept\", \"ujmZX4KXZqjwy6vi1aQFH5p4Ygk=\")\n        .build(),\n    )\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseSocket())\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertFailure(\n      101,\n      null,\n      ProtocolException::class.java,\n      \"Expected 'Upgrade' header value 'websocket' but was 'null'\",\n    )\n    webSocket.cancel()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun wrongUpgradeHeader() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(101)\n        .setHeader(\"Connection\", \"Upgrade\")\n        .setHeader(\"Upgrade\", \"Pepsi\")\n        .setHeader(\"Sec-WebSocket-Accept\", \"ujmZX4KXZqjwy6vi1aQFH5p4Ygk=\")\n        .build(),\n    )\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseSocket())\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertFailure(\n      101,\n      null,\n      ProtocolException::class.java,\n      \"Expected 'Upgrade' header value 'websocket' but was 'Pepsi'\",\n    )\n    webSocket.cancel()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun missingMagicHeader() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(101)\n        .setHeader(\"Connection\", \"Upgrade\")\n        .setHeader(\"Upgrade\", \"websocket\")\n        .build(),\n    )\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseSocket())\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertFailure(\n      101,\n      null,\n      ProtocolException::class.java,\n      \"Expected 'Sec-WebSocket-Accept' header value 'ujmZX4KXZqjwy6vi1aQFH5p4Ygk=' but was 'null'\",\n    )\n    webSocket.cancel()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun wrongMagicHeader() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(101)\n        .setHeader(\"Connection\", \"Upgrade\")\n        .setHeader(\"Upgrade\", \"websocket\")\n        .setHeader(\"Sec-WebSocket-Accept\", \"magic\")\n        .build(),\n    )\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .onRequestStart(CloseSocket())\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertFailure(\n      101,\n      null,\n      ProtocolException::class.java,\n      \"Expected 'Sec-WebSocket-Accept' header value 'ujmZX4KXZqjwy6vi1aQFH5p4Ygk=' but was 'magic'\",\n    )\n    webSocket.cancel()\n  }\n\n  @Test\n  @Throws(IOException::class)\n  fun clientIncludesForbiddenHeader() {\n    newWebSocket(\n      Request\n        .Builder()\n        .url(webServer.url(\"/\"))\n        .header(\"Sec-WebSocket-Extensions\", \"permessage-deflate\")\n        .build(),\n    )\n    clientListener.assertFailure(\n      ProtocolException::class.java,\n      \"Request header not permitted: 'Sec-WebSocket-Extensions'\",\n    )\n  }\n\n  @Test\n  fun webSocketAndApplicationInterceptors() {\n    val interceptedCount = AtomicInteger()\n    client =\n      client\n        .newBuilder()\n        .addInterceptor(\n          Interceptor { chain: Interceptor.Chain ->\n            assertThat(chain.request().body).isNull()\n            val response = chain.proceed(chain.request())\n            assertThat(response.header(\"Connection\")).isEqualTo(\"Upgrade\")\n            assertThat(response.body).isInstanceOf<UnreadableResponseBody>()\n            interceptedCount.incrementAndGet()\n            response\n          },\n        ).build()\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    assertThat(interceptedCount.get()).isEqualTo(1)\n    closeWebSockets(webSocket, serverListener.assertOpen())\n  }\n\n  @Test\n  fun webSocketAndNetworkInterceptors() {\n    client =\n      client\n        .newBuilder()\n        .addNetworkInterceptor(\n          Interceptor { chain: Interceptor.Chain? ->\n            throw AssertionError() // Network interceptors don't execute.\n          },\n        ).build()\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    closeWebSockets(webSocket, server)\n  }\n\n  @Test\n  fun overflowOutgoingQueue() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n\n    // Send messages until the client's outgoing buffer overflows!\n    val message: ByteString = ByteString.of(*ByteArray(1024 * 1024))\n    var messageCount: Long = 0\n    while (true) {\n      val success = webSocket.send(message)\n      if (!success) break\n      messageCount++\n      val queueSize = webSocket.queueSize()\n      assertThat(queueSize).isBetween(0L, messageCount * message.size)\n      // Expect to fail before enqueueing 32 MiB.\n      assertThat(messageCount).isLessThan(32L)\n    }\n\n    // Confirm all sent messages were received, followed by a client-initiated close.\n    val server = serverListener.assertOpen()\n    for (i in 0 until messageCount) {\n      serverListener.assertBinaryMessage(message)\n    }\n    serverListener.assertClosing(1001, \"\")\n\n    // When the server acknowledges the close the connection shuts down gracefully.\n    server.close(1000, null)\n    clientListener.assertClosing(1000, \"\")\n    clientListener.assertClosed(1000, \"\")\n    serverListener.assertClosed(1001, \"\")\n  }\n\n  @Test\n  fun closeReasonMaximumLength() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val clientReason = repeat('C', 123)\n    val serverReason = repeat('S', 123)\n    val webSocket: WebSocket = newWebSocket()\n    val server = serverListener.assertOpen()\n    clientListener.assertOpen()\n    webSocket.close(1000, clientReason)\n    serverListener.assertClosing(1000, clientReason)\n    server.close(1000, serverReason)\n    clientListener.assertClosing(1000, serverReason)\n    clientListener.assertClosed(1000, serverReason)\n    serverListener.assertClosed(1000, clientReason)\n  }\n\n  @Test\n  fun closeReasonTooLong() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    val server = serverListener.assertOpen()\n    clientListener.assertOpen()\n    val reason = repeat('X', 124)\n    assertFailsWith<IllegalArgumentException> {\n      webSocket.close(1000, reason)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"reason.size() > 123: $reason\")\n    }\n    webSocket.close(1000, null)\n    serverListener.assertClosing(1000, \"\")\n    server.close(1000, null)\n    clientListener.assertClosing(1000, \"\")\n    clientListener.assertClosed(1000, \"\")\n    serverListener.assertClosed(1000, \"\")\n  }\n\n  @Test\n  fun wsScheme() {\n    assumeNotWindows()\n    websocketScheme(\"ws\")\n  }\n\n  @Test\n  fun wsUppercaseScheme() {\n    websocketScheme(\"WS\")\n  }\n\n  @Test\n  fun wssScheme() {\n    webServer.useHttps(handshakeCertificates.sslSocketFactory())\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    websocketScheme(\"wss\")\n  }\n\n  @Test\n  fun httpsScheme() {\n    webServer.useHttps(handshakeCertificates.sslSocketFactory())\n    client =\n      client\n        .newBuilder()\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(RecordingHostnameVerifier())\n        .build()\n    websocketScheme(\"https\")\n  }\n\n  @Test\n  fun readTimeoutAppliesToHttpRequest() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .onResponseStart(Stall)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertFailure(\n      SocketTimeoutException::class.java,\n      \"timeout\",\n      \"Read timed out\",\n    )\n    assertThat(webSocket.close(1000, null)).isFalse()\n  }\n\n  /**\n   * There's no read timeout when reading the first byte of a new frame. But as soon as we start\n   * reading a frame we enable the read timeout. In this test we have the server returning the first\n   * byte of a frame but no more frames.\n   */\n  @Test\n  fun readTimeoutAppliesWithinFrames() {\n    webServer.dispatcher =\n      object : Dispatcher() {\n        override fun dispatch(request: RecordedRequest): MockResponse =\n          upgradeResponse(request)\n            .body(Buffer().write(\"81\".decodeHex())) // Truncated frame.\n            .removeHeader(\"Content-Length\")\n            .build()\n      }\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    clientListener.assertFailure(\n      SocketTimeoutException::class.java,\n      \"timeout\",\n      \"Read timed out\",\n    )\n    assertThat(webSocket.close(1000, null)).isFalse()\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun readTimeoutDoesNotApplyAcrossFrames() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n\n    // Sleep longer than the HTTP client's read timeout.\n    Thread.sleep((client.readTimeoutMillis + 500).toLong())\n    server.send(\"abc\")\n    clientListener.assertTextMessage(\"abc\")\n    closeWebSockets(webSocket, server)\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun clientPingsServerOnInterval() {\n    client =\n      client\n        .newBuilder()\n        .pingInterval(Duration.ofMillis(500))\n        .build()\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen() as RealWebSocket\n    val startNanos = System.nanoTime()\n    while (webSocket.receivedPongCount() < 3) {\n      Thread.sleep(50)\n    }\n    val elapsedUntilPong3 = System.nanoTime() - startNanos\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedUntilPong3).toDouble())\n      .isCloseTo(1500.0, 250.0)\n\n    // The client pinged the server 3 times, and it has ponged back 3 times.\n    assertThat(webSocket.sentPingCount()).isEqualTo(3)\n    assertThat(server.receivedPingCount()).isEqualTo(3)\n    assertThat(webSocket.receivedPongCount()).isEqualTo(3)\n\n    // The server has never pinged the client.\n    assertThat(server.receivedPongCount()).isEqualTo(0)\n    assertThat(webSocket.receivedPingCount()).isEqualTo(0)\n    closeWebSockets(webSocket, server)\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun clientDoesNotPingServerByDefault() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen() as RealWebSocket\n    Thread.sleep(1000)\n\n    // No pings and no pongs.\n    assertThat(webSocket.sentPingCount()).isEqualTo(0)\n    assertThat(webSocket.receivedPingCount()).isEqualTo(0)\n    assertThat(webSocket.receivedPongCount()).isEqualTo(0)\n    assertThat(server.sentPingCount()).isEqualTo(0)\n    assertThat(server.receivedPingCount()).isEqualTo(0)\n    assertThat(server.receivedPongCount()).isEqualTo(0)\n    closeWebSockets(webSocket, server)\n  }\n\n  /**\n   * Configure the websocket to send pings every 500 ms. Artificially prevent the server from\n   * responding to pings. The client should give up when attempting to send its 2nd ping, at about\n   * 1000 ms.\n   */\n  @Test\n  fun unacknowledgedPingFailsConnection() {\n    assumeNotWindows()\n    client =\n      client\n        .newBuilder()\n        .pingInterval(Duration.ofMillis(500))\n        .build()\n\n    // Stall in onOpen to prevent pongs from being sent.\n    val latch = CountDownLatch(1)\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(\n          object : WebSocketListener() {\n            override fun onOpen(\n              webSocket: WebSocket,\n              response: Response,\n            ) {\n              try {\n                latch.await() // The server can't respond to pings!\n              } catch (e: InterruptedException) {\n                throw AssertionError(e)\n              }\n            }\n          },\n        ).build(),\n    )\n    val openAtNanos = System.nanoTime()\n    newWebSocket()\n    clientListener.assertOpen()\n    clientListener.assertFailure(\n      SocketTimeoutException::class.java,\n      \"sent ping but didn't receive pong within 500ms (after 0 successful ping/pongs)\",\n    )\n    latch.countDown()\n    val elapsedUntilFailure = System.nanoTime() - openAtNanos\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedUntilFailure).toDouble())\n      .isCloseTo(1000.0, 250.0)\n  }\n\n  /** https://github.com/square/okhttp/issues/2788  */\n  @Test\n  fun clientCancelsIfCloseIsNotAcknowledged() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n\n    // Initiate a close on the client, which will schedule a hard cancel in 500 ms.\n    val closeAtNanos = System.nanoTime()\n    webSocket.close(1000, \"goodbye\", 500L)\n    serverListener.assertClosing(1000, \"goodbye\")\n\n    // Confirm that the hard cancel occurred after 500 ms.\n    clientListener.assertFailure()\n    val elapsedUntilFailure = System.nanoTime() - closeAtNanos\n    assertThat(TimeUnit.NANOSECONDS.toMillis(elapsedUntilFailure).toDouble())\n      .isCloseTo(500.0, 250.0)\n\n    // Close the server and confirm it saw what we expected.\n    server.close(1000, null)\n    serverListener.assertClosed(1000, \"goodbye\")\n  }\n\n  @Test\n  fun webSocketsDontTriggerEventListener() {\n    val eventRecorder = EventRecorder()\n    client =\n      client\n        .newBuilder()\n        .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n        .build()\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    webSocket.send(\"Web Sockets and Events?!\")\n    serverListener.assertTextMessage(\"Web Sockets and Events?!\")\n    webSocket.close(1000, \"\")\n    serverListener.assertClosing(1000, \"\")\n    server.close(1000, \"\")\n    clientListener.assertClosing(1000, \"\")\n    clientListener.assertClosed(1000, \"\")\n    serverListener.assertClosed(1000, \"\")\n    assertThat(eventRecorder.recordedEventTypes()).isEmpty()\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun callTimeoutAppliesToSetup() {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(500, TimeUnit.MILLISECONDS)\n        .build(),\n    )\n    client =\n      client\n        .newBuilder()\n        .readTimeout(Duration.ZERO)\n        .writeTimeout(Duration.ZERO)\n        .callTimeout(Duration.ofMillis(100))\n        .build()\n    newWebSocket()\n    clientListener.assertFailure(InterruptedIOException::class.java, \"timeout\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun callTimeoutDoesNotApplyOnceConnected() {\n    client =\n      client\n        .newBuilder()\n        .callTimeout(Duration.ofMillis(100))\n        .build()\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val webSocket: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    Thread.sleep(500)\n    server.send(\"Hello, WebSockets!\")\n    clientListener.assertTextMessage(\"Hello, WebSockets!\")\n    closeWebSockets(webSocket, server)\n  }\n\n  /**\n   * We had a bug where web socket connections were leaked if the HTTP connection upgrade was not\n   * successful. This test confirms that connections are released back to the connection pool!\n   * https://github.com/square/okhttp/issues/4258\n   */\n  @Test\n  @Throws(Exception::class)\n  fun webSocketConnectionIsReleased() {\n    // This test assumes HTTP/1.1 pooling semantics.\n    client =\n      client\n        .newBuilder()\n        .protocols(Arrays.asList(Protocol.HTTP_1_1))\n        .build()\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .code(HttpURLConnection.HTTP_NOT_FOUND)\n        .body(\"not found!\")\n        .build(),\n    )\n    webServer.enqueue(MockResponse())\n    newWebSocket()\n    clientListener.assertFailure()\n    val regularRequest =\n      Request\n        .Builder()\n        .url(webServer.url(\"/\"))\n        .build()\n    val response = client.newCall(regularRequest).execute()\n    response.close()\n    assertThat(webServer.takeRequest().exchangeIndex).isEqualTo(0)\n    assertThat(webServer.takeRequest().exchangeIndex).isEqualTo(1)\n  }\n\n  /** https://github.com/square/okhttp/issues/5705  */\n  @Test\n  fun closeWithoutSuccessfulConnect() {\n    val request =\n      Request\n        .Builder()\n        .url(webServer.url(\"/\"))\n        .build()\n    val webSocket = client.newWebSocket(request, clientListener)\n    webSocket.send(\"hello\")\n    webSocket.close(1000, null)\n  }\n\n  /** https://github.com/square/okhttp/issues/7768  */\n  @Test\n  @Throws(InterruptedException::class)\n  fun reconnectingToNonWebSocket() {\n    for (i in 0..29) {\n      webServer.enqueue(\n        MockResponse\n          .Builder()\n          .bodyDelay(100, TimeUnit.MILLISECONDS)\n          .body(\"Wrong endpoint\")\n          .code(401)\n          .build(),\n      )\n    }\n    val request =\n      Request\n        .Builder()\n        .url(webServer.url(\"/\"))\n        .build()\n    val attempts = CountDownLatch(20)\n    val webSockets = Collections.synchronizedList(ArrayList<WebSocket>())\n    val reconnectOnFailure: WebSocketListener =\n      object : WebSocketListener() {\n        override fun onFailure(\n          webSocket: WebSocket,\n          t: Throwable,\n          response: Response?,\n        ) {\n          if (attempts.count > 0) {\n            clientListener.setNextEventDelegate(this)\n            webSockets.add(client.newWebSocket(request, clientListener))\n            attempts.countDown()\n          }\n        }\n      }\n    clientListener.setNextEventDelegate(reconnectOnFailure)\n    webSockets.add(client.newWebSocket(request, clientListener))\n    attempts.await()\n    synchronized(webSockets) {\n      for (webSocket in webSockets) {\n        webSocket.cancel()\n      }\n    }\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun compressedMessages() {\n    successfulExtensions(\"permessage-deflate\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun compressedMessagesNoClientContextTakeover() {\n    successfulExtensions(\"permessage-deflate; client_no_context_takeover\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun compressedMessagesNoServerContextTakeover() {\n    successfulExtensions(\"permessage-deflate; server_no_context_takeover\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun unexpectedExtensionParameter() {\n    extensionNegotiationFailure(\"permessage-deflate; unknown_parameter=15\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun clientMaxWindowBitsIncluded() {\n    extensionNegotiationFailure(\"permessage-deflate; client_max_window_bits=15\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun serverMaxWindowBitsTooLow() {\n    extensionNegotiationFailure(\"permessage-deflate; server_max_window_bits=7\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun serverMaxWindowBitsTooHigh() {\n    extensionNegotiationFailure(\"permessage-deflate; server_max_window_bits=16\")\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun serverMaxWindowBitsJustRight() {\n    successfulExtensions(\"permessage-deflate; server_max_window_bits=15\")\n  }\n\n  @Throws(Exception::class)\n  private fun successfulExtensions(extensionsHeader: String) {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Sec-WebSocket-Extensions\", extensionsHeader)\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val client: WebSocket = newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n\n    // Server to client message big enough to be compressed.\n    val message1 = repeat('a', RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE.toInt())\n    server.send(message1)\n    clientListener.assertTextMessage(message1)\n\n    // Client to server message big enough to be compressed.\n    val message2 = repeat('b', RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE.toInt())\n    client.send(message2)\n    serverListener.assertTextMessage(message2)\n\n    // Empty server to client message.\n    val message3 = \"\"\n    server.send(message3)\n    clientListener.assertTextMessage(message3)\n\n    // Empty client to server message.\n    val message4 = \"\"\n    client.send(message4)\n    serverListener.assertTextMessage(message4)\n\n    // Server to client message that shares context with message1.\n    val message5 = message1 + message1\n    server.send(message5)\n    clientListener.assertTextMessage(message5)\n\n    // Client to server message that shares context with message2.\n    val message6 = message2 + message2\n    client.send(message6)\n    serverListener.assertTextMessage(message6)\n    closeWebSockets(client, server)\n    val upgradeRequest = webServer.takeRequest()\n    assertThat(upgradeRequest.headers[\"Sec-WebSocket-Extensions\"])\n      .isEqualTo(\"permessage-deflate\")\n  }\n\n  @Throws(Exception::class)\n  private fun extensionNegotiationFailure(extensionsHeader: String) {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"Sec-WebSocket-Extensions\", extensionsHeader)\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    newWebSocket()\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    val clientReason = \"unexpected Sec-WebSocket-Extensions in response header\"\n    serverListener.assertClosing(1010, clientReason)\n    server.close(1010, \"\")\n    clientListener.assertClosing(1010, \"\")\n    clientListener.assertClosed(1010, \"\")\n    serverListener.assertClosed(1010, clientReason)\n    clientListener.assertExhausted()\n    serverListener.assertExhausted()\n  }\n\n  private fun upgradeResponse(request: RecordedRequest): MockResponse.Builder {\n    val key = request.headers[\"Sec-WebSocket-Key\"]\n    return MockResponse\n      .Builder()\n      .status(\"HTTP/1.1 101 Switching Protocols\")\n      .setHeader(\"Connection\", \"Upgrade\")\n      .setHeader(\"Upgrade\", \"websocket\")\n      .setHeader(\"Sec-WebSocket-Accept\", acceptHeader(key!!))\n  }\n\n  private fun websocketScheme(scheme: String) {\n    webServer.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val request =\n      Request\n        .Builder()\n        .url(scheme + \"://\" + webServer.hostName + \":\" + webServer.port + \"/\")\n        .build()\n    val webSocket = newWebSocket(request)\n    clientListener.assertOpen()\n    val server = serverListener.assertOpen()\n    webSocket.send(\"abc\")\n    serverListener.assertTextMessage(\"abc\")\n    closeWebSockets(webSocket, server)\n  }\n\n  private fun newWebSocket(\n    request: Request =\n      Request\n        .Builder()\n        .get()\n        .url(\n          webServer.url(\"/\"),\n        ).build(),\n  ): RealWebSocket {\n    val webSocket =\n      RealWebSocket(\n        TaskRunner.INSTANCE,\n        request,\n        clientListener,\n        random,\n        client.pingIntervalMillis.toLong(),\n        null,\n        0L,\n        client.webSocketCloseTimeout.toLong(),\n      )\n    webSocket.connect(client)\n    return webSocket\n  }\n\n  private fun closeWebSockets(\n    client: WebSocket,\n    server: WebSocket,\n  ) {\n    server.close(1001, \"\")\n    clientListener.assertClosing(1001, \"\")\n    client.close(1000, \"\")\n    serverListener.assertClosing(1000, \"\")\n    clientListener.assertClosed(1001, \"\")\n    serverListener.assertClosed(1000, \"\")\n    clientListener.assertExhausted()\n    serverListener.assertExhausted()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/ws/WebSocketReaderTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.matches\nimport java.io.EOFException\nimport java.io.IOException\nimport java.net.ProtocolException\nimport java.util.Random\nimport kotlin.test.assertFailsWith\nimport okhttp3.internal.format\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.EMPTY\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\n\nclass WebSocketReaderTest {\n  private val data = Buffer()\n  private val callback = WebSocketRecorder(\"client\")\n  private val random = Random(0)\n\n  // Mutually exclusive. Use the one corresponding to the peer whose behavior you wish to test.\n  private val serverReader =\n    WebSocketReader(\n      isClient = false,\n      source = data,\n      frameCallback = callback.asFrameCallback(),\n      perMessageDeflate = false,\n      noContextTakeover = false,\n    )\n  private val serverReaderWithCompression =\n    WebSocketReader(\n      isClient = false,\n      source = data,\n      frameCallback = callback.asFrameCallback(),\n      perMessageDeflate = true,\n      noContextTakeover = false,\n    )\n  private val clientReader =\n    WebSocketReader(\n      isClient = true,\n      source = data,\n      frameCallback = callback.asFrameCallback(),\n      perMessageDeflate = false,\n      noContextTakeover = false,\n    )\n  private val clientReaderWithCompression =\n    WebSocketReader(\n      isClient = true,\n      source = data,\n      frameCallback = callback.asFrameCallback(),\n      perMessageDeflate = true,\n      noContextTakeover = false,\n    )\n\n  @AfterEach fun tearDown() {\n    callback.assertExhausted()\n  }\n\n  @Test fun controlFramesMustBeFinal() {\n    data.write(\"0a00\".decodeHex()) // Empty pong.\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Control frames must be final.\")\n    }\n  }\n\n  @Test fun reservedFlag1IsUnsupportedWithNoCompression() {\n    data.write(\"ca00\".decodeHex()) // Empty pong, flag 1 set.\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Unexpected rsv1 flag\")\n    }\n  }\n\n  @Test fun reservedFlag1IsUnsupportedForControlFrames() {\n    data.write(\"ca00\".decodeHex()) // Empty pong, flag 1 set.\n    assertFailsWith<ProtocolException> {\n      clientReaderWithCompression.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Unexpected rsv1 flag\")\n    }\n  }\n\n  @Test fun reservedFlag1IsUnsupportedForContinuationFrames() {\n    data.write(\"c000\".decodeHex()) // Empty continuation, flag 1 set.\n    assertFailsWith<ProtocolException> {\n      clientReaderWithCompression.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Unexpected rsv1 flag\")\n    }\n  }\n\n  @Test fun reservedFlags2and3AreUnsupported() {\n    data.write(\"aa00\".decodeHex()) // Empty pong, flag 2 set.\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Unexpected rsv2 flag\")\n    }\n    data.clear()\n    data.write(\"9a00\".decodeHex()) // Empty pong, flag 3 set.\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Unexpected rsv3 flag\")\n    }\n  }\n\n  @Test fun clientSentFramesMustBeMasked() {\n    data.write(\"8100\".decodeHex())\n    assertFailsWith<ProtocolException> {\n      serverReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Client-sent frames must be masked.\")\n    }\n  }\n\n  @Test fun serverSentFramesMustNotBeMasked() {\n    data.write(\"8180\".decodeHex())\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Server-sent frames must not be masked.\")\n    }\n  }\n\n  @Test fun controlFramePayloadMax() {\n    data.write(\"8a7e007e\".decodeHex())\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Control frame must be less than 125B.\")\n    }\n  }\n\n  @Test fun clientSimpleHello() {\n    data.write(\"810548656c6c6f\".decodeHex()) // Hello\n    clientReader.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientWithCompressionSimpleUncompressedHello() {\n    data.write(\"810548656c6c6f\".decodeHex()) // Hello\n    clientReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientWithCompressionSimpleCompressedHello() {\n    data.write(\"c107f248cdc9c90700\".decodeHex()) // Hello\n    clientReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun serverSimpleHello() {\n    data.write(\"818537fa213d7f9f4d5158\".decodeHex()) // Hello\n    serverReader.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun serverWithCompressionSimpleUncompressedHello() {\n    data.write(\"818537fa213d7f9f4d5158\".decodeHex()) // Hello\n    serverReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun serverWithCompressionSimpleCompressedHello() {\n    data.write(\"c18760b420bb92fced72a9b320\".decodeHex()) // Hello\n    serverReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientFramePayloadShort() {\n    data.write(\"817E000548656c6c6f\".decodeHex()) // Hello\n    clientReader.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientFramePayloadLong() {\n    data.write(\"817f000000000000000548656c6c6f\".decodeHex()) // Hello\n    clientReader.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientFramePayloadTooLongThrows() {\n    data.write(\"817f8000000000000000\".decodeHex())\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Frame length 0x8000000000000000 > 0x7FFFFFFFFFFFFFFF\",\n      )\n    }\n  }\n\n  @Test fun serverHelloTwoChunks() {\n    data.write(\"818537fa213d7f9f4d\".decodeHex()) // Hel\n    data.write(\"5158\".decodeHex()) // lo\n    serverReader.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun serverWithCompressionHelloTwoChunks() {\n    data.write(\"818537fa213d7f9f4d\".decodeHex()) // Hel\n    data.write(\"5158\".decodeHex()) // lo\n    serverReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun serverWithCompressionCompressedHelloTwoChunks() {\n    data.write(\"418460b420bb92fced72\".decodeHex()) // first 4 bytes of compressed 'Hello'\n    data.write(\"80833851d9d4f156d9\".decodeHex()) // last 3 bytes of compressed 'Hello'\n    serverReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientTwoFrameHello() {\n    data.write(\"010348656c\".decodeHex()) // Hel\n    data.write(\"80026c6f\".decodeHex()) // lo\n    clientReader.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientWithCompressionTwoFrameHello() {\n    data.write(\"010348656c\".decodeHex()) // Hel\n    data.write(\"80026c6f\".decodeHex()) // lo\n    clientReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientWithCompressionTwoFrameCompressedHello() {\n    data.write(\"4104f248cdc9\".decodeHex()) // first 4 bytes of compressed 'Hello'\n    data.write(\"8003c90700\".decodeHex()) // last 3 bytes of compressed 'Hello'\n    clientReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientTwoFrameHelloWithPongs() {\n    data.write(\"010348656c\".decodeHex()) // Hel\n    data.write(\"8a00\".decodeHex()) // Pong\n    data.write(\"8a00\".decodeHex()) // Pong\n    data.write(\"8a00\".decodeHex()) // Pong\n    data.write(\"8a00\".decodeHex()) // Pong\n    data.write(\"80026c6f\".decodeHex()) // lo\n    clientReader.processNextFrame()\n    callback.assertPong(EMPTY)\n    callback.assertPong(EMPTY)\n    callback.assertPong(EMPTY)\n    callback.assertPong(EMPTY)\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientTwoFrameCompressedHelloWithPongs() {\n    data.write(\"4104f248cdc9\".decodeHex()) // first 4 bytes of compressed 'Hello'\n    data.write(\"8a00\".decodeHex()) // Pong\n    data.write(\"8a00\".decodeHex()) // Pong\n    data.write(\"8a00\".decodeHex()) // Pong\n    data.write(\"8a00\".decodeHex()) // Pong\n    data.write(\"8003c90700\".decodeHex()) // last 3 bytes of compressed 'Hello'\n    clientReaderWithCompression.processNextFrame()\n    callback.assertPong(EMPTY)\n    callback.assertPong(EMPTY)\n    callback.assertPong(EMPTY)\n    callback.assertPong(EMPTY)\n    callback.assertTextMessage(\"Hello\")\n  }\n\n  @Test fun clientIncompleteMessageBodyThrows() {\n    data.write(\"810548656c\".decodeHex()) // Length = 5, \"Hel\"\n    assertFailsWith<EOFException> {\n      clientReader.processNextFrame()\n    }\n  }\n\n  @Test fun clientUncompressedMessageWithCompressedFlagThrows() {\n    data.write(\"c10548656c6c6f\".decodeHex()) // Uncompressed 'Hello', flag 1 set\n    assertFailsWith<IOException> {\n      clientReaderWithCompression.processNextFrame()\n    }\n  }\n\n  @Test fun clientIncompleteControlFrameBodyThrows() {\n    data.write(\"8a0548656c\".decodeHex()) // Length = 5, \"Hel\"\n    assertFailsWith<EOFException> {\n      clientReader.processNextFrame()\n    }\n  }\n\n  @Test fun serverIncompleteMessageBodyThrows() {\n    data.write(\"818537fa213d7f9f4d\".decodeHex()) // Length = 5, \"Hel\"\n    assertFailsWith<EOFException> {\n      serverReader.processNextFrame()\n    }\n  }\n\n  @Test fun serverIncompleteControlFrameBodyThrows() {\n    data.write(\"8a8537fa213d7f9f4d\".decodeHex()) // Length = 5, \"Hel\"\n    assertFailsWith<EOFException> {\n      serverReader.processNextFrame()\n    }\n  }\n\n  @Test fun clientSimpleBinary() {\n    val bytes = binaryData(256)\n    data.write(\"827E0100\".decodeHex()).write(bytes)\n    clientReader.processNextFrame()\n    callback.assertBinaryMessage(bytes)\n  }\n\n  @Test fun clientTwoFrameBinary() {\n    val bytes = binaryData(200)\n    data.write(\"0264\".decodeHex()).write(bytes, 0, 100)\n    data.write(\"8064\".decodeHex()).write(bytes, 100, 100)\n    clientReader.processNextFrame()\n    callback.assertBinaryMessage(bytes)\n  }\n\n  @Test fun twoFrameNotContinuation() {\n    val bytes = binaryData(200)\n    data.write(\"0264\".decodeHex()).write(bytes, 0, 100)\n    data.write(\"8264\".decodeHex()).write(bytes, 100, 100)\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Expected continuation opcode. Got: 2\")\n    }\n  }\n\n  @Test fun emptyPingCallsCallback() {\n    data.write(\"8900\".decodeHex()) // Empty ping\n    clientReader.processNextFrame()\n    callback.assertPing(EMPTY)\n  }\n\n  @Test fun pingCallsCallback() {\n    data.write(\"890548656c6c6f\".decodeHex()) // Ping with \"Hello\"\n    clientReader.processNextFrame()\n    callback.assertPing(\"Hello\".encodeUtf8())\n  }\n\n  @Test fun emptyCloseCallsCallback() {\n    data.write(\"8800\".decodeHex()) // Empty close\n    clientReader.processNextFrame()\n    callback.assertClosing(1005, \"\")\n  }\n\n  @Test fun closeLengthOfOneThrows() {\n    data.write(\"880100\".decodeHex()) // Close with invalid 1-byte payload\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Malformed close payload length of 1.\")\n    }\n  }\n\n  @Test fun closeCallsCallback() {\n    data.write(\"880703e848656c6c6f\".decodeHex()) // Close with code and reason\n    clientReader.processNextFrame()\n    callback.assertClosing(1000, \"Hello\")\n  }\n\n  @Test fun closeIncompleteCallsCallback() {\n    data.write(\"880703e948656c6c6f\".decodeHex()) // Close with code and reason\n    data.close()\n    clientReader.processNextFrame()\n    callback.assertClosing(1001, \"Hello\")\n  }\n\n  @Test fun closeOutOfRangeThrows() {\n    data.write(\"88020001\".decodeHex()) // Close with code 1\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Code must be in range [1000,5000): 1\")\n    }\n    data.write(\"88021388\".decodeHex()) // Close with code 5000\n    assertFailsWith<ProtocolException> {\n      clientReader.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"Code must be in range [1000,5000): 5000\")\n    }\n  }\n\n  @Test fun closeReservedSetThrows() {\n    data.write(\"880203ec\".decodeHex()) // Close with code 1004\n    data.write(\"880203ed\".decodeHex()) // Close with code 1005\n    data.write(\"880203ee\".decodeHex()) // Close with code 1006\n    for (i in 1015..2999) {\n      data.write((\"8802\" + format(\"%04X\", i)).decodeHex()) // Close with code 'i'\n    }\n    var count = 0\n    while (!data.exhausted()) {\n      assertFailsWith<ProtocolException> {\n        clientReader.processNextFrame()\n      }.also { expected ->\n        assertThat(expected.message!!)\n          .matches(Regex(\"Code \\\\d+ is reserved and may not be used.\"))\n      }\n      count++\n    }\n    assertThat(count).isEqualTo(1988)\n  }\n\n  @Test fun clientWithCompressionCannotBeUsedAfterClose() {\n    data.write(\"c107f248cdc9c90700\".decodeHex()) // Hello\n    clientReaderWithCompression.processNextFrame()\n    callback.assertTextMessage(\"Hello\")\n    data.write(\"c107f248cdc9c90700\".decodeHex()) // Hello\n    clientReaderWithCompression.close()\n    assertFailsWith<IllegalStateException> {\n      clientReaderWithCompression.processNextFrame()\n    }.also { expected ->\n      assertThat(expected.message!!).contains(\"closed\")\n    }\n  }\n\n  private fun binaryData(length: Int): ByteString {\n    val junk = ByteArray(length)\n    random.nextBytes(junk)\n    return junk.toByteString()\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/ws/WebSocketRecorder.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.isSameInstanceAs\nimport java.io.IOException\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.TimeUnit\nimport okhttp3.Response\nimport okhttp3.WebSocket\nimport okhttp3.WebSocketListener\nimport okhttp3.internal.platform.Platform\nimport okio.ByteString\n\nclass WebSocketRecorder(\n  private val name: String,\n) : WebSocketListener() {\n  private val events = LinkedBlockingQueue<Any>()\n  private var delegate: WebSocketListener? = null\n\n  /** Sets a delegate for handling the next callback to this listener. Cleared after invoked.  */\n  fun setNextEventDelegate(delegate: WebSocketListener?) {\n    this.delegate = delegate\n  }\n\n  override fun onOpen(\n    webSocket: WebSocket,\n    response: Response,\n  ) {\n    Platform.get().log(\"[WS $name] onOpen\", Platform.INFO, null)\n    val delegate = delegate\n    if (delegate != null) {\n      this.delegate = null\n      delegate.onOpen(webSocket, response)\n    } else {\n      events.add(Open(webSocket, response))\n    }\n  }\n\n  override fun onMessage(\n    webSocket: WebSocket,\n    bytes: ByteString,\n  ) {\n    Platform.get().log(\"[WS $name] onMessage\", Platform.INFO, null)\n    val delegate = delegate\n    if (delegate != null) {\n      this.delegate = null\n      delegate.onMessage(webSocket, bytes)\n    } else {\n      events.add(Message(bytes = bytes))\n    }\n  }\n\n  override fun onMessage(\n    webSocket: WebSocket,\n    text: String,\n  ) {\n    Platform.get().log(\"[WS $name] onMessage\", Platform.INFO, null)\n    val delegate = delegate\n    if (delegate != null) {\n      this.delegate = null\n      delegate.onMessage(webSocket, text)\n    } else {\n      events.add(Message(string = text))\n    }\n  }\n\n  override fun onClosing(\n    webSocket: WebSocket,\n    code: Int,\n    reason: String,\n  ) {\n    Platform.get().log(\"[WS $name] onClosing $code\", Platform.INFO, null)\n    val delegate = delegate\n    if (delegate != null) {\n      this.delegate = null\n      delegate.onClosing(webSocket, code, reason)\n    } else {\n      events.add(Closing(code, reason))\n    }\n  }\n\n  override fun onClosed(\n    webSocket: WebSocket,\n    code: Int,\n    reason: String,\n  ) {\n    Platform.get().log(\"[WS $name] onClosed $code\", Platform.INFO, null)\n    val delegate = delegate\n    if (delegate != null) {\n      this.delegate = null\n      delegate.onClosed(webSocket, code, reason)\n    } else {\n      events.add(Closed(code, reason))\n    }\n  }\n\n  override fun onFailure(\n    webSocket: WebSocket,\n    t: Throwable,\n    response: Response?,\n  ) {\n    Platform.get().log(\"[WS $name] onFailure\", Platform.INFO, t)\n    val delegate = delegate\n    if (delegate != null) {\n      this.delegate = null\n      delegate.onFailure(webSocket, t, response)\n    } else {\n      events.add(Failure(t, response))\n    }\n  }\n\n  private fun nextEvent(): Any =\n    events.poll(10, TimeUnit.SECONDS)\n      ?: throw AssertionError(\"Timed out waiting for event.\")\n\n  fun assertTextMessage(payload: String?) {\n    assertThat(nextEvent()).isEqualTo(Message(string = payload))\n  }\n\n  fun assertBinaryMessage(payload: ByteString?) {\n    assertThat(nextEvent()).isEqualTo(Message(payload))\n  }\n\n  fun assertPing(payload: ByteString) {\n    assertThat(nextEvent()).isEqualTo(Ping(payload))\n  }\n\n  fun assertPong(payload: ByteString) {\n    assertThat(nextEvent()).isEqualTo(Pong(payload))\n  }\n\n  fun assertClosing(\n    code: Int,\n    reason: String,\n  ) {\n    assertThat(nextEvent()).isEqualTo(Closing(code, reason))\n  }\n\n  fun assertClosed(\n    code: Int,\n    reason: String,\n  ) {\n    assertThat(nextEvent()).isEqualTo(Closed(code, reason))\n  }\n\n  fun assertExhausted() {\n    assertThat(events).isEmpty()\n  }\n\n  fun assertOpen(): WebSocket {\n    val event = nextEvent() as Open\n    return event.webSocket\n  }\n\n  fun assertFailure(t: Throwable?) {\n    val event = nextEvent() as Failure\n    assertThat(event.response).isNull()\n    assertThat(event.t).isSameInstanceAs(t)\n  }\n\n  fun assertFailure(\n    cls: Class<out IOException?>?,\n    vararg messages: String,\n  ) {\n    val event = nextEvent() as Failure\n    assertThat(event.response).isNull()\n    assertThat(event.t.javaClass).isEqualTo(cls)\n    if (messages.isNotEmpty()) {\n      assertThat(messages).contains(event.t.message)\n    }\n  }\n\n  fun assertFailure() {\n    nextEvent() as Failure\n  }\n\n  fun assertFailure(\n    code: Int,\n    body: String?,\n    cls: Class<out IOException?>?,\n    message: String?,\n  ) {\n    val event = nextEvent() as Failure\n    assertThat(event.response!!.code).isEqualTo(code)\n    if (body != null) {\n      assertThat(event.responseBody).isEqualTo(body)\n    }\n    assertThat(event.t.javaClass).isEqualTo(cls)\n    assertThat(event.t.message).isEqualTo(message)\n  }\n\n  /** Expose this recorder as a frame callback and shim in \"ping\" events.  */\n  fun asFrameCallback() =\n    object : WebSocketReader.FrameCallback {\n      override fun onReadMessage(text: String) {\n        events.add(Message(string = text))\n      }\n\n      override fun onReadMessage(bytes: ByteString) {\n        events.add(Message(bytes = bytes))\n      }\n\n      override fun onReadPing(payload: ByteString) {\n        events.add(Ping(payload))\n      }\n\n      override fun onReadPong(payload: ByteString) {\n        events.add(Pong(payload))\n      }\n\n      override fun onReadClose(\n        code: Int,\n        reason: String,\n      ) {\n        events.add(Closing(code, reason))\n      }\n    }\n\n  internal class Open(\n    val webSocket: WebSocket,\n    val response: Response,\n  )\n\n  internal class Failure(\n    val t: Throwable,\n    val response: Response?,\n  ) {\n    val responseBody: String? =\n      when {\n        response != null && response.code != 101 -> response.body.string()\n        else -> null\n      }\n\n    override fun toString(): String =\n      when (response) {\n        null -> \"Failure[$t]\"\n        else -> \"Failure[$response]\"\n      }\n  }\n\n  internal data class Message(\n    val bytes: ByteString? = null,\n    val string: String? = null,\n  )\n\n  internal data class Ping(\n    val payload: ByteString,\n  )\n\n  internal data class Pong(\n    val payload: ByteString,\n  )\n\n  internal data class Closing(\n    val code: Int,\n    val reason: String,\n  )\n\n  internal data class Closed(\n    val code: Int,\n    val reason: String,\n  )\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/kotlin/okhttp3/internal/ws/WebSocketWriterTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.ws\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.util.Random\nimport kotlin.test.assertFailsWith\nimport okhttp3.TestUtil.repeat\nimport okhttp3.internal.format\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_BINARY\nimport okhttp3.internal.ws.WebSocketProtocol.OPCODE_TEXT\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_BYTE_MAX\nimport okhttp3.internal.ws.WebSocketProtocol.PAYLOAD_SHORT_MAX\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.EMPTY\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.AfterEachCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass WebSocketWriterTest {\n  private val data = Buffer()\n  private val random = Random(0)\n\n  /**\n   * Check all data as verified inside of the test. We do this in an AfterEachCallback so that\n   * exceptions thrown from the test do not cause this check to fail.\n   */\n  @RegisterExtension\n  val noDataLeftBehind =\n    AfterEachCallback { context: ExtensionContext ->\n      if (context.executionException.isPresent) return@AfterEachCallback\n      assertThat(data.readByteString().hex(), \"Data not empty\")\n        .isEqualTo(\"\")\n    }\n\n  // Mutually exclusive. Use the one corresponding to the peer whose behavior you wish to test.\n  private val serverWriter =\n    WebSocketWriter(\n      isClient = false,\n      sink = data,\n      random = random,\n      perMessageDeflate = false,\n      noContextTakeover = false,\n      minimumDeflateSize = 0L,\n    )\n  private val clientWriter =\n    WebSocketWriter(\n      isClient = true,\n      sink = data,\n      random = random,\n      perMessageDeflate = false,\n      noContextTakeover = false,\n      minimumDeflateSize = 0L,\n    )\n\n  @Test fun serverTextMessage() {\n    serverWriter.writeMessageFrame(OPCODE_TEXT, \"Hello\".encodeUtf8())\n    assertData(\"810548656c6c6f\")\n  }\n\n  @Test fun serverCompressedTextMessage() {\n    val serverWriter =\n      WebSocketWriter(\n        false,\n        data,\n        random,\n        true,\n        false,\n        0L,\n      )\n    serverWriter.writeMessageFrame(OPCODE_TEXT, \"Hello\".encodeUtf8())\n    assertData(\"c107f248cdc9c90700\")\n  }\n\n  @Test fun serverSmallBufferedPayloadWrittenAsOneFrame() {\n    val length = 5\n    val payload: ByteString = (binaryData(length))\n    serverWriter.writeMessageFrame(OPCODE_TEXT, payload)\n    assertData(\"8105\")\n    assertData(payload)\n  }\n\n  @Test fun serverLargeBufferedPayloadWrittenAsOneFrame() {\n    val length = 12345\n    val payload: ByteString = (binaryData(length))\n    serverWriter.writeMessageFrame(OPCODE_TEXT, payload)\n    assertData(\"817e\")\n    assertData(format(\"%04x\", length))\n    assertData(payload)\n  }\n\n  @Test fun clientTextMessage() {\n    clientWriter.writeMessageFrame(OPCODE_TEXT, \"Hello\".encodeUtf8())\n    assertData(\"818560b420bb28d14cd70f\")\n  }\n\n  @Test fun clientCompressedTextMessage() {\n    val clientWriter =\n      WebSocketWriter(\n        false,\n        data,\n        random,\n        true,\n        false,\n        0L,\n      )\n    clientWriter.writeMessageFrame(OPCODE_TEXT, \"Hello\".encodeUtf8())\n    assertData(\"c107f248cdc9c90700\")\n  }\n\n  @Test fun serverBinaryMessage() {\n    val payload =\n      (\n        \"60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da71\" +\n          \"2c82bcd4d554bf0b54023c2\"\n      ).decodeHex()\n    serverWriter.writeMessageFrame(OPCODE_BINARY, payload)\n    assertData(\"8232\")\n    assertData(payload)\n  }\n\n  @Test fun serverMessageLengthShort() {\n    // Create a payload which will overflow the normal payload byte size.\n    val payload = Buffer()\n    while (payload.completeSegmentByteCount() <= PAYLOAD_BYTE_MAX) {\n      payload.writeByte('0'.code)\n    }\n    serverWriter.writeMessageFrame(OPCODE_BINARY, payload.snapshot())\n\n    // Write directly to the unbuffered sink. This ensures it will become single frame.\n    assertData(\"827e\") // 'e' == 4-byte follow-up length.\n    assertData(format(\"%04X\", payload.completeSegmentByteCount()))\n    assertData(payload.readByteString())\n  }\n\n  @Test fun serverMessageLengthLong() {\n    // Create a payload which will overflow the normal and short payload byte size.\n    val payload = Buffer()\n    while (payload.completeSegmentByteCount() <= PAYLOAD_SHORT_MAX) {\n      payload.writeByte('0'.code)\n    }\n    serverWriter.writeMessageFrame(OPCODE_BINARY, payload.snapshot())\n\n    // Write directly to the unbuffered sink. This ensures it will become single frame.\n    assertData(\"827f\") // 'f' == 16-byte follow-up length.\n    assertData(format(\"%016X\", payload.size))\n    assertData(payload.readByteString())\n  }\n\n  @Test fun clientBinary() {\n    val payload =\n      (\n        \"60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da71\" +\n          \"2c82bcd4d554bf0b54023c2\"\n      ).decodeHex()\n    clientWriter.writeMessageFrame(OPCODE_BINARY, payload)\n    assertData(\"82b2\")\n    assertData(\"60b420bb\")\n    assertData(\n      \"0000000058e5f96f1a7fb386dec41920967d0d185a443df4d7c4c9376391d4a65e0ed8230d1332734b796dee2\" +\n        \"b4495fb4376\",\n    )\n  }\n\n  @Test fun serverEmptyClose() {\n    serverWriter.writeClose(0, null)\n    assertData(\"8800\")\n  }\n\n  @Test fun serverCloseWithCode() {\n    serverWriter.writeClose(1001, null)\n    assertData(\"880203e9\")\n  }\n\n  @Test fun serverCloseWithCodeAndReason() {\n    serverWriter.writeClose(1001, \"Hello\".encodeUtf8())\n    assertData(\"880703e948656c6c6f\")\n  }\n\n  @Test fun clientEmptyClose() {\n    clientWriter.writeClose(0, null)\n    assertData(\"888060b420bb\")\n  }\n\n  @Test fun clientCloseWithCode() {\n    clientWriter.writeClose(1001, null)\n    assertData(\"888260b420bb635d\")\n  }\n\n  @Test fun clientCloseWithCodeAndReason() {\n    clientWriter.writeClose(1001, \"Hello\".encodeUtf8())\n    assertData(\"888760b420bb635d68de0cd84f\")\n  }\n\n  @Test fun closeWithOnlyReasonThrows() {\n    clientWriter.writeClose(0, \"Hello\".encodeUtf8())\n    assertData(\"888760b420bb60b468de0cd84f\")\n  }\n\n  @Test fun closeCodeOutOfRangeThrows() {\n    assertFailsWith<IllegalArgumentException> {\n      clientWriter.writeClose(98724976, \"Hello\".encodeUtf8())\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Code must be in range [1000,5000): 98724976\")\n    }\n  }\n\n  @Test fun closeReservedThrows() {\n    assertFailsWith<IllegalArgumentException> {\n      clientWriter.writeClose(1005, \"Hello\".encodeUtf8())\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"Code 1005 is reserved and may not be used.\")\n    }\n  }\n\n  @Test fun serverEmptyPing() {\n    serverWriter.writePing(EMPTY)\n    assertData(\"8900\")\n  }\n\n  @Test fun clientEmptyPing() {\n    clientWriter.writePing(EMPTY)\n    assertData(\"898060b420bb\")\n  }\n\n  @Test fun serverPingWithPayload() {\n    serverWriter.writePing(\"Hello\".encodeUtf8())\n    assertData(\"890548656c6c6f\")\n  }\n\n  @Test fun clientPingWithPayload() {\n    clientWriter.writePing(\"Hello\".encodeUtf8())\n    assertData(\"898560b420bb28d14cd70f\")\n  }\n\n  @Test fun serverEmptyPong() {\n    serverWriter.writePong(EMPTY)\n    assertData(\"8a00\")\n  }\n\n  @Test fun clientEmptyPong() {\n    clientWriter.writePong(EMPTY)\n    assertData(\"8a8060b420bb\")\n  }\n\n  @Test fun serverPongWithPayload() {\n    serverWriter.writePong(\"Hello\".encodeUtf8())\n    assertData(\"8a0548656c6c6f\")\n  }\n\n  @Test fun clientPongWithPayload() {\n    clientWriter.writePong(\"Hello\".encodeUtf8())\n    assertData(\"8a8560b420bb28d14cd70f\")\n  }\n\n  @Test fun pingTooLongThrows() {\n    assertFailsWith<IllegalArgumentException> {\n      serverWriter.writePing(binaryData(1000))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Payload size must be less than or equal to 125\",\n      )\n    }\n  }\n\n  @Test fun pongTooLongThrows() {\n    assertFailsWith<IllegalArgumentException> {\n      serverWriter.writePong((binaryData(1000)))\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Payload size must be less than or equal to 125\",\n      )\n    }\n  }\n\n  @Test fun closeTooLongThrows() {\n    assertFailsWith<IllegalArgumentException> {\n      val longReason: ByteString = repeat('X', 124).encodeUtf8()\n      serverWriter.writeClose(1000, longReason)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\n        \"Payload size must be less than or equal to 125\",\n      )\n    }\n  }\n\n  private fun assertData(hex: String) {\n    assertData(hex.decodeHex())\n  }\n\n  private fun assertData(expected: ByteString) {\n    val actual = data.readByteString(Math.min(expected.size.toLong(), data.size))\n    assertThat(actual).isEqualTo(expected)\n  }\n\n  companion object {\n    private fun binaryData(length: Int): ByteString {\n      val junk = ByteArray(length)\n      Random(0).nextBytes(junk)\n      return junk.toByteString()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp/src/jvmTest/resources/okhttp3/internal/publicsuffix/NOTICE",
    "content": "Note that public_suffix_list.dat is compiled from The Public Suffix List:\nhttps://publicsuffix.org/list/public_suffix_list.dat\n\nIt is subject to the terms of the Mozilla Public License, v. 2.0:\nhttps://mozilla.org/MPL/2.0/\n"
  },
  {
    "path": "okhttp/src/jvmTest/resources/web-platform-test-toascii.json",
    "content": "[\n  {\n    \"comment\": \"Label with hyphens in 3rd and 4th position\",\n    \"input\": \"aa--\",\n    \"output\": \"aa--\"\n  },\n  {\n    \"input\": \"a†--\",\n    \"output\": \"xn--a---kp0a\"\n  },\n  {\n    \"input\": \"ab--c\",\n    \"output\": \"ab--c\"\n  },\n  {\n    \"comment\": \"Label with leading hyphen\",\n    \"input\": \"-x\",\n    \"output\": \"-x\"\n  },\n  {\n    \"input\": \"-†\",\n    \"output\": \"xn----xhn\"\n  },\n  {\n    \"input\": \"-x.xn--zca\",\n    \"output\": \"-x.xn--zca\"\n  },\n  {\n    \"input\": \"-x.ß\",\n    \"output\": \"-x.xn--zca\"\n  },\n  {\n    \"comment\": \"Label with trailing hyphen\",\n    \"input\": \"x-.xn--zca\",\n    \"output\": \"x-.xn--zca\"\n  },\n  {\n    \"input\": \"x-.ß\",\n    \"output\": \"x-.xn--zca\"\n  },\n  {\n    \"comment\": \"Empty labels\",\n    \"input\": \"x..xn--zca\",\n    \"output\": \"x..xn--zca\"\n  },\n  {\n    \"input\": \"x..ß\",\n    \"output\": \"x..xn--zca\"\n  },\n  {\n    \"comment\": \"Invalid Punycode\",\n    \"input\": \"xn--a\",\n    \"output\": null\n  },\n  {\n    \"input\": \"xn--a.xn--zca\",\n    \"output\": null\n  },\n  {\n    \"input\": \"xn--a.ß\",\n    \"output\": null\n  },\n  {\n    \"input\": \"xn--ls8h=\",\n    \"output\": null\n  },\n  {\n    \"comment\": \"Invalid Punycode (contains non-ASCII character)\",\n    \"input\": \"xn--tešla\",\n    \"output\": null\n  },\n  {\n    \"comment\": \"Valid Punycode\",\n    \"input\": \"xn--zca.xn--zca\",\n    \"output\": \"xn--zca.xn--zca\"\n  },\n  {\n    \"comment\": \"Mixed\",\n    \"input\": \"xn--zca.ß\",\n    \"output\": \"xn--zca.xn--zca\"\n  },\n  {\n    \"input\": \"ab--c.xn--zca\",\n    \"output\": \"ab--c.xn--zca\"\n  },\n  {\n    \"input\": \"ab--c.ß\",\n    \"output\": \"ab--c.xn--zca\"\n  },\n  {\n    \"comment\": \"CheckJoiners is true\",\n    \"input\": \"\\u200D.example\",\n    \"output\": null\n  },\n  {\n    \"input\": \"xn--1ug.example\",\n    \"output\": null\n  },\n  {\n    \"comment\": \"CheckBidi is true\",\n    \"input\": \"يa\",\n    \"output\": null\n  },\n  {\n    \"input\": \"xn--a-yoc\",\n    \"output\": null\n  },\n  {\n    \"comment\": \"processing_option is Nontransitional_Processing\",\n    \"input\": \"ශ්‍රී\",\n    \"output\": \"xn--10cl1a0b660p\"\n  },\n  {\n    \"input\": \"نامه‌ای\",\n    \"output\": \"xn--mgba3gch31f060k\"\n  },\n  {\n    \"comment\": \"U+FFFD\",\n    \"input\": \"\\uFFFD.com\",\n    \"output\": null\n  },\n  {\n    \"comment\": \"U+FFFD character encoded in Punycode\",\n    \"input\": \"xn--zn7c.com\",\n    \"output\": null\n  },\n  {\n    \"comment\": \"Label longer than 63 code points\",\n    \"input\": \"x01234567890123456789012345678901234567890123456789012345678901x\",\n    \"output\": \"x01234567890123456789012345678901234567890123456789012345678901x\"\n  },\n  {\n    \"input\": \"x01234567890123456789012345678901234567890123456789012345678901†\",\n    \"output\": \"xn--x01234567890123456789012345678901234567890123456789012345678901-6963b\"\n  },\n  {\n    \"input\": \"x01234567890123456789012345678901234567890123456789012345678901x.xn--zca\",\n    \"output\": \"x01234567890123456789012345678901234567890123456789012345678901x.xn--zca\"\n  },\n  {\n    \"input\": \"x01234567890123456789012345678901234567890123456789012345678901x.ß\",\n    \"output\": \"x01234567890123456789012345678901234567890123456789012345678901x.xn--zca\"\n  },\n  {\n    \"comment\": \"Domain excluding TLD longer than 253 code points\",\n    \"input\": \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.x\",\n    \"output\": \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.x\"\n  },\n  {\n    \"input\": \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.xn--zca\",\n    \"output\": \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.xn--zca\"\n  },\n  {\n    \"input\": \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.ß\",\n    \"output\": \"01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.xn--zca\"\n  },\n  {\n    \"comment\": \"IDNA ignored code points\",\n    \"input\": \"a\\u00ADb\",\n    \"output\": \"ab\"\n  },\n  {\n    \"input\": \"a%C2%ADb\",\n    \"output\": \"ab\"\n  },\n  {\n    \"comment\": \"Empty host after domain to ASCII\",\n    \"input\": \"\\u00AD\",\n    \"output\": null\n  },\n  {\n    \"input\": \"%C2%AD\",\n    \"output\": null\n  },\n  {\n    \"input\": \"xn--\",\n    \"output\": null\n  },\n  {\n    \"comment\": \"Interesting UseSTD3ASCIIRules=false cases\",\n    \"input\": \"≠\",\n    \"output\": \"xn--1ch\"\n  },\n  {\n    \"input\": \"≮\",\n    \"output\": \"xn--gdh\"\n  },\n  {\n    \"input\": \"≯\",\n    \"output\": \"xn--hdh\"\n  }\n]\n"
  },
  {
    "path": "okhttp/src/jvmTest/resources/web-platform-test-urltestdata.txt",
    "content": "# FORMAT NOT DOCUMENTED YET (parser is urltestparser.js)\n\n# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/segments.js\nhttp://example\\t.\\norg http://example.org/foo/bar s:http h:example.org p:/\nhttp://user:pass@foo:21/bar;par?b#c  s:http u:user pass:pass h:foo port:21 p:/bar;par q:?b f:#c\nhttp:foo.com  s:http h:example.org p:/foo/foo.com\n\\t\\s\\s\\s:foo.com\\s\\s\\s\\n  s:http h:example.org p:/foo/:foo.com\n\\sfoo.com\\s\\s  s:http h:example.org p:/foo/foo.com\na:\\t\\sfoo.com  s:a p:\\sfoo.com\nhttp://f:21/\\sb\\s?\\sd\\s#\\se\\s  s:http h:f port:21 p:/%20b%20 q:?%20d%20 f:#\\se\nhttp://f:/c  s:http h:f p:/c\nhttp://f:0/c  s:http h:f port:0 p:/c\nhttp://f:00000000000000/c  s:http h:f port:0 p:/c\nhttp://f:00000000000000000000080/c  s:http h:f p:/c\nhttp://f:b/c\nhttp://f:\\s/c\nhttp://f:\\n/c  s:http h:f p:/c\nhttp://f:fifty-two/c\nhttp://f:999999/c  s:http h:f port:999999 p:/c\nhttp://f:\\s21\\s/\\sb\\s?\\sd\\s#\\se\\s\n  s:http h:example.org p:/foo/bar\n\\s\\s\\t  s:http h:example.org p:/foo/bar\n:foo.com/  s:http h:example.org p:/foo/:foo.com/\n:foo.com\\\\  s:http h:example.org p:/foo/:foo.com/\n:  s:http h:example.org p:/foo/:\n:a  s:http h:example.org p:/foo/:a\n:/  s:http h:example.org p:/foo/:/\n:\\\\  s:http h:example.org p:/foo/:/\n:#  s:http h:example.org p:/foo/: f:#\n\\#  s:http h:example.org p:/foo/bar f:#\n\\#/  s:http h:example.org p:/foo/bar f:#/\n\\#\\\\  s:http h:example.org p:/foo/bar f:#\\\\\n\\#;?  s:http h:example.org p:/foo/bar f:#;?\n?  s:http h:example.org p:/foo/bar q:?\n/  s:http h:example.org p:/\n:23  s:http h:example.org p:/foo/:23\n/:23  s:http h:example.org p:/:23\n::  s:http h:example.org p:/foo/::\n::23  s:http h:example.org p:/foo/::23\nfoo://  s:foo p://\nhttp://a:b@c:29/d  s:http u:a pass:b h:c port:29 p:/d\nhttp::@c:29  s:http h:example.org p:/foo/:@c:29\nhttp://&a:foo(b]c@d:2/  s:http u:&a pass:foo(b]c h:d port:2 p:/\nhttp://::@c@d:2  s:http pass::%40c h:d port:2 p:/\nhttp://foo.com:b@d/  s:http u:foo.com pass:b h:d p:/\nhttp://foo.com/\\\\@  s:http h:foo.com p://@\nhttp:\\\\\\\\foo.com\\\\  s:http h:foo.com p:/\nhttp:\\\\\\\\a\\\\b:c\\\\d@foo.com\\\\  s:http h:a p:/b:c/d@foo.com/\nfoo:/  s:foo p:/\nfoo:/bar.com/  s:foo p:/bar.com/\nfoo://///////  s:foo p://///////\nfoo://///////bar.com/  s:foo p://///////bar.com/\nfoo:////://///  s:foo p:////://///\nc:/foo  s:c p:/foo\n//foo/bar  s:http h:foo p:/bar\nhttp://foo/path;a??e#f#g  s:http h:foo p:/path;a q:??e f:#f#g\nhttp://foo/abcd?efgh?ijkl  s:http h:foo p:/abcd q:?efgh?ijkl\nhttp://foo/abcd#foo?bar  s:http h:foo p:/abcd f:#foo?bar\n[61:24:74]:98  s:http h:example.org p:/foo/[61:24:74]:98\nhttp:[61:27]/:foo  s:http h:example.org p:/foo/[61:27]/:foo\nhttp://[1::2]:3:4\nhttp://2001::1\nhttp://2001::1]\nhttp://2001::1]:80\nhttp://[2001::1]  s:http h:[2001::1] p:/\nhttp://[2001::1]:80  s:http h:[2001::1] p:/\nhttp:/example.com/  s:http h:example.org p:/example.com/\nftp:/example.com/  s:ftp h:example.com p:/\nhttps:/example.com/  s:https h:example.com p:/\nmadeupscheme:/example.com/  s:madeupscheme p:/example.com/\nfile:/example.com/  s:file p:/example.com/\nftps:/example.com/  s:ftps p:/example.com/\ngopher:/example.com/  s:gopher h:example.com p:/\nws:/example.com/  s:ws h:example.com p:/\nwss:/example.com/  s:wss h:example.com p:/\ndata:/example.com/  s:data p:/example.com/\njavascript:/example.com/  s:javascript p:/example.com/\nmailto:/example.com/  s:mailto p:/example.com/\nhttp:example.com/  s:http h:example.org p:/foo/example.com/\nftp:example.com/  s:ftp h:example.com p:/\nhttps:example.com/  s:https h:example.com p:/\nmadeupscheme:example.com/  s:madeupscheme p:example.com/\nftps:example.com/  s:ftps p:example.com/\ngopher:example.com/  s:gopher h:example.com p:/\nws:example.com/  s:ws h:example.com p:/\nwss:example.com/  s:wss h:example.com p:/\ndata:example.com/  s:data p:example.com/\njavascript:example.com/  s:javascript p:example.com/\nmailto:example.com/  s:mailto p:example.com/\n/a/b/c  s:http h:example.org p:/a/b/c\n/a/\\s/c  s:http h:example.org p:/a/%20/c\n/a%2fc  s:http h:example.org p:/a%2fc\n/a/%2f/c  s:http h:example.org p:/a/%2f/c\n\\#\\u03B2  s:http h:example.org p:/foo/bar f:#\\u03B2\ndata:text/html,test#test  s:data p:text/html,test f:#test\n\n# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/file.html\n\n# Basic canonicalization, uppercase should be converted to lowercase\nfile:c:\\\\foo\\\\bar.html file:///tmp/mock/path s:file p:/c:/foo/bar.html\n\n# Spaces should fail\n\\s\\sFile:c|////foo\\\\bar.html  s:file p:/c:////foo/bar.html\n\n# This should fail\nC|/foo/bar  s:file p:/C:/foo/bar\n\n# This should fail\n/C|\\\\foo\\\\bar  s:file p:/C:/foo/bar\n//C|/foo/bar  s:file p:/C:/foo/bar\n//server/file  s:file h:server p:/file\n\\\\\\\\server\\\\file  s:file h:server p:/file\n/\\\\server/file  s:file h:server p:/file\nfile:///foo/bar.txt  s:file p:/foo/bar.txt\nfile:///home/me  s:file p:/home/me\n//  s:file p:/\n///  s:file p:/\n///test  s:file p:/test\nfile://test  s:file h:test p:/\nfile://localhost  s:file h:localhost p:/\nfile://localhost/  s:file h:localhost p:/\nfile://localhost/test  s:file h:localhost p:/test\ntest  s:file p:/tmp/mock/test\nfile:test  s:file p:/tmp/mock/test\n\n# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/path.js\nhttp://example.com/././foo about:blank s:http h:example.com p:/foo\nhttp://example.com/./.foo  s:http h:example.com p:/.foo\nhttp://example.com/foo/.  s:http h:example.com p:/foo/\nhttp://example.com/foo/./  s:http h:example.com p:/foo/\nhttp://example.com/foo/bar/..  s:http h:example.com p:/foo/\nhttp://example.com/foo/bar/../  s:http h:example.com p:/foo/\nhttp://example.com/foo/..bar  s:http h:example.com p:/foo/..bar\nhttp://example.com/foo/bar/../ton  s:http h:example.com p:/foo/ton\nhttp://example.com/foo/bar/../ton/../../a  s:http h:example.com p:/a\nhttp://example.com/foo/../../..  s:http h:example.com p:/\nhttp://example.com/foo/../../../ton  s:http h:example.com p:/ton\nhttp://example.com/foo/%2e  s:http h:example.com p:/foo/\nhttp://example.com/foo/%2e%2  s:http h:example.com p:/foo/%2e%2\nhttp://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar  s:http h:example.com p:/%2e.bar\nhttp://example.com////../..  s:http h:example.com p://\nhttp://example.com/foo/bar//../..  s:http h:example.com p:/foo/\nhttp://example.com/foo/bar//..  s:http h:example.com p:/foo/bar/\nhttp://example.com/foo  s:http h:example.com p:/foo\nhttp://example.com/%20foo  s:http h:example.com p:/%20foo\nhttp://example.com/foo%  s:http h:example.com p:/foo%\nhttp://example.com/foo%2  s:http h:example.com p:/foo%2\nhttp://example.com/foo%2zbar  s:http h:example.com p:/foo%2zbar\nhttp://example.com/foo%2\\u00C2\\u00A9zbar  s:http h:example.com p:/foo%2%C3%82%C2%A9zbar\nhttp://example.com/foo%41%7a  s:http h:example.com p:/foo%41%7a\nhttp://example.com/foo\\t\\u0091%91  s:http h:example.com p:/foo%C2%91%91\nhttp://example.com/foo%00%51  s:http h:example.com p:/foo%00%51\nhttp://example.com/(%28:%3A%29)  s:http h:example.com p:/(%28:%3A%29)\nhttp://example.com/%3A%3a%3C%3c  s:http h:example.com p:/%3A%3a%3C%3c\nhttp://example.com/foo\\tbar  s:http h:example.com p:/foobar\nhttp://example.com\\\\\\\\foo\\\\\\\\bar  s:http h:example.com p://foo//bar\nhttp://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd  s:http h:example.com p:/%7Ffp3%3Eju%3Dduvgw%3Dd\nhttp://example.com/@asdf%40  s:http h:example.com p:/@asdf%40\nhttp://example.com/\\u4F60\\u597D\\u4F60\\u597D  s:http h:example.com p:/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD\nhttp://example.com/\\u2025/foo  s:http h:example.com p:/%E2%80%A5/foo\nhttp://example.com/\\uFEFF/foo  s:http h:example.com p:/%EF%BB%BF/foo\nhttp://example.com/\\u202E/foo/\\u202D/bar  s:http h:example.com p:/%E2%80%AE/foo/%E2%80%AD/bar\n\n# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/relative.js\nhttp://www.google.com/foo?bar=baz# about:blank s:http h:www.google.com p:/foo q:?bar=baz f:#\nhttp://www.google.com/foo?bar=baz#\\s\\u00BB  s:http h:www.google.com p:/foo q:?bar=baz f:#\\s\\u00BB\ndata:test#\\s\\u00BB  s:data p:test f:#\\s\\u00BB\nhttp://[www.google.com]/\nhttp://www.google.com  s:http h:www.google.com p:/\nhttp://192.0x00A80001  s:http h:192.168.0.1 p:/\nhttp://www/foo%2Ehtml  s:http h:www p:/foo%2Ehtml\nhttp://www/foo/%2E/html  s:http h:www p:/foo/html\nhttp://user:pass@/\nhttp://%25DOMAIN:foobar@foodomain.com/  s:http u:%25DOMAIN pass:foobar h:foodomain.com p:/\nhttp:\\\\\\\\www.google.com\\\\foo  s:http h:www.google.com p:/foo\nhttp://foo:80/  s:http h:foo p:/\nhttp://foo:81/  s:http h:foo port:81 p:/\nhttpa://foo:80/  s:httpa p://foo:80/\nhttp://foo:-80/\nhttps://foo:443/  s:https h:foo p:/\nhttps://foo:80/  s:https h:foo port:80 p:/\nftp://foo:21/  s:ftp h:foo p:/\nftp://foo:80/  s:ftp h:foo port:80 p:/\ngopher://foo:70/  s:gopher h:foo p:/\ngopher://foo:443/  s:gopher h:foo port:443 p:/\nws://foo:80/  s:ws h:foo p:/\nws://foo:81/  s:ws h:foo port:81 p:/\nws://foo:443/  s:ws h:foo port:443 p:/\nws://foo:815/  s:ws h:foo port:815 p:/\nwss://foo:80/  s:wss h:foo port:80 p:/\nwss://foo:81/  s:wss h:foo port:81 p:/\nwss://foo:443/  s:wss h:foo p:/\nwss://foo:815/  s:wss h:foo port:815 p:/\nhttp:/example.com/  s:http h:example.com p:/\nftp:/example.com/  s:ftp h:example.com p:/\nhttps:/example.com/  s:https h:example.com p:/\nmadeupscheme:/example.com/  s:madeupscheme p:/example.com/\nfile:/example.com/  s:file p:/example.com/\nftps:/example.com/  s:ftps p:/example.com/\ngopher:/example.com/  s:gopher h:example.com p:/\nws:/example.com/  s:ws h:example.com p:/\nwss:/example.com/  s:wss h:example.com p:/\ndata:/example.com/  s:data p:/example.com/\njavascript:/example.com/  s:javascript p:/example.com/\nmailto:/example.com/  s:mailto p:/example.com/\nhttp:example.com/  s:http h:example.com p:/\nftp:example.com/  s:ftp h:example.com p:/\nhttps:example.com/  s:https h:example.com p:/\nmadeupscheme:example.com/  s:madeupscheme p:example.com/\nftps:example.com/  s:ftps p:example.com/\ngopher:example.com/  s:gopher h:example.com p:/\nws:example.com/  s:ws h:example.com p:/\nwss:example.com/  s:wss h:example.com p:/\ndata:example.com/  s:data p:example.com/\njavascript:example.com/  s:javascript p:example.com/\nmailto:example.com/  s:mailto p:example.com/\n\n# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/segments-userinfo-vs-host.html\nhttp:@www.example.com about:blank s:http h:www.example.com p:/\nhttp:/@www.example.com  s:http h:www.example.com p:/\nhttp://@www.example.com  s:http h:www.example.com p:/\nhttp:a:b@www.example.com  s:http u:a pass:b h:www.example.com p:/\nhttp:/a:b@www.example.com  s:http u:a pass:b h:www.example.com p:/\nhttp://a:b@www.example.com  s:http u:a pass:b h:www.example.com p:/\nhttp://@pple.com  s:http h:pple.com p:/\nhttp::b@www.example.com  s:http pass:b h:www.example.com p:/\nhttp:/:b@www.example.com  s:http pass:b h:www.example.com p:/\nhttp://:b@www.example.com  s:http pass:b h:www.example.com p:/\nhttp:/:@/www.example.com\nhttp://user@/www.example.com\nhttp:@/www.example.com\nhttp:/@/www.example.com\nhttp://@/www.example.com\nhttps:@/www.example.com\nhttp:a:b@/www.example.com\nhttp:/a:b@/www.example.com\nhttp://a:b@/www.example.com\nhttp::@/www.example.com\nhttp:a:@www.example.com  s:http u:a pass: h:www.example.com p:/\nhttp:/a:@www.example.com  s:http u:a pass: h:www.example.com p:/\nhttp://a:@www.example.com  s:http u:a pass: h:www.example.com p:/\nhttp://www.@pple.com  s:http u:www. h:pple.com p:/\nhttp:@:www.example.com\nhttp:/@:www.example.com\nhttp://@:www.example.com\nhttp://:@www.example.com  s:http pass: h:www.example.com p:/\n\n#Others\n/ http://www.example.com/test s:http h:www.example.com p:/\n/test.txt  s:http h:www.example.com p:/test.txt\n.  s:http h:www.example.com p:/\n..  s:http h:www.example.com p:/\ntest.txt  s:http h:www.example.com p:/test.txt\n./test.txt  s:http h:www.example.com p:/test.txt\n../test.txt  s:http h:www.example.com p:/test.txt\n../aaa/test.txt  s:http h:www.example.com p:/aaa/test.txt\n../../test.txt  s:http h:www.example.com p:/test.txt\n\\u4E2D/test.txt  s:http h:www.example.com p:/%E4%B8%AD/test.txt\nhttp://www.example2.com  s:http h:www.example2.com p:/\n//www.example2.com  s:http h:www.example2.com p:/\n\n# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/host.html\n\n# Basic canonicalization, uppercase should be converted to lowercase\nhttp://ExAmPlE.CoM http://other.com/ s:http p:/ h:example.com\n\n# Spaces should fail\nhttp://example\\sexample.com\n\n# This should fail\nhttp://Goo%20\\sgoo%7C|.com\n\n# U+3000 is mapped to U+0020 (space) which is disallowed\nhttp://GOO\\u00a0\\u3000goo.com\n\n# Other types of space (no-break, zero-width, zero-width-no-break) are\n# name-prepped away to nothing.\n# U+200B, U+2060, and U+FEFF, are ignored\nhttp://GOO\\u200b\\u2060\\ufeffgoo.com  s:http p:/ h:googoo.com\n\n# Ideographic full stop (full-width period for Chinese, etc.) should be\n# treated as a dot.\n# U+3002 is mapped to U+002E (dot)\nhttp://www.foo\\u3002bar.com  s:http p:/ h:www.foo.bar.com\n\n# Invalid unicode characters should fail...\n# U+FDD0 is disallowed; %ef%b7%90 is U+FDD0\nhttp://\\ufdd0zyx.com\n\n# ...This is the same as previous but escaped.\nhttp://%ef%b7%90zyx.com\n\n# Test name prepping, fullwidth input should be converted to ASCII and NOT\n# IDN-ized. This is \"Go\" in fullwidth UTF-8/UTF-16.\nhttp://\\uff27\\uff4f.com  s:http p:/ h:go.com\n\n# URL spec forbids the following.\n# https://www.w3.org/Bugs/Public/show_bug.cgi?id=24257\nhttp://\\uff05\\uff14\\uff11.com\nhttp://%ef%bc%85%ef%bc%94%ef%bc%91.com\n\n# ...%00 in fullwidth should fail (also as escaped UTF-8 input)\nhttp://\\uff05\\uff10\\uff10.com\nhttp://%ef%bc%85%ef%bc%90%ef%bc%90.com\n\n# Basic IDN support, UTF-8 and UTF-16 input should be converted to IDN\nhttp://\\u4f60\\u597d\\u4f60\\u597d  s:http p:/ h:xn--6qqa088eba\n\n# Invalid escaped characters should fail and the percents should be\n# escaped. https://www.w3.org/Bugs/Public/show_bug.cgi?id=24191\nhttp://%zz%66%a.com\n\n# If we get an invalid character that has been escaped.\nhttp://%25\nhttp://hello%00\n\n# Escaped numbers should be treated like IP addresses if they are.\n# No special handling for IPv4 or IPv4-like URLs\nhttp://%30%78%63%30%2e%30%32%35%30.01  s:http p:/ h:192.168.0.1\nhttp://%30%78%63%30%2e%30%32%35%30.01%2e  s:http p:/ h:0xc0.0250.01.\nhttp://192.168.0.257\n\n# Invalid escaping should trigger the regular host error handling.\nhttp://%3g%78%63%30%2e%30%32%35%30%2E.01\n\n# Something that isn't exactly an IP should get treated as a host and\n# spaces escaped.\nhttp://192.168.0.1\\shello\n\n# Fullwidth and escaped UTF-8 fullwidth should still be treated as IP.\n# These are \"0Xc0.0250.01\" in fullwidth.\nhttp://\\uff10\\uff38\\uff43\\uff10\\uff0e\\uff10\\uff12\\uff15\\uff10\\uff0e\\uff10\\uff11  s:http p:/ h:192.168.0.1\n\n# Broken IPv6\nhttp://[google.com]\n\n# Misc Unicode\nhttp://foo:\\uD83D\\uDCA9@example.com/bar  s:http h:example.com p:/bar u:foo pass:%F0%9F%92%A9\n\n# resolving a relative reference against an unknown scheme results in an error\nx test:test\n\n"
  },
  {
    "path": "okhttp/src/main/resources/META-INF/native-image/okhttp/okhttp/native-image.properties",
    "content": "Args = -H:+AddAllCharsets --enable-http --enable-https --features=okhttp3.internal.graal.OkHttpFeature --report-unsupported-elements-at-runtime\n"
  },
  {
    "path": "okhttp-bom/build.gradle.kts",
    "content": "plugins {\n  id(\"okhttp.base-conventions\")\n  id(\"okhttp.publish-conventions\")\n  id(\"java-platform\")\n}\n\ndependencies {\n  constraints {\n    project.rootProject.subprojects.forEach { subproject ->\n      if (subproject.name != \"okhttp-bom\") {\n        api(subproject)\n      }\n    }\n    api(\"com.squareup.okhttp3:okhttp-jvm:${project.version}\")\n    api(\"com.squareup.okhttp3:okhttp-android:${project.version}\")\n  }\n}\n\npublishing {\n  publications.create<MavenPublication>(\"maven\") {\n    from(project.components[\"javaPlatform\"])\n  }\n}\n"
  },
  {
    "path": "okhttp-brotli/README.md",
    "content": "OkHttp Brotli Implementation\n============================\n\nThis module is an implementation of [Brotli][1] compression.\nIt enables Brotli support in addition to tranparent Gzip support,\nprovided Accept-Encoding is not set previously.  Modern web servers\nmust choose to return Brotli responses.  n.b. It is not used for\nsending requests.\n\n```java\nOkHttpClient client = new OkHttpClient.Builder()\n  .addInterceptor(BrotliInterceptor.INSTANCE)\n  .build();\n```\n\n```kotlin\nimplementation(\"com.squareup.okhttp3:okhttp-brotli:5.3.0\")\n```\n\n [1]: https://github.com/google/brotli\n"
  },
  {
    "path": "okhttp-brotli/api/okhttp-brotli.api",
    "content": "public final class okhttp3/brotli/Brotli : okhttp3/CompressionInterceptor$DecompressionAlgorithm {\n\tpublic static final field INSTANCE Lokhttp3/brotli/Brotli;\n\tpublic fun decompress (Lokio/BufferedSource;)Lokio/Source;\n\tpublic fun getEncoding ()Ljava/lang/String;\n}\n\npublic final class okhttp3/brotli/BrotliInterceptor : okhttp3/CompressionInterceptor {\n\tpublic static final field INSTANCE Lokhttp3/brotli/BrotliInterceptor;\n}\n\n"
  },
  {
    "path": "okhttp-brotli/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Export-Package: okhttp3.brotli\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.brotli\",\n)\n\nproject.applyJavaModules(\"okhttp3.brotli\")\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n  api(libs.brotli.dec)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(libs.conscrypt.openjdk)\n  testImplementation(libs.junit)\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n  testImplementation(libs.assertk)\n}\n"
  },
  {
    "path": "okhttp-brotli/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.brotli {\n  requires okhttp3;\n  exports okhttp3.brotli;\n}\n"
  },
  {
    "path": "okhttp-brotli/src/main/kotlin/okhttp3/brotli/Brotli.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.brotli\n\nimport okhttp3.CompressionInterceptor\nimport okio.BufferedSource\nimport okio.Source\nimport okio.source\nimport org.brotli.dec.BrotliInputStream\n\nobject Brotli : CompressionInterceptor.DecompressionAlgorithm {\n  override val encoding: String get() = \"br\"\n\n  override fun decompress(compressedSource: BufferedSource): Source = BrotliInputStream(compressedSource.inputStream()).source()\n}\n"
  },
  {
    "path": "okhttp-brotli/src/main/kotlin/okhttp3/brotli/BrotliInterceptor.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.brotli\n\nimport okhttp3.CompressionInterceptor\nimport okhttp3.Gzip\n\n/**\n * Transparent Brotli response support.\n *\n * Adds Accept-Encoding: br to request and checks (and strips) for Content-Encoding: br in\n * responses.  n.b. this replaces the transparent gzip compression in BridgeInterceptor.\n */\nobject BrotliInterceptor : CompressionInterceptor(Brotli, Gzip)\n"
  },
  {
    "path": "okhttp-brotli/src/test/java/okhttp3/brotli/BrotliInterceptorTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\n  \"INVISIBLE_REFERENCE\",\n)\n\npackage okhttp3.brotli\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport java.io.IOException\nimport kotlin.test.assertFailsWith\nimport okhttp3.CompressionInterceptor\nimport okhttp3.Gzip\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okio.ByteString\nimport okio.ByteString.Companion.EMPTY\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport org.junit.jupiter.api.Test\n\nclass BrotliInterceptorTest {\n  val brotliInterceptor = CompressionInterceptor(Brotli, Gzip)\n\n  @Test\n  fun testUncompressBrotli() {\n    val s =\n      \"1bce00009c05ceb9f028d14e416230f718960a537b0922d2f7b6adef56532c08dff44551516690131494db\" +\n        \"6021c7e3616c82c1bc2416abb919aaa06e8d30d82cc2981c2f5c900bfb8ee29d5c03deb1c0dacff80e\" +\n        \"abe82ba64ed250a497162006824684db917963ecebe041b352a3e62d629cc97b95cac24265b175171e\" +\n        \"5cb384cd0912aeb5b5dd9555f2dd1a9b20688201\"\n\n    val response =\n      response(\"https://httpbin.org/brotli\", s.decodeHex()) {\n        header(\"Content-Encoding\", \"br\")\n      }\n\n    val uncompressed = brotliInterceptor.decompress(response)\n\n    val responseString = uncompressed.body.string()\n    assertThat(responseString).contains(\"\\\"brotli\\\": true,\")\n    assertThat(responseString).contains(\"\\\"Accept-Encoding\\\": \\\"br\\\"\")\n  }\n\n  @Test\n  fun testUncompressGzip() {\n    val s =\n      \"1f8b0800968f215d02ff558ec10e82301044ef7c45b3e75269d0c478e340e4a426e007086c4a636c9bb65e\" +\n        \"24fcbb5b484c3cec61deccecee9c3106eaa39dc3114e2cfa377296d8848f117d20369324500d03ba98\" +\n        \"d766b0a3368a0ce83d4f55581b14696c88894f31ba5e1b61bdfa79f7803eaf149a35619f29b3db0b29\" +\n        \"8abcbd54b7b6b97640c965bbfec238d9f4109ceb6edb01d66ba54d6247296441531e445970f627215b\" +\n        \"b22f1017320dd5000000\"\n\n    val response =\n      response(\"https://httpbin.org/gzip\", s.decodeHex()) {\n        header(\"Content-Encoding\", \"gzip\")\n      }\n\n    val uncompressed = brotliInterceptor.decompress(response)\n\n    val responseString = uncompressed.body.string()\n    assertThat(responseString).contains(\"\\\"gzipped\\\": true,\")\n    assertThat(responseString).contains(\"\\\"Accept-Encoding\\\": \\\"br,gzip\\\"\")\n  }\n\n  @Test\n  fun testNoUncompress() {\n    val response = response(\"https://httpbin.org/brotli\", \"XXXX\".encodeUtf8())\n\n    val same = brotliInterceptor.decompress(response)\n\n    val responseString = same.body.string()\n    assertThat(responseString).isEqualTo(\"XXXX\")\n  }\n\n  @Test\n  fun testFailsUncompress() {\n    val response =\n      response(\"https://httpbin.org/brotli\", \"bb919aaa06e8\".decodeHex()) {\n        header(\"Content-Encoding\", \"br\")\n      }\n\n    assertFailsWith<IOException> {\n      val failingResponse = brotliInterceptor.decompress(response)\n      failingResponse.body.string()\n    }.also { ioe ->\n      assertThat(ioe).hasMessage(\"Brotli stream decoding failed\")\n      assertThat(ioe.cause?.javaClass?.simpleName).isEqualTo(\"BrotliRuntimeException\")\n    }\n  }\n\n  @Test\n  fun testSkipUncompressNoContentResponse() {\n    val response =\n      response(\"https://httpbin.org/brotli\", EMPTY) {\n        header(\"Content-Encoding\", \"br\")\n        code(204)\n        message(\"NO CONTENT\")\n      }\n\n    val same = brotliInterceptor.decompress(response)\n\n    val responseString = same.body.string()\n    assertThat(responseString).isEmpty()\n  }\n\n  private fun response(\n    url: String,\n    bodyHex: ByteString,\n    fn: Response.Builder.() -> Unit = {},\n  ): Response =\n    Response\n      .Builder()\n      .body(bodyHex.toResponseBody(\"text/plain\".toMediaType()))\n      .code(200)\n      .message(\"OK\")\n      .request(Request.Builder().url(url).build())\n      .protocol(Protocol.HTTP_2)\n      .apply(fn)\n      .build()\n}\n"
  },
  {
    "path": "okhttp-brotli/src/test/java/okhttp3/brotli/BrotliTestMain.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.brotli\n\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nfun main() {\n  val client =\n    OkHttpClient\n      .Builder()\n      .addInterceptor(BrotliInterceptor)\n      .build()\n\n  sendRequest(\"https://httpbin.org/brotli\", client)\n  sendRequest(\"https://httpbin.org/gzip\", client)\n}\n\nprivate fun sendRequest(\n  url: String,\n  client: OkHttpClient,\n) {\n  val req = Request.Builder().url(url).build()\n\n  client.newCall(req).execute().use {\n    println(it.body.string())\n  }\n}\n"
  },
  {
    "path": "okhttp-coroutines/Module.md",
    "content": "# Module okhttp-coroutines\n\nOkHttp Coroutines library.\n"
  },
  {
    "path": "okhttp-coroutines/README.md",
    "content": "OkHttp Coroutines\n=================\n\nSupport for Kotlin clients using coroutines.\n\n```kotlin\nval call = client.newCall(request)\n\ncall.executeAsync().use { response ->\n  withContext(Dispatchers.IO) {\n    println(response.body?.string())\n  }\n}\n```\n\nThis is implemented using `suspendCancellableCoroutine`\nbut uses the standard Dispatcher in OkHttp. This means\nthat by default Kotlin's Dispatchers are not used.\n\nCancellation if implemented sensibly in both directions.\nCancelling a coroutine scope, will cancel the call.\nCancelling a call, will throw a CancellationException\nbut not cancel the scope if caught.\n"
  },
  {
    "path": "okhttp-coroutines/api/okhttp-coroutines.api",
    "content": "public final class okhttp3/coroutines/ExecuteAsyncKt {\n\tpublic static final fun executeAsync (Lokhttp3/Call;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;\n}\n\n"
  },
  {
    "path": "okhttp-coroutines/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Export-Package: okhttp3.coroutines\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.coroutines\",\n)\n\nproject.applyJavaModules(\"okhttp3.coroutines\")\n\ndependencies {\n  api(projects.okhttp)\n  implementation(libs.kotlinx.coroutines.core)\n  api(libs.square.okio)\n  api(libs.kotlin.stdlib)\n\n  testImplementation(libs.kotlin.test.annotations)\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n  testApi(libs.assertk)\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(libs.kotlinx.coroutines.test)\n  testImplementation(projects.mockwebserver3Junit5)\n}\n"
  },
  {
    "path": "okhttp-coroutines/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.coroutines {\n  requires okhttp3;\n  exports okhttp3.coroutines;\n}\n"
  },
  {
    "path": "okhttp-coroutines/src/main/kotlin/okhttp3/coroutines/ExecuteAsync.kt",
    "content": "/*\n * Copyright (c) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\npackage okhttp3.coroutines\n\nimport kotlin.coroutines.resumeWithException\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.Response\nimport okhttp3.internal.closeQuietly\nimport okio.IOException\n\nsuspend fun Call.executeAsync(): Response =\n  suspendCancellableCoroutine { continuation ->\n    continuation.invokeOnCancellation {\n      this.cancel()\n    }\n    this.enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          continuation.resumeWithException(e)\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          continuation.resume(response) { _, value, _ ->\n            value.closeQuietly()\n          }\n        }\n      },\n    )\n  }\n"
  },
  {
    "path": "okhttp-coroutines/src/test/kotlin/okhttp3/coroutines/ExecuteAsyncTest.kt",
    "content": "/*\n * Copyright (c) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage okhttp3.coroutines\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isTrue\nimport java.io.IOException\nimport java.util.concurrent.TimeUnit\nimport kotlin.test.assertFailsWith\nimport kotlin.time.Duration.Companion.seconds\nimport kotlinx.coroutines.CancellationException\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.TimeoutCancellationException\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.job\nimport kotlinx.coroutines.supervisorScope\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.coroutines.withContext\nimport kotlinx.coroutines.withTimeout\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.SocketEffect\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Callback\nimport okhttp3.FailingCall\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.ResponseBody\nimport okio.Buffer\nimport okio.ForwardingSource\nimport okio.buffer\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junit.jupiter.api.fail\n\nclass ExecuteAsyncTest {\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private var client = clientTestRule.newClientBuilder().build()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  val request by lazy { Request(server.url(\"/\")) }\n\n  @Test\n  fun suspendCall() {\n    runTest {\n      server.enqueue(MockResponse(body = \"abc\"))\n\n      val call = client.newCall(request)\n\n      call.executeAsync().use {\n        withContext(Dispatchers.IO) {\n          assertThat(it.body.string()).isEqualTo(\"abc\")\n        }\n      }\n    }\n  }\n\n  @Test\n  fun timeoutCall() {\n    runTest {\n      server.enqueue(\n        MockResponse\n          .Builder()\n          .bodyDelay(5, TimeUnit.SECONDS)\n          .body(\"abc\")\n          .build(),\n      )\n\n      val call = client.newCall(request)\n\n      assertFailsWith<TimeoutCancellationException> {\n        withTimeout(1.seconds) {\n          call.executeAsync().use {\n            withContext(Dispatchers.IO) {\n              it.body.string()\n            }\n            fail(\"No expected to get response\")\n          }\n        }\n      }\n\n      assertThat(call.isCanceled()).isTrue()\n    }\n  }\n\n  @Test\n  fun cancelledCall() {\n    runTest {\n      server.enqueue(\n        MockResponse\n          .Builder()\n          .bodyDelay(5, TimeUnit.SECONDS)\n          .body(\"abc\")\n          .build(),\n      )\n\n      val call = client.newCall(request)\n\n      assertFailsWith<IOException> {\n        call.executeAsync().use {\n          call.cancel()\n          withContext(Dispatchers.IO) {\n            it.body.string()\n          }\n        }\n      }\n\n      assertThat(call.isCanceled()).isTrue()\n    }\n  }\n\n  @Test\n  fun failedCall() {\n    runTest {\n      server.enqueue(\n        MockResponse\n          .Builder()\n          .body(\"abc\")\n          .onResponseStart(SocketEffect.ShutdownConnection)\n          .build(),\n      )\n\n      val call = client.newCall(request)\n\n      assertFailsWith<IOException> {\n        call.executeAsync().use {\n          withContext(Dispatchers.IO) {\n            it.body.string()\n          }\n        }\n      }\n    }\n  }\n\n  @Test\n  fun responseClosedIfCoroutineCanceled() {\n    runTest {\n      val call = ClosableCall()\n\n      supervisorScope {\n        assertFailsWith<CancellationException> {\n          coroutineScope {\n            call.afterCallbackOnResponse = {\n              coroutineContext.job.cancel()\n            }\n            call.executeAsync()\n          }\n        }\n      }\n\n      assertThat(call.canceled).isTrue()\n      assertThat(call.responseClosed).isTrue()\n    }\n  }\n\n  /** A call that keeps track of whether its response body is closed. */\n  private class ClosableCall : FailingCall() {\n    private val response =\n      Response\n        .Builder()\n        .request(Request(\"https://example.com/\".toHttpUrl()))\n        .protocol(Protocol.HTTP_1_1)\n        .message(\"OK\")\n        .code(200)\n        .body(\n          object : ResponseBody() {\n            override fun contentType() = null\n\n            override fun contentLength() = -1L\n\n            override fun source() =\n              object : ForwardingSource(Buffer()) {\n                override fun close() {\n                  responseClosed = true\n                }\n              }.buffer()\n          },\n        ).build()\n\n    var responseClosed = false\n    var canceled = false\n    var afterCallbackOnResponse: () -> Unit = {}\n\n    override fun cancel() {\n      canceled = true\n    }\n\n    override fun enqueue(responseCallback: Callback) {\n      responseCallback.onResponse(this, response)\n      afterCallbackOnResponse()\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/README.md",
    "content": "OkHttp DNS over HTTPS Implementation\n====================================\n\nThis module is an implementation of [DNS over HTTPS][1] using OkHttp.\n\n### Download\n\n```kotlin\ntestImplementation(\"com.squareup.okhttp3:okhttp-dnsoverhttps:5.3.0\")\n```\n\n### Usage\n\n```\n  val appCache = Cache(File(\"cacheDir\", \"okhttpcache\"), 10 * 1024 * 1024)\n  val bootstrapClient = OkHttpClient.Builder().cache(appCache).build()\n\n  val dns = DnsOverHttps.Builder().client(bootstrapClient)\n    .url(\"https://dns.google/dns-query\".toHttpUrl())\n    .bootstrapDnsHosts(InetAddress.getByName(\"8.8.4.4\"), InetAddress.getByName(\"8.8.8.8\"))\n    .build()\n\n  val client = bootstrapClient.newBuilder().dns(dns).build()\n```\n\n\n[1]: https://en.wikipedia.org/wiki/DNS_over_HTTPS\n"
  },
  {
    "path": "okhttp-dnsoverhttps/api/okhttp-dnsoverhttps.api",
    "content": "public final class okhttp3/dnsoverhttps/DnsOverHttps : okhttp3/Dns {\n\tpublic static final field Companion Lokhttp3/dnsoverhttps/DnsOverHttps$Companion;\n\tpublic static final field MAX_RESPONSE_SIZE I\n\tpublic final fun client ()Lokhttp3/OkHttpClient;\n\tpublic final fun includeIPv6 ()Z\n\tpublic fun lookup (Ljava/lang/String;)Ljava/util/List;\n\tpublic final fun post ()Z\n\tpublic final fun resolvePrivateAddresses ()Z\n\tpublic final fun resolvePublicAddresses ()Z\n\tpublic final fun url ()Lokhttp3/HttpUrl;\n}\n\npublic final class okhttp3/dnsoverhttps/DnsOverHttps$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun bootstrapDnsHosts (Ljava/util/List;)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n\tpublic final fun bootstrapDnsHosts ([Ljava/net/InetAddress;)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n\tpublic final fun build ()Lokhttp3/dnsoverhttps/DnsOverHttps;\n\tpublic final fun client (Lokhttp3/OkHttpClient;)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n\tpublic final fun includeIPv6 (Z)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n\tpublic final fun post (Z)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n\tpublic final fun resolvePrivateAddresses (Z)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n\tpublic final fun resolvePublicAddresses (Z)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n\tpublic final fun systemDns (Lokhttp3/Dns;)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n\tpublic final fun url (Lokhttp3/HttpUrl;)Lokhttp3/dnsoverhttps/DnsOverHttps$Builder;\n}\n\npublic final class okhttp3/dnsoverhttps/DnsOverHttps$Companion {\n\tpublic final fun getDNS_MESSAGE ()Lokhttp3/MediaType;\n}\n\n"
  },
  {
    "path": "okhttp-dnsoverhttps/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Export-Package: okhttp3.dnsoverhttps\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.dnsoverhttps\",\n)\n\nproject.applyJavaModules(\"okhttp3.dnsoverhttps\")\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(projects.mockwebserver)\n  testImplementation(projects.mockwebserver3Junit5)\n  testImplementation(libs.square.okio.fakefilesystem)\n  testImplementation(libs.conscrypt.openjdk)\n  testImplementation(libs.junit)\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.dnsoverhttps {\n  requires okhttp3;\n  exports okhttp3.dnsoverhttps;\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/src/main/kotlin/okhttp3/dnsoverhttps/BootstrapDns.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.dnsoverhttps\n\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport okhttp3.Dns\n\n/**\n * Internal Bootstrap DNS implementation for handling initial connection to DNS over HTTPS server.\n *\n * Returns hardcoded results for the known host.\n */\ninternal class BootstrapDns(\n  private val dnsHostname: String,\n  private val dnsServers: List<InetAddress>,\n) : Dns {\n  @Throws(UnknownHostException::class)\n  override fun lookup(hostname: String): List<InetAddress> {\n    if (this.dnsHostname != hostname) {\n      throw UnknownHostException(\n        \"BootstrapDns called for $hostname instead of $dnsHostname\",\n      )\n    }\n\n    return dnsServers\n  }\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/src/main/kotlin/okhttp3/dnsoverhttps/DnsOverHttps.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.dnsoverhttps\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport java.util.concurrent.CountDownLatch\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.Dns\nimport okhttp3.HttpUrl\nimport okhttp3.MediaType\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.Response\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.publicsuffix.PublicSuffixDatabase\n\n/**\n * [DNS over HTTPS implementation][doh_spec].\n *\n * > A DNS API client encodes a single DNS query into an HTTP request\n * > using either the HTTP GET or POST method and the other requirements\n * > of this section.  The DNS API server defines the URI used by the\n * > request through the use of a URI Template.\n *\n * [doh_spec]: https://tools.ietf.org/html/draft-ietf-doh-dns-over-https-13\n */\nclass DnsOverHttps internal constructor(\n  @get:JvmName(\"client\") val client: OkHttpClient,\n  @get:JvmName(\"url\") val url: HttpUrl,\n  @get:JvmName(\"includeIPv6\") val includeIPv6: Boolean,\n  @get:JvmName(\"post\") val post: Boolean,\n  @get:JvmName(\"resolvePrivateAddresses\") val resolvePrivateAddresses: Boolean,\n  @get:JvmName(\"resolvePublicAddresses\") val resolvePublicAddresses: Boolean,\n) : Dns {\n  @Throws(UnknownHostException::class)\n  override fun lookup(hostname: String): List<InetAddress> {\n    if (!resolvePrivateAddresses || !resolvePublicAddresses) {\n      val privateHost = isPrivateHost(hostname)\n\n      if (privateHost && !resolvePrivateAddresses) {\n        throw UnknownHostException(\"private hosts not resolved\")\n      }\n\n      if (!privateHost && !resolvePublicAddresses) {\n        throw UnknownHostException(\"public hosts not resolved\")\n      }\n    }\n\n    return lookupHttps(hostname)\n  }\n\n  @Throws(UnknownHostException::class)\n  private fun lookupHttps(hostname: String): List<InetAddress> {\n    val networkRequests =\n      buildList {\n        add(client.newCall(buildRequest(hostname, DnsRecordCodec.TYPE_A)))\n\n        if (includeIPv6) {\n          add(client.newCall(buildRequest(hostname, DnsRecordCodec.TYPE_AAAA)))\n        }\n      }\n\n    val failures = ArrayList<Exception>(2)\n    val results = ArrayList<InetAddress>(5)\n    executeRequests(hostname, networkRequests, results, failures)\n\n    return results.ifEmpty {\n      throwBestFailure(hostname, failures)\n    }\n  }\n\n  private fun executeRequests(\n    hostname: String,\n    networkRequests: List<Call>,\n    responses: MutableList<InetAddress>,\n    failures: MutableList<Exception>,\n  ) {\n    val latch = CountDownLatch(networkRequests.size)\n\n    for (call in networkRequests) {\n      call.enqueue(\n        object : Callback {\n          override fun onFailure(\n            call: Call,\n            e: IOException,\n          ) {\n            synchronized(failures) {\n              failures.add(e)\n            }\n            latch.countDown()\n          }\n\n          override fun onResponse(\n            call: Call,\n            response: Response,\n          ) {\n            processResponse(response, hostname, responses, failures)\n            latch.countDown()\n          }\n        },\n      )\n    }\n\n    try {\n      latch.await()\n    } catch (e: InterruptedException) {\n      failures.add(e)\n    }\n  }\n\n  private fun processResponse(\n    response: Response,\n    hostname: String,\n    results: MutableList<InetAddress>,\n    failures: MutableList<Exception>,\n  ) {\n    try {\n      val addresses = readResponse(hostname, response)\n      synchronized(results) {\n        results.addAll(addresses)\n      }\n    } catch (e: Exception) {\n      synchronized(failures) {\n        failures.add(e)\n      }\n    }\n  }\n\n  @Throws(UnknownHostException::class)\n  private fun throwBestFailure(\n    hostname: String,\n    failures: List<Exception>,\n  ): List<InetAddress> {\n    if (failures.isEmpty()) {\n      throw UnknownHostException(hostname)\n    }\n\n    val failure = failures[0]\n\n    if (failure is UnknownHostException) {\n      throw failure\n    }\n\n    val unknownHostException = UnknownHostException(hostname)\n    unknownHostException.initCause(failure)\n\n    for (i in 1 until failures.size) {\n      unknownHostException.addSuppressed(failures[i])\n    }\n\n    throw unknownHostException\n  }\n\n  @Throws(Exception::class)\n  private fun readResponse(\n    hostname: String,\n    response: Response,\n  ): List<InetAddress> {\n    if (response.cacheResponse == null && response.protocol !== Protocol.HTTP_2 && response.protocol !== Protocol.QUIC) {\n      Platform.get().log(\"Incorrect protocol: ${response.protocol}\", Platform.WARN)\n    }\n\n    response.use {\n      if (!response.isSuccessful) {\n        throw IOException(\"response: \" + response.code + \" \" + response.message)\n      }\n\n      val body = response.body\n\n      if (body.contentLength() > MAX_RESPONSE_SIZE) {\n        throw IOException(\n          \"response size exceeds limit ($MAX_RESPONSE_SIZE bytes): ${body.contentLength()} bytes\",\n        )\n      }\n\n      val responseBytes = body.source().readByteString()\n\n      return DnsRecordCodec.decodeAnswers(hostname, responseBytes)\n    }\n  }\n\n  private fun buildRequest(\n    hostname: String,\n    type: Int,\n  ): Request =\n    Request\n      .Builder()\n      .header(\"Accept\", DNS_MESSAGE.toString())\n      .apply {\n        val query = DnsRecordCodec.encodeQuery(hostname, type)\n\n        val dnsUrl: HttpUrl = this@DnsOverHttps.url\n        if (post) {\n          url(dnsUrl)\n            .cacheUrlOverride(\n              dnsUrl\n                .newBuilder()\n                .addQueryParameter(\"hostname\", hostname)\n                .build(),\n            ).post(query.toRequestBody(DNS_MESSAGE))\n        } else {\n          val encoded = query.base64Url().replace(\"=\", \"\")\n          val requestUrl = dnsUrl.newBuilder().addQueryParameter(\"dns\", encoded).build()\n\n          url(requestUrl)\n        }\n      }.build()\n\n  class Builder {\n    internal var client: OkHttpClient? = null\n    internal var url: HttpUrl? = null\n    internal var includeIPv6 = true\n    internal var post = false\n    internal var systemDns = Dns.SYSTEM\n    internal var bootstrapDnsHosts: List<InetAddress>? = null\n    internal var resolvePrivateAddresses = false\n    internal var resolvePublicAddresses = true\n\n    fun build(): DnsOverHttps {\n      val client = this.client ?: throw NullPointerException(\"client not set\")\n      return DnsOverHttps(\n        client.newBuilder().dns(buildBootstrapClient(this)).build(),\n        checkNotNull(url) { \"url not set\" },\n        includeIPv6,\n        post,\n        resolvePrivateAddresses,\n        resolvePublicAddresses,\n      )\n    }\n\n    fun client(client: OkHttpClient) =\n      apply {\n        this.client = client\n      }\n\n    fun url(url: HttpUrl) =\n      apply {\n        this.url = url\n      }\n\n    fun includeIPv6(includeIPv6: Boolean) =\n      apply {\n        this.includeIPv6 = includeIPv6\n      }\n\n    fun post(post: Boolean) =\n      apply {\n        this.post = post\n      }\n\n    fun resolvePrivateAddresses(resolvePrivateAddresses: Boolean) =\n      apply {\n        this.resolvePrivateAddresses = resolvePrivateAddresses\n      }\n\n    fun resolvePublicAddresses(resolvePublicAddresses: Boolean) =\n      apply {\n        this.resolvePublicAddresses = resolvePublicAddresses\n      }\n\n    fun bootstrapDnsHosts(bootstrapDnsHosts: List<InetAddress>?) =\n      apply {\n        this.bootstrapDnsHosts = bootstrapDnsHosts\n      }\n\n    fun bootstrapDnsHosts(vararg bootstrapDnsHosts: InetAddress): Builder = bootstrapDnsHosts(bootstrapDnsHosts.toList())\n\n    fun systemDns(systemDns: Dns) =\n      apply {\n        this.systemDns = systemDns\n      }\n  }\n\n  companion object {\n    val DNS_MESSAGE: MediaType = \"application/dns-message\".toMediaType()\n    const val MAX_RESPONSE_SIZE = 64 * 1024\n\n    private fun buildBootstrapClient(builder: Builder): Dns {\n      val hosts = builder.bootstrapDnsHosts\n\n      return if (hosts != null) {\n        BootstrapDns(builder.url!!.host, hosts)\n      } else {\n        builder.systemDns\n      }\n    }\n\n    internal fun isPrivateHost(host: String): Boolean = PublicSuffixDatabase.get().getEffectiveTldPlusOne(host) == null\n  }\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/src/main/kotlin/okhttp3/dnsoverhttps/DnsRecordCodec.kt",
    "content": "/*\n * Copyright 2016 The Netty Project\n *\n * The Netty Project licenses this file to you under the Apache License, version 2.0 (the\n * \"License\"); you may not use this file except in compliance with the License. You may obtain a\n * copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage okhttp3.dnsoverhttps\n\nimport java.io.EOFException\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport okio.Buffer\nimport okio.ByteString\nimport okio.utf8Size\n\n/**\n * Trivial Dns Encoder/Decoder, basically ripped from Netty full implementation.\n */\ninternal object DnsRecordCodec {\n  private const val SERVFAIL = 2\n  private const val NXDOMAIN = 3\n  const val TYPE_A = 0x0001\n  const val TYPE_AAAA = 0x001c\n  private const val TYPE_PTR = 0x000c\n  private val ASCII = Charsets.US_ASCII\n\n  fun encodeQuery(\n    host: String,\n    type: Int,\n  ): ByteString =\n    Buffer()\n      .apply {\n        writeShort(0) // query id\n        writeShort(256) // flags with recursion\n        writeShort(1) // question count\n        writeShort(0) // answerCount\n        writeShort(0) // authorityResourceCount\n        writeShort(0) // additional\n\n        val nameBuf = Buffer()\n        val labels = host.split('.').dropLastWhile { it.isEmpty() }\n        for (label in labels) {\n          val utf8ByteCount = label.utf8Size()\n          require(utf8ByteCount == label.length.toLong()) { \"non-ascii hostname: $host\" }\n          nameBuf.writeByte(utf8ByteCount.toInt())\n          nameBuf.writeUtf8(label)\n        }\n        nameBuf.writeByte(0) // end\n\n        nameBuf.copyTo(this, 0, nameBuf.size)\n        writeShort(type)\n        writeShort(1) // CLASS_IN\n      }.readByteString()\n\n  @Throws(Exception::class)\n  fun decodeAnswers(\n    hostname: String,\n    byteString: ByteString,\n  ): List<InetAddress> {\n    val result = mutableListOf<InetAddress>()\n\n    val buf = Buffer()\n    buf.write(byteString)\n    buf.readShort() // query id\n\n    val flags = buf.readShort().toInt() and 0xffff\n    require(flags shr 15 != 0) { \"not a response\" }\n\n    val responseCode = flags and 0xf\n\n    if (responseCode == NXDOMAIN) {\n      throw UnknownHostException(\"$hostname: NXDOMAIN\")\n    } else if (responseCode == SERVFAIL) {\n      throw UnknownHostException(\"$hostname: SERVFAIL\")\n    }\n\n    val questionCount = buf.readShort().toInt() and 0xffff\n    val answerCount = buf.readShort().toInt() and 0xffff\n    buf.readShort() // authority record count\n    buf.readShort() // additional record count\n\n    for (i in 0 until questionCount) {\n      skipName(buf) // name\n      buf.readShort() // type\n      buf.readShort() // class\n    }\n\n    for (i in 0 until answerCount) {\n      skipName(buf) // name\n\n      val type = buf.readShort().toInt() and 0xffff\n      buf.readShort() // class\n      @Suppress(\"UNUSED_VARIABLE\")\n      val ttl = buf.readInt().toLong() and 0xffffffffL // ttl\n      val length = buf.readShort().toInt() and 0xffff\n\n      if (type == TYPE_A || type == TYPE_AAAA) {\n        val bytes = ByteArray(length)\n        buf.read(bytes)\n        result.add(InetAddress.getByAddress(bytes))\n      } else {\n        buf.skip(length.toLong())\n      }\n    }\n\n    return result\n  }\n\n  @Throws(EOFException::class)\n  private fun skipName(source: Buffer) {\n    // 0 - 63 bytes\n    var length = source.readByte().toInt()\n\n    if (length < 0) {\n      // compressed name pointer, first two bits are 1\n      // drop second byte of compression offset\n      source.skip(1)\n    } else {\n      while (length > 0) {\n        // skip each part of the domain name\n        source.skip(length.toLong())\n        length = source.readByte().toInt()\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsOverHttpsTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.dnsoverhttps\n\nimport assertk.assertThat\nimport assertk.assertions.contains\nimport assertk.assertions.containsExactly\nimport assertk.assertions.containsExactlyInAnyOrder\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.isNull\nimport java.io.EOFException\nimport java.io.File\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport java.util.concurrent.TimeUnit\nimport kotlin.reflect.KClass\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Cache\nimport okhttp3.CallEvent\nimport okhttp3.CallEvent.CacheHit\nimport okhttp3.CallEvent.CacheMiss\nimport okhttp3.Dns\nimport okhttp3.EventRecorder\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.testing.PlatformRule\nimport okio.Buffer\nimport okio.ByteString.Companion.decodeHex\nimport okio.Path.Companion.toPath\nimport okio.fakefilesystem.FakeFileSystem\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Tag(\"Slowish\")\nclass DnsOverHttpsTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private lateinit var dns: Dns\n  private val cacheFs = FakeFileSystem()\n  private val eventRecorder = EventRecorder()\n  private val bootstrapClient =\n    OkHttpClient\n      .Builder()\n      .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))\n      .eventListener(eventRecorder.eventListener)\n      .build()\n\n  @BeforeEach\n  fun setUp() {\n    server.protocols = bootstrapClient.protocols\n    dns = buildLocalhost(bootstrapClient, false)\n  }\n\n  @Test\n  fun getOne() {\n    server.enqueue(\n      dnsResponse(\n        \"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010\" +\n          \"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000\" +\n          \"0003b00049df00112\",\n      ),\n    )\n    val result = dns.lookup(\"google.com\")\n    assertThat(result).isEqualTo(listOf(address(\"157.240.1.18\")))\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"GET\")\n    assertThat(recordedRequest.url.encodedQuery)\n      .isEqualTo(\"ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ\")\n  }\n\n  @Test\n  fun getIpv6() {\n    server.enqueue(\n      dnsResponse(\n        \"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c0005000\" +\n          \"100000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c0420001000\" +\n          \"10000003b00049df00112\",\n      ),\n    )\n    server.enqueue(\n      dnsResponse(\n        \"0000818000010003000000000567726170680866616365626f6f6b03636f6d00001c0001c00c0005000\" +\n          \"100000a1b000603617069c012c0300005000100000b1f000c04737461720463313072c012c042001c000\" +\n          \"10000003b00102a032880f0290011faceb00c00000002\",\n      ),\n    )\n    dns = buildLocalhost(bootstrapClient, true)\n    val result = dns.lookup(\"google.com\")\n    assertThat(result.size).isEqualTo(2)\n    assertThat(result).contains(address(\"157.240.1.18\"))\n    assertThat(result).contains(address(\"2a03:2880:f029:11:face:b00c:0:2\"))\n    val request1 = server.takeRequest()\n    assertThat(request1.method).isEqualTo(\"GET\")\n    val request2 = server.takeRequest()\n    assertThat(request2.method).isEqualTo(\"GET\")\n    assertThat(listOf(request1.url.encodedQuery, request2.url.encodedQuery))\n      .containsExactlyInAnyOrder(\n        \"ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ\",\n        \"ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AABwAAQ\",\n      )\n  }\n\n  @Test\n  fun failure() {\n    server.enqueue(\n      dnsResponse(\n        \"0000818300010000000100000e7364666c6b686673646c6b6a64660265650000010001c01b00060001000\" +\n          \"007070038026e7303746c64c01b0a686f73746d61737465720d6565737469696e7465726e6574c01b5adb1\" +\n          \"2c100000e10000003840012750000000e10\",\n      ),\n    )\n    try {\n      dns.lookup(\"google.com\")\n      fail<Any>()\n    } catch (uhe: UnknownHostException) {\n      assertThat(uhe.message).isEqualTo(\"google.com: NXDOMAIN\")\n    }\n    val recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"GET\")\n    assertThat(recordedRequest.url.encodedQuery)\n      .isEqualTo(\"ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ\")\n  }\n\n  @Test\n  fun failOnExcessiveResponse() {\n    val array = CharArray(128 * 1024 + 2) { '0' }\n    server.enqueue(dnsResponse(String(array)))\n    try {\n      dns.lookup(\"google.com\")\n      fail<Any>()\n    } catch (ioe: IOException) {\n      assertThat(ioe.message).isEqualTo(\"google.com\")\n      val cause = ioe.cause!!\n      assertThat(cause).isInstanceOf<IOException>()\n      assertThat(cause).hasMessage(\"response size exceeds limit (65536 bytes): 65537 bytes\")\n    }\n  }\n\n  @Test\n  fun failOnBadResponse() {\n    server.enqueue(dnsResponse(\"00\"))\n    try {\n      dns.lookup(\"google.com\")\n      fail<Any>()\n    } catch (ioe: IOException) {\n      assertThat(ioe).hasMessage(\"google.com\")\n      assertThat(ioe.cause!!).isInstanceOf<EOFException>()\n    }\n  }\n\n  // TODO GET preferred order - with tests to confirm this\n  // 1. successful fresh cached GET response\n  // 2. unsuccessful (404, 500) fresh cached GET response\n  // 3. successful network response\n  // 4. successful stale cached GET response\n  // 5. unsuccessful response\n  @Test\n  fun usesCache() {\n    val cache = Cache(cacheFs, \"cache\".toPath(), (100 * 1024).toLong())\n    val cachedClient = bootstrapClient.newBuilder().cache(cache).build()\n    val cachedDns = buildLocalhost(cachedClient, false)\n\n    repeat(2) {\n      server.enqueue(\n        dnsResponse(\n          \"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010\" +\n            \"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000\" +\n            \"0003b00049df00112\",\n        ).newBuilder()\n          .setHeader(\"cache-control\", \"private, max-age=298\")\n          .build(),\n      )\n    }\n\n    var result = cachedDns.lookup(\"google.com\")\n    assertThat(result).containsExactly(address(\"157.240.1.18\"))\n    var recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"GET\")\n    assertThat(recordedRequest.url.encodedQuery)\n      .isEqualTo(\"ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ\")\n\n    assertThat(cacheEvents()).containsExactly(CacheMiss::class)\n\n    result = cachedDns.lookup(\"google.com\")\n    assertThat(server.takeRequest(1, TimeUnit.MILLISECONDS)).isNull()\n    assertThat(result).isEqualTo(listOf(address(\"157.240.1.18\")))\n\n    assertThat(cacheEvents()).containsExactly(CacheHit::class)\n\n    result = cachedDns.lookup(\"www.google.com\")\n    assertThat(result).containsExactly(address(\"157.240.1.18\"))\n    recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"GET\")\n    assertThat(recordedRequest.url.encodedQuery)\n      .isEqualTo(\"ct&dns=AAABAAABAAAAAAAAA3d3dwZnb29nbGUDY29tAAABAAE\")\n\n    assertThat(cacheEvents()).containsExactly(CacheMiss::class)\n  }\n\n  @Test\n  fun usesCacheEvenForPost() {\n    val cache = Cache(cacheFs, \"cache\".toPath(), (100 * 1024).toLong())\n    val cachedClient = bootstrapClient.newBuilder().cache(cache).build()\n    val cachedDns = buildLocalhost(cachedClient, false, post = true)\n    repeat(2) {\n      server.enqueue(\n        dnsResponse(\n          \"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010\" +\n            \"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000\" +\n            \"0003b00049df00112\",\n        ).newBuilder()\n          .setHeader(\"cache-control\", \"private, max-age=298\")\n          .build(),\n      )\n    }\n\n    var result = cachedDns.lookup(\"google.com\")\n    assertThat(result).containsExactly(address(\"157.240.1.18\"))\n    var recordedRequest = server.takeRequest()\n    assertThat(recordedRequest.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest.url.encodedQuery)\n      .isEqualTo(\"ct\")\n\n    assertThat(cacheEvents()).containsExactly(CacheMiss::class)\n\n    result = cachedDns.lookup(\"google.com\")\n    assertThat(server.takeRequest(0, TimeUnit.MILLISECONDS)).isNull()\n    assertThat(result).isEqualTo(listOf(address(\"157.240.1.18\")))\n\n    assertThat(cacheEvents()).containsExactly(CacheHit::class)\n\n    result = cachedDns.lookup(\"www.google.com\")\n    assertThat(result).containsExactly(address(\"157.240.1.18\"))\n    recordedRequest = server.takeRequest(0, TimeUnit.MILLISECONDS)!!\n    assertThat(recordedRequest.method).isEqualTo(\"POST\")\n    assertThat(recordedRequest.url.encodedQuery)\n      .isEqualTo(\"ct\")\n\n    assertThat(cacheEvents()).containsExactly(CacheMiss::class)\n  }\n\n  @Test\n  fun usesCacheOnlyIfFresh() {\n    val cache = Cache(File(\"./target/DnsOverHttpsTest.cache\"), 100 * 1024L)\n    val cachedClient = bootstrapClient.newBuilder().cache(cache).build()\n    val cachedDns = buildLocalhost(cachedClient, false)\n    server.enqueue(\n      dnsResponse(\n        \"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010\" +\n          \"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000\" +\n          \"0003b00049df00112\",\n      ).newBuilder()\n        .setHeader(\"cache-control\", \"max-age=1\")\n        .build(),\n    )\n    var result = cachedDns.lookup(\"google.com\")\n    assertThat(result).containsExactly(address(\"157.240.1.18\"))\n    var recordedRequest = server.takeRequest(0, TimeUnit.SECONDS)\n    assertThat(recordedRequest!!.method).isEqualTo(\"GET\")\n    assertThat(recordedRequest.url.encodedQuery).isEqualTo(\n      \"ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ\",\n    )\n\n    assertThat(cacheEvents()).containsExactly(CacheMiss::class)\n\n    Thread.sleep(2000)\n    server.enqueue(\n      dnsResponse(\n        \"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010\" +\n          \"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000\" +\n          \"0003b00049df00112\",\n      ).newBuilder()\n        .setHeader(\"cache-control\", \"max-age=1\")\n        .build(),\n    )\n    result = cachedDns.lookup(\"google.com\")\n    assertThat(result).isEqualTo(listOf(address(\"157.240.1.18\")))\n    recordedRequest = server.takeRequest(0, TimeUnit.SECONDS)\n    assertThat(recordedRequest!!.method).isEqualTo(\"GET\")\n    assertThat(recordedRequest.url.encodedQuery)\n      .isEqualTo(\"ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ\")\n\n    assertThat(cacheEvents()).containsExactly(CacheMiss::class)\n  }\n\n  private fun cacheEvents(): List<KClass<out CallEvent>> =\n    eventRecorder\n      .recordedEventTypes()\n      .filter { \"Cache\" in it.simpleName!! }\n      .also { eventRecorder.clearAllEvents() }\n\n  private fun dnsResponse(s: String): MockResponse =\n    MockResponse\n      .Builder()\n      .body(Buffer().write(s.decodeHex()))\n      .addHeader(\"content-type\", \"application/dns-message\")\n      .addHeader(\"content-length\", s.length / 2)\n      .build()\n\n  private fun buildLocalhost(\n    bootstrapClient: OkHttpClient,\n    includeIPv6: Boolean,\n    post: Boolean = false,\n  ): DnsOverHttps {\n    val url = server.url(\"/lookup?ct\")\n    return DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .includeIPv6(includeIPv6)\n      .resolvePrivateAddresses(true)\n      .url(url)\n      .post(post)\n      .build()\n  }\n\n  companion object {\n    private fun address(host: String) = InetAddress.getByName(host)\n  }\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsRecordCodecTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.dnsoverhttps\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport kotlin.test.assertFailsWith\nimport okhttp3.dnsoverhttps.DnsRecordCodec.TYPE_A\nimport okhttp3.dnsoverhttps.DnsRecordCodec.TYPE_AAAA\nimport okhttp3.dnsoverhttps.DnsRecordCodec.decodeAnswers\nimport okio.ByteString.Companion.decodeHex\nimport org.junit.jupiter.api.Test\n\nclass DnsRecordCodecTest {\n  @Test\n  fun testGoogleDotComEncoding() {\n    val encoded = encodeQuery(\"google.com\", TYPE_A)\n    assertThat(encoded).isEqualTo(\"AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ\")\n  }\n\n  private fun encodeQuery(\n    host: String,\n    type: Int,\n  ): String = DnsRecordCodec.encodeQuery(host, type).base64Url().replace(\"=\", \"\")\n\n  @Test\n  fun testGoogleDotComEncodingWithIPv6() {\n    val encoded = encodeQuery(\"google.com\", TYPE_AAAA)\n    assertThat(encoded).isEqualTo(\"AAABAAABAAAAAAAABmdvb2dsZQNjb20AABwAAQ\")\n  }\n\n  @Test\n  fun testGoogleDotComDecodingFromCloudflare() {\n    val encoded =\n      decodeAnswers(\n        hostname = \"test.com\",\n        byteString =\n          (\n            \"00008180000100010000000006676f6f676c6503636f6d0000010001c00c0001000100000043\" +\n              \"0004d83ad54e\"\n          ).decodeHex(),\n      )\n    assertThat(encoded).containsExactly(InetAddress.getByName(\"216.58.213.78\"))\n  }\n\n  @Test\n  fun testGoogleDotComDecodingFromGoogle() {\n    val decoded =\n      decodeAnswers(\n        hostname = \"test.com\",\n        byteString =\n          (\n            \"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c\" +\n              \"0005000100000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c0420001\" +\n              \"00010000003b00049df00112\"\n          ).decodeHex(),\n      )\n    assertThat(decoded).containsExactly(InetAddress.getByName(\"157.240.1.18\"))\n  }\n\n  @Test\n  fun testGoogleDotComDecodingFromGoogleIPv6() {\n    val decoded =\n      decodeAnswers(\n        hostname = \"test.com\",\n        byteString =\n          (\n            \"0000818000010003000000000567726170680866616365626f6f6b03636f6d00001c0001c00c\" +\n              \"0005000100000a1b000603617069c012c0300005000100000b1f000c04737461720463313072c012c042001c\" +\n              \"00010000003b00102a032880f0290011faceb00c00000002\"\n          ).decodeHex(),\n      )\n    assertThat(decoded)\n      .containsExactly(InetAddress.getByName(\"2a03:2880:f029:11:face:b00c:0:2\"))\n  }\n\n  @Test\n  fun testGoogleDotComDecodingNxdomainFailure() {\n    assertFailsWith<UnknownHostException> {\n      decodeAnswers(\n        hostname = \"sdflkhfsdlkjdf.ee\",\n        byteString =\n          (\n            \"0000818300010000000100000e7364666c6b686673646c6b6a64660265650000010001c01b\" +\n              \"00060001000007070038026e7303746c64c01b0a686f73746d61737465720d6565737469696e7465726e65\" +\n              \"74c01b5adb12c100000e10000003840012750000000e10\"\n          ).decodeHex(),\n      )\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"sdflkhfsdlkjdf.ee: NXDOMAIN\")\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DohProviders.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.dnsoverhttps\n\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\n\n/**\n * Temporary registry of known DNS over HTTPS providers.\n *\n * https://github.com/curl/curl/wiki/DNS-over-HTTPS\n */\nobject DohProviders {\n  private fun buildGoogle(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://dns.google/dns-query\".toHttpUrl())\n      .bootstrapDnsHosts(getByIp(\"8.8.4.4\"), getByIp(\"8.8.8.8\"))\n      .build()\n\n  private fun buildGooglePost(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://dns.google/dns-query\".toHttpUrl())\n      .bootstrapDnsHosts(getByIp(\"8.8.4.4\"), getByIp(\"8.8.8.8\"))\n      .post(true)\n      .build()\n\n  private fun buildCloudflareIp(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://1.1.1.1/dns-query\".toHttpUrl())\n      .includeIPv6(false)\n      .build()\n\n  private fun buildCloudflare(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://1.1.1.1/dns-query\".toHttpUrl())\n      .bootstrapDnsHosts(getByIp(\"1.1.1.1\"), getByIp(\"1.0.0.1\"))\n      .includeIPv6(false)\n      .build()\n\n  private fun buildCloudflarePost(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://cloudflare-dns.com/dns-query\".toHttpUrl())\n      .bootstrapDnsHosts(getByIp(\"1.1.1.1\"), getByIp(\"1.0.0.1\"))\n      .includeIPv6(false)\n      .post(true)\n      .build()\n\n  fun buildCleanBrowsing(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://doh.cleanbrowsing.org/doh/family-filter/\".toHttpUrl())\n      .includeIPv6(false)\n      .build()\n\n  private fun buildChantra(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://dns.dnsoverhttps.net/dns-query\".toHttpUrl())\n      .includeIPv6(false)\n      .build()\n\n  private fun buildCryptoSx(bootstrapClient: OkHttpClient): DnsOverHttps =\n    DnsOverHttps\n      .Builder()\n      .client(bootstrapClient)\n      .url(\"https://doh.crypto.sx/dns-query\".toHttpUrl())\n      .includeIPv6(false)\n      .build()\n\n  @JvmStatic\n  fun providers(\n    client: OkHttpClient,\n    http2Only: Boolean,\n    workingOnly: Boolean,\n    getOnly: Boolean,\n  ): List<DnsOverHttps> =\n    buildList {\n      add(buildGoogle(client))\n      if (!getOnly) {\n        add(buildGooglePost(client))\n      }\n      add(buildCloudflare(client))\n      add(buildCloudflareIp(client))\n      if (!getOnly) {\n        add(buildCloudflarePost(client))\n      }\n      if (!workingOnly) {\n        // result += buildCleanBrowsing(client); // timeouts\n        add(buildCryptoSx(client)) // 521 - server down\n      }\n      add(buildChantra(client))\n    }\n\n  private fun getByIp(host: String): InetAddress =\n    try {\n      InetAddress.getByName(host)\n    } catch (e: UnknownHostException) {\n      // unlikely\n      throw RuntimeException(e)\n    }\n}\n"
  },
  {
    "path": "okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/TestDohMain.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.dnsoverhttps\n\nimport java.io.File\nimport java.net.UnknownHostException\nimport java.security.Security\nimport okhttp3.Cache\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.dnsoverhttps.DohProviders.providers\nimport org.conscrypt.OpenSSLProvider\n\nprivate fun runBatch(\n  dnsProviders: List<DnsOverHttps>,\n  names: List<String>,\n) {\n  var time = System.currentTimeMillis()\n  for (dns in dnsProviders) {\n    println(\"Testing ${dns.url}\")\n    for (host in names) {\n      print(\"$host: \")\n      System.out.flush()\n      try {\n        val results = dns.lookup(host)\n        println(results)\n      } catch (uhe: UnknownHostException) {\n        var e: Throwable? = uhe\n        while (e != null) {\n          println(e)\n          e = e.cause\n        }\n      }\n    }\n    println()\n  }\n  time = System.currentTimeMillis() - time\n  println(\"Time: ${time.toDouble() / 1000} seconds\\n\")\n}\n\nfun main() {\n  Security.insertProviderAt(OpenSSLProvider(), 1)\n  var bootstrapClient = OkHttpClient()\n  var names = listOf(\"google.com\", \"graph.facebook.com\", \"sdflkhfsdlkjdf.ee\")\n  try {\n    println(\"uncached\\n********\\n\")\n    var dnsProviders =\n      providers(\n        client = bootstrapClient,\n        http2Only = false,\n        workingOnly = false,\n        getOnly = false,\n      )\n    runBatch(dnsProviders, names)\n    val dnsCache =\n      Cache(\n        directory = File(\"./target/TestDohMain.cache.${System.currentTimeMillis()}\"),\n        maxSize = 10L * 1024 * 1024,\n      )\n    println(\"Bad targets\\n***********\\n\")\n    val url = \"https://dns.cloudflare.com/.not-so-well-known/run-dmc-query\".toHttpUrl()\n    val badProviders =\n      listOf(\n        DnsOverHttps\n          .Builder()\n          .client(bootstrapClient)\n          .url(url)\n          .post(true)\n          .build(),\n      )\n    runBatch(badProviders, names)\n    println(\"cached first run\\n****************\\n\")\n    names = listOf(\"google.com\", \"graph.facebook.com\")\n    bootstrapClient =\n      bootstrapClient\n        .newBuilder()\n        .cache(dnsCache)\n        .build()\n    dnsProviders =\n      providers(\n        client = bootstrapClient,\n        http2Only = true,\n        workingOnly = true,\n        getOnly = true,\n      )\n    runBatch(dnsProviders, names)\n    println(\"cached second run\\n*****************\\n\")\n    dnsProviders =\n      providers(\n        client = bootstrapClient,\n        http2Only = true,\n        workingOnly = true,\n        getOnly = true,\n      )\n    runBatch(dnsProviders, names)\n  } finally {\n    bootstrapClient.connectionPool.evictAll()\n    bootstrapClient.dispatcher.executorService.shutdownNow()\n    bootstrapClient.cache?.close()\n  }\n}\n"
  },
  {
    "path": "okhttp-hpacktests/README.md",
    "content": "OkHttp HPACK tests\n==================\n\nThese tests use the [hpack-test-case][1] project to validate OkHttp's HPACK\nimplementation.  The HPACK test cases are in a separate git submodule, so to\ninitialize them, you must run:\n\n    git submodule init\n    git submodule update\n\nTODO\n----\n\n * Add maven goal to avoid manual call to git submodule init.\n * Make hpack-test-case update itself from git, and run new tests.\n * Add maven goal to generate stories and a pull request to hpack-test-case\n   to have others validate our output.\n\n[1]: https://github.com/http2jp/hpack-test-case \n"
  },
  {
    "path": "okhttp-hpacktests/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\ndependencies {\n  testImplementation(libs.square.okio)\n  testImplementation(libs.square.moshi)\n  testImplementation(libs.square.moshi.kotlin)\n  testImplementation(projects.okhttp)\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(projects.mockwebserver)\n  testImplementation(libs.junit)\n}\n"
  },
  {
    "path": "okhttp-hpacktests/src/test/java/okhttp3/internal/http2/HpackDecodeInteropTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport okhttp3.SimpleProvider\nimport okhttp3.internal.http2.hpackjson.HpackJsonUtil\nimport okhttp3.internal.http2.hpackjson.Story\nimport org.junit.jupiter.api.Assumptions.assumeFalse\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ArgumentsSource\n\nclass HpackDecodeInteropTest : HpackDecodeTestBase() {\n  @ParameterizedTest\n  @ArgumentsSource(StoriesTestProvider::class)\n  fun testGoodDecoderInterop(story: Story) {\n    assumeFalse(\n      story === Story.MISSING,\n      \"Test stories missing, checkout git submodule\",\n    )\n    testDecoder(story)\n  }\n\n  internal class StoriesTestProvider : SimpleProvider() {\n    override fun arguments(): List<Any> = createStories(HpackJsonUtil.storiesForCurrentDraft())\n  }\n}\n"
  },
  {
    "path": "okhttp-hpacktests/src/test/java/okhttp3/internal/http2/HpackDecodeTestBase.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport okhttp3.internal.http2.hpackjson.HpackJsonUtil\nimport okhttp3.internal.http2.hpackjson.Story\nimport okio.Buffer\n\n/**\n * Tests Hpack implementation using https://github.com/http2jp/hpack-test-case/\n */\nopen class HpackDecodeTestBase {\n  private val bytesIn = Buffer()\n  private val hpackReader = Hpack.Reader(bytesIn, 4096)\n\n  protected fun testDecoder(story: Story) {\n    for (testCase in story.cases) {\n      val encoded = testCase.wire ?: continue\n      bytesIn.write(encoded)\n      hpackReader.readHeaders()\n      assertSetEquals(\n        \"seqno=$testCase.seqno\",\n        testCase.headersList,\n        hpackReader.getAndResetHeaderList(),\n      )\n    }\n  }\n\n  companion object {\n    /**\n     * Reads all stories in the folders provided, asserts if no story found.\n     */\n    @JvmStatic\n    protected fun createStories(interopTests: Array<String>): List<Any> {\n      if (interopTests.isEmpty()) return listOf<Any>(Story.MISSING)\n\n      val result = mutableListOf<Any>()\n      for (interopTestName in interopTests) {\n        val stories = HpackJsonUtil.readStories(interopTestName)\n        result.addAll(stories)\n      }\n      return result\n    }\n\n    /**\n     * Checks if `expected` and `observed` are equal when viewed as a set and headers are\n     * deduped.\n     *\n     * TODO: See if duped headers should be preserved on decode and verify.\n     */\n    private fun assertSetEquals(\n      message: String,\n      expected: List<Header>,\n      observed: List<Header>,\n    ) {\n      assertThat(LinkedHashSet(observed), message)\n        .isEqualTo(LinkedHashSet(expected))\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-hpacktests/src/test/java/okhttp3/internal/http2/HpackRoundTripTest.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2\n\nimport okhttp3.SimpleProvider\nimport okhttp3.internal.http2.hpackjson.Case\nimport okhttp3.internal.http2.hpackjson.Story\nimport okio.Buffer\nimport org.junit.jupiter.api.Assumptions.assumeFalse\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ArgumentsSource\n\n/**\n * Tests for round-tripping headers through hpack.\n *\n * TODO: update hpack-test-case with the output of our encoder.\n * This test will hide complementary bugs in the encoder and decoder,\n * We should test that the encoder is producing responses that are\n */\nclass HpackRoundTripTest : HpackDecodeTestBase() {\n  internal class StoriesTestProvider : SimpleProvider() {\n    override fun arguments(): List<Any> = createStories(RAW_DATA)\n  }\n\n  private val bytesOut = Buffer()\n  private val hpackWriter = Hpack.Writer(out = bytesOut)\n\n  @ParameterizedTest\n  @ArgumentsSource(StoriesTestProvider::class)\n  fun testRoundTrip(story: Story) {\n    assumeFalse(\n      story === Story.MISSING,\n      \"Test stories missing, checkout git submodule\",\n    )\n\n    val newCases = mutableListOf<Case>()\n    for (case in story.cases) {\n      hpackWriter.writeHeaders(case.headersList)\n      newCases += case.copy(wire = bytesOut.readByteString())\n    }\n\n    testDecoder(story.copy(cases = newCases))\n  }\n\n  companion object {\n    private val RAW_DATA = arrayOf(\"raw-data\")\n  }\n}\n"
  },
  {
    "path": "okhttp-hpacktests/src/test/java/okhttp3/internal/http2/hpackjson/Case.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2.hpackjson\n\nimport okhttp3.internal.http2.Header\nimport okio.ByteString\n\n/**\n * Representation of an individual case (set of headers and wire format). There are many cases for a\n * single story.  This class is used reflectively with Moshi to parse stories.\n */\ndata class Case(\n  val seqno: Int = 0,\n  val wire: ByteString? = null,\n  val headers: List<Map<String, String>>,\n) : Cloneable {\n  val headersList: List<Header>\n    get() {\n      val result = mutableListOf<Header>()\n      for (inputHeader in headers) {\n        val (key, value) = inputHeader.entries.iterator().next()\n        result.add(Header(key, value))\n      }\n      return result\n    }\n\n  public override fun clone() = Case(seqno, this.wire, headers)\n}\n"
  },
  {
    "path": "okhttp-hpacktests/src/test/java/okhttp3/internal/http2/hpackjson/HpackJsonUtil.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2.hpackjson\n\nimport com.squareup.moshi.FromJson\nimport com.squareup.moshi.Moshi\nimport com.squareup.moshi.ToJson\nimport com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory\nimport java.io.File\nimport java.io.IOException\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeHex\nimport okio.FileSystem\nimport okio.Path\nimport okio.Path.Companion.toOkioPath\nimport okio.buffer\nimport okio.source\n\n/**\n * Utilities for reading HPACK tests.\n */\nobject HpackJsonUtil {\n  @Suppress(\"unused\")\n  private val MOSHI =\n    Moshi\n      .Builder()\n      .add(\n        object : Any() {\n          @ToJson fun byteStringToJson(byteString: ByteString) = byteString.hex()\n\n          @FromJson fun byteStringFromJson(json: String) = json.decodeHex()\n        },\n      ).add(KotlinJsonAdapterFactory())\n      .build()\n  private val STORY_JSON_ADAPTER = MOSHI.adapter(Story::class.java)\n\n  private val fileSystem = FileSystem.SYSTEM\n\n  private fun readStory(source: BufferedSource): Story = STORY_JSON_ADAPTER.fromJson(source)!!\n\n  private fun readStory(file: Path): Story {\n    fileSystem.read(file) {\n      return readStory(this)\n    }\n  }\n\n  /** Iterate through the hpack-test-case resources, only picking stories for the current draft.  */\n  fun storiesForCurrentDraft(): Array<String> {\n    val resource =\n      HpackJsonUtil::class.java.getResource(\"/hpack-test-case\")\n        ?: return arrayOf()\n\n    val testCaseDirectory = File(resource.toURI()).toOkioPath()\n    val result = mutableListOf<String>()\n    for (path in fileSystem.list(testCaseDirectory)) {\n      val story00 = path / \"story_00.json\"\n      if (!fileSystem.exists(story00)) continue\n      try {\n        readStory(story00)\n        result.add(path.name)\n      } catch (ignored: IOException) {\n        // Skip this path.\n      }\n    }\n    return result.toTypedArray<String>()\n  }\n\n  /**\n   * Reads stories named \"story_xx.json\" from the folder provided.\n   */\n  fun readStories(testFolderName: String): List<Story> {\n    val result = mutableListOf<Story>()\n    var i = 0\n    while (true) { // break after last test.\n      val storyResourceName =\n        String.format(\n          \"/hpack-test-case/%s/story_%02d.json\",\n          testFolderName,\n          i,\n        )\n      val storyInputStream =\n        HpackJsonUtil::class.java.getResourceAsStream(storyResourceName)\n          ?: break\n      try {\n        storyInputStream.use {\n          val story =\n            readStory(storyInputStream.source().buffer())\n              .copy(fileName = storyResourceName)\n          result.add(story)\n          i++\n        }\n      } finally {\n        storyInputStream.close()\n      }\n    }\n\n    if (result.isEmpty()) {\n      // missing files\n      result.add(Story.MISSING)\n    }\n\n    return result\n  }\n}\n"
  },
  {
    "path": "okhttp-hpacktests/src/test/java/okhttp3/internal/http2/hpackjson/Story.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.http2.hpackjson\n\n/**\n * Representation of one story, a set of request headers to encode or decode. This class is used\n * reflectively with Moshi to parse stories from files.\n */\ndata class Story(\n  val description: String? = null,\n  val cases: List<Case>,\n  val fileName: String? = null,\n) {\n  // Used as the test name.\n  override fun toString() = fileName ?: \"?\"\n\n  companion object {\n    @JvmField\n    val MISSING = Story(description = \"Missing\", cases = listOf(), \"missing\")\n  }\n}\n"
  },
  {
    "path": "okhttp-idna-mapping-table/README.md",
    "content": "OkHttp IDNA Mapping Table\n=========================\n\nThis module contains supporting tools for building the IDNA mapping table.\n\nIt is not required for runtime IDN mappings.\n"
  },
  {
    "path": "okhttp-idna-mapping-table/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n  id(\"ru.vyarus.animalsniffer\")\n}\n\ndependencies {\n  api(libs.square.okio)\n  api(libs.square.kotlin.poet)\n  testImplementation(libs.assertk)\n  testImplementation(libs.junit.jupiter.api)\n  testImplementation(libs.junit.jupiter.params)\n\n  testImplementation(rootProject.libs.junit.jupiter.engine)\n}\n\nanimalsniffer {\n  isIgnoreFailures = true\n}\n"
  },
  {
    "path": "okhttp-idna-mapping-table/src/main/kotlin/okhttp3/internal/idn/GenerateIdnaMappingTableCode.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:JvmName(\"GenerateIdnaMappingTableCode\")\n\npackage okhttp3.internal.idn\n\nimport com.squareup.kotlinpoet.ClassName\nimport com.squareup.kotlinpoet.FileSpec\nimport com.squareup.kotlinpoet.KModifier\nimport com.squareup.kotlinpoet.PropertySpec\nimport java.io.File\nimport okio.FileSystem\nimport okio.Path.Companion.toPath\n\nfun main(vararg args: String) {\n  val data = loadIdnaMappingTableData()\n  val file = generateMappingTableFile(data)\n  file.writeTo(File(args[0]))\n}\n\nfun loadIdnaMappingTableData(): IdnaMappingTableData {\n  val path = \"/okhttp3/internal/idna/IdnaMappingTable.txt\".toPath()\n  val table =\n    FileSystem.RESOURCES.read(path) {\n      readPlainTextIdnaMappingTable()\n    }\n  return buildIdnaMappingTableData(table)\n}\n\n/**\n * Generate a file containing the mapping table's string literals, like this:\n *\n * ```\n * internal val IDNA_MAPPING_TABLE: IdnaMappingTable = IdnaMappingTable(\n *   sections = \"...\",\n *   ranges = \"...\",\n *   mappings = \"\",\n * )\n * ```\n */\nfun generateMappingTableFile(data: IdnaMappingTableData): FileSpec {\n  val packageName = \"okhttp3.internal.idn\"\n  val idnaMappingTable = ClassName(packageName, \"IdnaMappingTable\")\n\n  return FileSpec\n    .builder(packageName, \"IdnaMappingTableInstance\")\n    .addProperty(\n      PropertySpec\n        .builder(\"IDNA_MAPPING_TABLE\", idnaMappingTable)\n        .addModifiers(KModifier.INTERNAL)\n        .initializer(\n          \"\"\"\n          |%T(⇥\n          |sections = \"%L\",\n          |ranges = \"%L\",\n          |mappings = \"%L\",\n          |⇤)\n          \"\"\".trimMargin(),\n          idnaMappingTable,\n          data.sections.escapeDataString(),\n          data.ranges.escapeDataString(),\n          data.mappings.escapeDataString(),\n        ).build(),\n    ).build()\n}\n\n/**\n * KotlinPoet doesn't really know what to do with a string containing NUL, BEL, DEL, etc. We also\n * don't want to perform `trimMargin()` at runtime.\n */\nfun String.escapeDataString(): String =\n  buildString {\n    for (codePoint in this@escapeDataString.codePoints()) {\n      when (codePoint) {\n        in 0..0x20,\n        '\"'.code,\n        '$'.code,\n        '\\\\'.code,\n        '·'.code,\n        127,\n        -> append(String.format(\"\\\\u%04x\", codePoint))\n\n        else -> appendCodePoint(codePoint)\n      }\n    }\n  }\n"
  },
  {
    "path": "okhttp-idna-mapping-table/src/main/kotlin/okhttp3/internal/idn/IdnaMappingTableData.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\n/** Recipe to build an `IdnaMappingTable`. */\nclass IdnaMappingTableData(\n  val sections: String,\n  val ranges: String,\n  val mappings: String,\n)\n"
  },
  {
    "path": "okhttp-idna-mapping-table/src/main/kotlin/okhttp3/internal/idn/MappedRange.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\nimport kotlin.math.abs\nimport okio.ByteString\n\ninternal sealed interface MappedRange {\n  val rangeStart: Int\n\n  data class Constant(\n    override val rangeStart: Int,\n    val type: Int,\n  ) : MappedRange {\n    val b1: Int\n      get() =\n        when (type) {\n          TYPE_IGNORED -> 119\n          TYPE_VALID -> 120\n          TYPE_DISALLOWED -> 121\n          else -> error(\"unexpected type: $type\")\n        }\n  }\n\n  data class Inline1(\n    override val rangeStart: Int,\n    private val mappedTo: ByteString,\n  ) : MappedRange {\n    val b1: Int\n      get() {\n        val b3bit8 = mappedTo[0] and 0x80 != 0\n        return if (b3bit8) 123 else 122\n      }\n\n    val b2: Int\n      get() = mappedTo[0] and 0x7f\n  }\n\n  data class Inline2(\n    override val rangeStart: Int,\n    private val mappedTo: ByteString,\n  ) : MappedRange {\n    val b1: Int\n      get() {\n        val b2bit8 = mappedTo[0] and 0x80 != 0\n        val b3bit8 = mappedTo[1] and 0x80 != 0\n        return when {\n          b2bit8 && b3bit8 -> 127\n          b3bit8 -> 126\n          b2bit8 -> 125\n          else -> 124\n        }\n      }\n\n    val b2: Int\n      get() = mappedTo[0] and 0x7f\n\n    val b3: Int\n      get() = mappedTo[1] and 0x7f\n  }\n\n  data class InlineDelta(\n    override val rangeStart: Int,\n    val codepointDelta: Int,\n  ) : MappedRange {\n    private val absoluteDelta = abs(codepointDelta)\n\n    val b1: Int\n      get() =\n        when {\n          codepointDelta < 0 -> 0x40 or (absoluteDelta shr 14)\n          codepointDelta > 0 -> 0x50 or (absoluteDelta shr 14)\n          else -> error(\"Unexpected codepointDelta of 0\")\n        }\n\n    val b2: Int\n      get() = absoluteDelta shr 7 and 0x7f\n\n    val b3: Int\n      get() = absoluteDelta and 0x7f\n\n    companion object {\n      const val MAX_VALUE = 0x3FFFF\n    }\n  }\n\n  data class External(\n    override val rangeStart: Int,\n    val mappedTo: ByteString,\n  ) : MappedRange\n}\n"
  },
  {
    "path": "okhttp-idna-mapping-table/src/main/kotlin/okhttp3/internal/idn/MappingTables.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\nimport kotlin.math.abs\nimport kotlin.streams.toList\nimport okio.Buffer\n\n/** Index [table] for compactness as specified by `IdnaMappingTable`. */\nfun buildIdnaMappingTableData(table: SimpleIdnaMappingTable): IdnaMappingTableData {\n  val simplified = mergeAdjacentRanges(table.mappings)\n  val withoutSectionSpans = withoutSectionSpans(simplified)\n  val sections = sections(withoutSectionSpans)\n\n  val rangesBuffer = Buffer()\n  val mappingsBuffer = StringBuilder()\n  val sectionIndexBuffer = Buffer()\n\n  var previousMappedRanges: List<MappedRange>? = null\n\n  for ((section, sectionMappedRanges) in sections) {\n    // Skip sequential ranges when they are equal.\n    if (sectionMappedRanges == previousMappedRanges) continue\n    previousMappedRanges = sectionMappedRanges\n\n    val sectionOffset = rangesBuffer.size.toInt() / 4\n\n    // Section prefix.\n    sectionIndexBuffer.writeByte(section and 0x1fc000 shr 14)\n    sectionIndexBuffer.writeByte((section and 0x3f80) shr 7)\n\n    // Section index.\n    sectionIndexBuffer.writeByte((sectionOffset and 0x3f80) shr 7)\n    sectionIndexBuffer.writeByte(sectionOffset and 0x7f)\n\n    // Ranges.\n    for (range in sectionMappedRanges) {\n      rangesBuffer.writeByte(range.rangeStart)\n\n      when (range) {\n        is MappedRange.Constant -> {\n          rangesBuffer.writeByte(range.b1)\n          rangesBuffer.writeByte('-'.code)\n          rangesBuffer.writeByte('-'.code)\n        }\n\n        is MappedRange.Inline1 -> {\n          rangesBuffer.writeByte(range.b1)\n          rangesBuffer.writeByte(range.b2)\n          rangesBuffer.writeByte('-'.code)\n        }\n\n        is MappedRange.Inline2 -> {\n          rangesBuffer.writeByte(range.b1)\n          rangesBuffer.writeByte(range.b2)\n          rangesBuffer.writeByte(range.b3)\n        }\n\n        is MappedRange.InlineDelta -> {\n          rangesBuffer.writeByte(range.b1)\n          rangesBuffer.writeByte(range.b2)\n          rangesBuffer.writeByte(range.b3)\n        }\n\n        is MappedRange.External -> {\n          // Write the mapping.\n          val mappingOffset: Int\n          val mappedTo = range.mappedTo.utf8()\n          val mappingIndex = mappingsBuffer.indexOf(mappedTo)\n          if (mappingIndex == -1) {\n            mappingOffset = mappingsBuffer.length\n            mappingsBuffer.append(mappedTo)\n          } else {\n            mappingOffset = mappingIndex\n          }\n\n          // Write the range bytes.\n          val b1 = mappedTo.length\n          val b2 = (mappingOffset and 0x3f80) shr 7\n          val b3 = mappingOffset and 0x7f\n          rangesBuffer.writeByte(b1)\n          rangesBuffer.writeByte(b2)\n          rangesBuffer.writeByte(b3)\n        }\n      }\n    }\n  }\n\n  return IdnaMappingTableData(\n    sections = sectionIndexBuffer.readUtf8(),\n    ranges = rangesBuffer.readUtf8(),\n    mappings = mappingsBuffer.toString(),\n  )\n}\n\n/**\n * If [mapping] qualifies to be encoded as [MappedRange.InlineDelta] return new instance, otherwise null.\n * An [MappedRange.InlineDelta] must be a mapping from a single code-point to a single code-point with a difference\n * that can be represented in 2^18-1.\n */\ninternal fun inlineDeltaOrNull(mapping: Mapping): MappedRange.InlineDelta? {\n  if (mapping.hasSingleSourceCodePoint) {\n    val sourceCodePoint = mapping.sourceCodePoint0\n    val mappedCodePoints =\n      mapping.mappedTo\n        .utf8()\n        .codePoints()\n        .toList()\n    if (mappedCodePoints.size == 1) {\n      val codePointDelta = mappedCodePoints.single() - sourceCodePoint\n      if (MappedRange.InlineDelta.MAX_VALUE >= abs(codePointDelta)) {\n        return MappedRange.InlineDelta(mapping.rangeStart, codePointDelta)\n      }\n    }\n  }\n  return null\n}\n\n/**\n * Inputs must have applied [withoutSectionSpans].\n */\ninternal fun sections(mappings: List<Mapping>): Map<Int, List<MappedRange>> {\n  val result = mutableMapOf<Int, MutableList<MappedRange>>()\n\n  for (mapping in mappings) {\n    require(!mapping.spansSections)\n\n    val section = mapping.section\n    val rangeStart = mapping.rangeStart\n\n    val sectionList = result.getOrPut(section) { mutableListOf() }\n\n    sectionList +=\n      when (mapping.type) {\n        TYPE_MAPPED -> {\n          run {\n            val deltaMapping = inlineDeltaOrNull(mapping)\n            if (deltaMapping != null) {\n              return@run deltaMapping\n            }\n\n            when (mapping.mappedTo.size) {\n              1 -> MappedRange.Inline1(rangeStart, mapping.mappedTo)\n              2 -> MappedRange.Inline2(rangeStart, mapping.mappedTo)\n              else -> MappedRange.External(rangeStart, mapping.mappedTo)\n            }\n          }\n        }\n\n        TYPE_IGNORED, TYPE_VALID, TYPE_DISALLOWED -> {\n          MappedRange.Constant(rangeStart, mapping.type)\n        }\n\n        else -> {\n          error(\"unexpected mapping type: ${mapping.type}\")\n        }\n      }\n  }\n\n  for (sectionList in result.values) {\n    mergeAdjacentDeltaMappedRanges(sectionList)\n  }\n\n  return result.toMap()\n}\n\n/**\n * Modifies [ranges] to combine any adjacent [MappedRange.InlineDelta] of same size to single entry.\n * @returns same instance of [ranges] for convenience\n */\ninternal fun mergeAdjacentDeltaMappedRanges(ranges: MutableList<MappedRange>): MutableList<MappedRange> {\n  var i = 0\n  while (i < ranges.size) {\n    val curr = ranges[i]\n    if (curr is MappedRange.InlineDelta) {\n      val j = i + 1\n      mergeAdjacent@ while (j < ranges.size) {\n        val next = ranges[j]\n        if (next is MappedRange.InlineDelta &&\n          curr.codepointDelta == next.codepointDelta\n        ) {\n          ranges.removeAt(j)\n        } else {\n          break@mergeAdjacent\n        }\n      }\n    }\n    i++\n  }\n  return ranges\n}\n\n/**\n * Returns a copy of [mappings], splitting to ensure that each mapping is entirely contained within\n * a single section.\n */\ninternal fun withoutSectionSpans(mappings: List<Mapping>): List<Mapping> {\n  val result = mutableListOf<Mapping>()\n\n  val i = mappings.iterator()\n  var current = i.next()\n\n  while (true) {\n    if (current.spansSections) {\n      result +=\n        Mapping(\n          current.sourceCodePoint0,\n          current.section + 0x7f,\n          current.type,\n          current.mappedTo,\n        )\n      current =\n        Mapping(\n          current.section + 0x80,\n          current.sourceCodePoint1,\n          current.type,\n          current.mappedTo,\n        )\n    } else {\n      result += current\n      current = if (i.hasNext()) i.next() else break\n    }\n  }\n\n  return result\n}\n\n/** Returns a copy of [mappings] with adjacent ranges merged wherever possible. */\ninternal fun mergeAdjacentRanges(mappings: List<Mapping>): List<Mapping> {\n  var index = 0\n  val result = mutableListOf<Mapping>()\n\n  while (index < mappings.size) {\n    val mapping = mappings[index]\n    val type = canonicalizeType(mapping.type)\n    val mappedTo = mapping.mappedTo\n\n    var unionWith: Mapping = mapping\n    index++\n\n    while (index < mappings.size) {\n      val next = mappings[index]\n\n      if (type != canonicalizeType(next.type)) break\n      if (type == TYPE_MAPPED && mappedTo != next.mappedTo) break\n\n      unionWith = next\n      index++\n    }\n\n    result +=\n      Mapping(\n        sourceCodePoint0 = mapping.sourceCodePoint0,\n        sourceCodePoint1 = unionWith.sourceCodePoint1,\n        type = type,\n        mappedTo = mappedTo,\n      )\n  }\n\n  return result\n}\n\ninternal fun canonicalizeType(type: Int): Int =\n  when (type) {\n    TYPE_IGNORED -> TYPE_IGNORED\n\n    TYPE_MAPPED,\n    TYPE_DISALLOWED_STD3_MAPPED,\n    -> TYPE_MAPPED\n\n    TYPE_DEVIATION,\n    TYPE_DISALLOWED_STD3_VALID,\n    TYPE_VALID,\n    -> TYPE_VALID\n\n    TYPE_DISALLOWED -> TYPE_DISALLOWED\n\n    else -> error(\"unexpected type: $type\")\n  }\n\ninternal infix fun Byte.and(mask: Int): Int = toInt() and mask\n\ninternal infix fun Short.and(mask: Int): Int = toInt() and mask\n\ninternal infix fun Int.and(mask: Long): Long = toLong() and mask\n"
  },
  {
    "path": "okhttp-idna-mapping-table/src/main/kotlin/okhttp3/internal/idn/SimpleIdnaMappingTable.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\nimport java.io.IOException\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.Options\n\n/**\n * A decoded [mapping table] that can perform the [mapping step] of IDNA processing.\n *\n * This implementation is optimized for readability over efficiency.\n *\n * This implements non-transitional processing by preserving deviation characters.\n *\n * This implementation's STD3 rules are configured to `UseSTD3ASCIIRules=false`. This is\n * permissive and permits the `_` character.\n *\n * [mapping table]: https://www.unicode.org/reports/tr46/#IDNA_Mapping_Table\n * [mapping step]: https://www.unicode.org/reports/tr46/#ProcessingStepMap\n */\nclass SimpleIdnaMappingTable internal constructor(\n  internal val mappings: List<Mapping>,\n) {\n  /**\n   * Returns true if the [codePoint] was applied successfully. Returns false if it was disallowed.\n   */\n  fun map(\n    codePoint: Int,\n    sink: BufferedSink,\n  ): Boolean {\n    val index =\n      mappings.binarySearch {\n        when {\n          it.sourceCodePoint1 < codePoint -> -1\n          it.sourceCodePoint0 > codePoint -> 1\n          else -> 0\n        }\n      }\n\n    // Code points must be in 0..0x10ffff.\n    require(index in mappings.indices) { \"unexpected code point: $codePoint\" }\n\n    val mapping = mappings[index]\n    var result = true\n\n    when (mapping.type) {\n      TYPE_IGNORED -> {\n        Unit\n      }\n\n      TYPE_MAPPED, TYPE_DISALLOWED_STD3_MAPPED -> {\n        sink.write(mapping.mappedTo)\n      }\n\n      TYPE_DEVIATION, TYPE_DISALLOWED_STD3_VALID, TYPE_VALID -> {\n        sink.writeUtf8CodePoint(codePoint)\n      }\n\n      TYPE_DISALLOWED -> {\n        sink.writeUtf8CodePoint(codePoint)\n        result = false\n      }\n    }\n\n    return result\n  }\n}\n\nprivate val optionsDelimiter =\n  Options.of(\n    // 0.\n    \".\".encodeUtf8(),\n    // 1.\n    \" \".encodeUtf8(),\n    // 2.\n    \";\".encodeUtf8(),\n    // 3.\n    \"#\".encodeUtf8(),\n    // 4.\n    \"\\n\".encodeUtf8(),\n  )\n\nprivate val optionsDot =\n  Options.of(\n    // 0.\n    \".\".encodeUtf8(),\n  )\n\nprivate const val DELIMITER_DOT = 0\nprivate const val DELIMITER_SPACE = 1\nprivate const val DELIMITER_SEMICOLON = 2\nprivate const val DELIMITER_HASH = 3\nprivate const val DELIMITER_NEWLINE = 4\n\nprivate val optionsType =\n  Options.of(\n    // 0.\n    \"deviation \".encodeUtf8(),\n    // 1.\n    \"disallowed \".encodeUtf8(),\n    // 2.\n    \"disallowed_STD3_mapped \".encodeUtf8(),\n    // 3.\n    \"disallowed_STD3_valid \".encodeUtf8(),\n    // 4.\n    \"ignored \".encodeUtf8(),\n    // 5.\n    \"mapped \".encodeUtf8(),\n    // 6.\n    \"valid \".encodeUtf8(),\n  )\n\ninternal const val TYPE_DEVIATION = 0\ninternal const val TYPE_DISALLOWED = 1\ninternal const val TYPE_DISALLOWED_STD3_MAPPED = 2\ninternal const val TYPE_DISALLOWED_STD3_VALID = 3\ninternal const val TYPE_IGNORED = 4\ninternal const val TYPE_MAPPED = 5\ninternal const val TYPE_VALID = 6\n\nprivate fun BufferedSource.skipWhitespace() {\n  while (!exhausted()) {\n    if (buffer[0] != ' '.code.toByte()) return\n    skip(1L)\n  }\n}\n\nprivate fun BufferedSource.skipRestOfLine() {\n  when (val newline = indexOf('\\n'.code.toByte())) {\n    -1L -> skip(buffer.size)\n\n    // Exhaust this source.\n    else -> skip(newline + 1)\n  }\n}\n\n/**\n * Reads lines from `IdnaMappingTable.txt`.\n *\n * Comment lines are either blank or start with a `#` character. Lines may also end with a comment.\n * All comments are ignored.\n *\n * Regular lines contain fields separated by semicolons.\n *\n * The first element on each line is a single hex code point (like 0041) or a hex code point range\n * (like 0030..0039).\n *\n * The second element on each line is a mapping type, like `valid` or `mapped`.\n *\n * For lines that contain a mapping target, the next thing is a sequence of hex code points (like\n * 0031 2044 0034).\n *\n * All other data is ignored.\n */\nfun BufferedSource.readPlainTextIdnaMappingTable(): SimpleIdnaMappingTable {\n  val mappedTo = Buffer()\n  val result = mutableListOf<Mapping>()\n\n  while (!exhausted()) {\n    // Skip comment and empty lines.\n    when (select(optionsDelimiter)) {\n      DELIMITER_HASH -> {\n        skipRestOfLine()\n        continue\n      }\n\n      DELIMITER_NEWLINE -> {\n        continue\n      }\n\n      DELIMITER_DOT, DELIMITER_SPACE, DELIMITER_SEMICOLON -> {\n        throw IOException(\"unexpected delimiter\")\n      }\n    }\n\n    // \"002F\" or \"0000..002C\"\n    val sourceCodePoint0 = readHexadecimalUnsignedLong()\n    val sourceCodePoint1 =\n      when (select(optionsDot)) {\n        DELIMITER_DOT -> {\n          if (readByte() != '.'.code.toByte()) throw IOException(\"expected '..'\")\n          readHexadecimalUnsignedLong()\n        }\n\n        else -> {\n          sourceCodePoint0\n        }\n      }\n\n    skipWhitespace()\n    if (readByte() != ';'.code.toByte()) throw IOException(\"expected ';'\")\n\n    // \"valid\" or \"mapped\"\n    skipWhitespace()\n    val type = select(optionsType)\n\n    when (type) {\n      TYPE_DEVIATION, TYPE_MAPPED, TYPE_DISALLOWED_STD3_MAPPED -> {\n        skipWhitespace()\n        if (readByte() != ';'.code.toByte()) throw IOException(\"expected ';'\")\n\n        // Like \"0061\" or \"0031 2044 0034\".\n        while (true) {\n          skipWhitespace()\n\n          when (select(optionsDelimiter)) {\n            DELIMITER_HASH -> {\n              break\n            }\n\n            DELIMITER_DOT, DELIMITER_SEMICOLON, DELIMITER_NEWLINE -> {\n              throw IOException(\"unexpected delimiter\")\n            }\n          }\n\n          mappedTo.writeUtf8CodePoint(readHexadecimalUnsignedLong().toInt())\n        }\n      }\n\n      TYPE_DISALLOWED, TYPE_DISALLOWED_STD3_VALID, TYPE_IGNORED, TYPE_VALID -> {\n        Unit\n      }\n\n      else -> {\n        throw IOException(\"unexpected type\")\n      }\n    }\n\n    skipRestOfLine()\n\n    result +=\n      Mapping(\n        sourceCodePoint0.toInt(),\n        sourceCodePoint1.toInt(),\n        type,\n        mappedTo.readByteString(),\n      )\n  }\n\n  return SimpleIdnaMappingTable(result)\n}\n\ninternal data class Mapping(\n  val sourceCodePoint0: Int,\n  val sourceCodePoint1: Int,\n  val type: Int,\n  val mappedTo: ByteString,\n) {\n  val section: Int\n    get() = sourceCodePoint0 and 0x1fff80\n\n  val rangeStart: Int\n    get() = sourceCodePoint0 and 0x7f\n\n  val hasSingleSourceCodePoint: Boolean\n    get() = sourceCodePoint0 == sourceCodePoint1\n\n  val spansSections: Boolean\n    get() = (sourceCodePoint0 and 0x1fff80) != (sourceCodePoint1 and 0x1fff80)\n}\n"
  },
  {
    "path": "okhttp-idna-mapping-table/src/main/resources/okhttp3/internal/idna/IdnaMappingTable.txt",
    "content": "# IdnaMappingTable.txt\n# Date: 2023-08-10, 22:32:27 GMT\n# © 2023 Unicode®, Inc.\n# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.\n# For terms of use, see https://www.unicode.org/terms_of_use.html\n#\n# Unicode IDNA Compatible Preprocessing for UTS #46\n# Version: 15.1.0\n#\n# For documentation and usage, see https://www.unicode.org/reports/tr46\n#\n0000..002C    ; disallowed_STD3_valid                  # 1.1  <control-0000>..COMMA\n002D..002E    ; valid                                  # 1.1  HYPHEN-MINUS..FULL STOP\n002F          ; disallowed_STD3_valid                  # 1.1  SOLIDUS\n0030..0039    ; valid                                  # 1.1  DIGIT ZERO..DIGIT NINE\n003A..0040    ; disallowed_STD3_valid                  # 1.1  COLON..COMMERCIAL AT\n0041          ; mapped                 ; 0061          # 1.1  LATIN CAPITAL LETTER A\n0042          ; mapped                 ; 0062          # 1.1  LATIN CAPITAL LETTER B\n0043          ; mapped                 ; 0063          # 1.1  LATIN CAPITAL LETTER C\n0044          ; mapped                 ; 0064          # 1.1  LATIN CAPITAL LETTER D\n0045          ; mapped                 ; 0065          # 1.1  LATIN CAPITAL LETTER E\n0046          ; mapped                 ; 0066          # 1.1  LATIN CAPITAL LETTER F\n0047          ; mapped                 ; 0067          # 1.1  LATIN CAPITAL LETTER G\n0048          ; mapped                 ; 0068          # 1.1  LATIN CAPITAL LETTER H\n0049          ; mapped                 ; 0069          # 1.1  LATIN CAPITAL LETTER I\n004A          ; mapped                 ; 006A          # 1.1  LATIN CAPITAL LETTER J\n004B          ; mapped                 ; 006B          # 1.1  LATIN CAPITAL LETTER K\n004C          ; mapped                 ; 006C          # 1.1  LATIN CAPITAL LETTER L\n004D          ; mapped                 ; 006D          # 1.1  LATIN CAPITAL LETTER M\n004E          ; mapped                 ; 006E          # 1.1  LATIN CAPITAL LETTER N\n004F          ; mapped                 ; 006F          # 1.1  LATIN CAPITAL LETTER O\n0050          ; mapped                 ; 0070          # 1.1  LATIN CAPITAL LETTER P\n0051          ; mapped                 ; 0071          # 1.1  LATIN CAPITAL LETTER Q\n0052          ; mapped                 ; 0072          # 1.1  LATIN CAPITAL LETTER R\n0053          ; mapped                 ; 0073          # 1.1  LATIN CAPITAL LETTER S\n0054          ; mapped                 ; 0074          # 1.1  LATIN CAPITAL LETTER T\n0055          ; mapped                 ; 0075          # 1.1  LATIN CAPITAL LETTER U\n0056          ; mapped                 ; 0076          # 1.1  LATIN CAPITAL LETTER V\n0057          ; mapped                 ; 0077          # 1.1  LATIN CAPITAL LETTER W\n0058          ; mapped                 ; 0078          # 1.1  LATIN CAPITAL LETTER X\n0059          ; mapped                 ; 0079          # 1.1  LATIN CAPITAL LETTER Y\n005A          ; mapped                 ; 007A          # 1.1  LATIN CAPITAL LETTER Z\n005B..0060    ; disallowed_STD3_valid                  # 1.1  LEFT SQUARE BRACKET..GRAVE ACCENT\n0061..007A    ; valid                                  # 1.1  LATIN SMALL LETTER A..LATIN SMALL LETTER Z\n007B..007F    ; disallowed_STD3_valid                  # 1.1  LEFT CURLY BRACKET..<control-007F>\n0080..009F    ; disallowed                             # 1.1  <control-0080>..<control-009F>\n00A0          ; disallowed_STD3_mapped ; 0020          # 1.1  NO-BREAK SPACE\n00A1..00A7    ; valid                  ;      ; NV8    # 1.1  INVERTED EXCLAMATION MARK..SECTION SIGN\n00A8          ; disallowed_STD3_mapped ; 0020 0308     # 1.1  DIAERESIS\n00A9          ; valid                  ;      ; NV8    # 1.1  COPYRIGHT SIGN\n00AA          ; mapped                 ; 0061          # 1.1  FEMININE ORDINAL INDICATOR\n00AB..00AC    ; valid                  ;      ; NV8    # 1.1  LEFT-POINTING DOUBLE ANGLE QUOTATION MARK..NOT SIGN\n00AD          ; ignored                                # 1.1  SOFT HYPHEN\n00AE          ; valid                  ;      ; NV8    # 1.1  REGISTERED SIGN\n00AF          ; disallowed_STD3_mapped ; 0020 0304     # 1.1  MACRON\n00B0..00B1    ; valid                  ;      ; NV8    # 1.1  DEGREE SIGN..PLUS-MINUS SIGN\n00B2          ; mapped                 ; 0032          # 1.1  SUPERSCRIPT TWO\n00B3          ; mapped                 ; 0033          # 1.1  SUPERSCRIPT THREE\n00B4          ; disallowed_STD3_mapped ; 0020 0301     # 1.1  ACUTE ACCENT\n00B5          ; mapped                 ; 03BC          # 1.1  MICRO SIGN\n00B6          ; valid                  ;      ; NV8    # 1.1  PILCROW SIGN\n00B7          ; valid                                  # 1.1  MIDDLE DOT\n00B8          ; disallowed_STD3_mapped ; 0020 0327     # 1.1  CEDILLA\n00B9          ; mapped                 ; 0031          # 1.1  SUPERSCRIPT ONE\n00BA          ; mapped                 ; 006F          # 1.1  MASCULINE ORDINAL INDICATOR\n00BB          ; valid                  ;      ; NV8    # 1.1  RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK\n00BC          ; mapped                 ; 0031 2044 0034 #1.1  VULGAR FRACTION ONE QUARTER\n00BD          ; mapped                 ; 0031 2044 0032 #1.1  VULGAR FRACTION ONE HALF\n00BE          ; mapped                 ; 0033 2044 0034 #1.1  VULGAR FRACTION THREE QUARTERS\n00BF          ; valid                  ;      ; NV8    # 1.1  INVERTED QUESTION MARK\n00C0          ; mapped                 ; 00E0          # 1.1  LATIN CAPITAL LETTER A WITH GRAVE\n00C1          ; mapped                 ; 00E1          # 1.1  LATIN CAPITAL LETTER A WITH ACUTE\n00C2          ; mapped                 ; 00E2          # 1.1  LATIN CAPITAL LETTER A WITH CIRCUMFLEX\n00C3          ; mapped                 ; 00E3          # 1.1  LATIN CAPITAL LETTER A WITH TILDE\n00C4          ; mapped                 ; 00E4          # 1.1  LATIN CAPITAL LETTER A WITH DIAERESIS\n00C5          ; mapped                 ; 00E5          # 1.1  LATIN CAPITAL LETTER A WITH RING ABOVE\n00C6          ; mapped                 ; 00E6          # 1.1  LATIN CAPITAL LETTER AE\n00C7          ; mapped                 ; 00E7          # 1.1  LATIN CAPITAL LETTER C WITH CEDILLA\n00C8          ; mapped                 ; 00E8          # 1.1  LATIN CAPITAL LETTER E WITH GRAVE\n00C9          ; mapped                 ; 00E9          # 1.1  LATIN CAPITAL LETTER E WITH ACUTE\n00CA          ; mapped                 ; 00EA          # 1.1  LATIN CAPITAL LETTER E WITH CIRCUMFLEX\n00CB          ; mapped                 ; 00EB          # 1.1  LATIN CAPITAL LETTER E WITH DIAERESIS\n00CC          ; mapped                 ; 00EC          # 1.1  LATIN CAPITAL LETTER I WITH GRAVE\n00CD          ; mapped                 ; 00ED          # 1.1  LATIN CAPITAL LETTER I WITH ACUTE\n00CE          ; mapped                 ; 00EE          # 1.1  LATIN CAPITAL LETTER I WITH CIRCUMFLEX\n00CF          ; mapped                 ; 00EF          # 1.1  LATIN CAPITAL LETTER I WITH DIAERESIS\n00D0          ; mapped                 ; 00F0          # 1.1  LATIN CAPITAL LETTER ETH\n00D1          ; mapped                 ; 00F1          # 1.1  LATIN CAPITAL LETTER N WITH TILDE\n00D2          ; mapped                 ; 00F2          # 1.1  LATIN CAPITAL LETTER O WITH GRAVE\n00D3          ; mapped                 ; 00F3          # 1.1  LATIN CAPITAL LETTER O WITH ACUTE\n00D4          ; mapped                 ; 00F4          # 1.1  LATIN CAPITAL LETTER O WITH CIRCUMFLEX\n00D5          ; mapped                 ; 00F5          # 1.1  LATIN CAPITAL LETTER O WITH TILDE\n00D6          ; mapped                 ; 00F6          # 1.1  LATIN CAPITAL LETTER O WITH DIAERESIS\n00D7          ; valid                  ;      ; NV8    # 1.1  MULTIPLICATION SIGN\n00D8          ; mapped                 ; 00F8          # 1.1  LATIN CAPITAL LETTER O WITH STROKE\n00D9          ; mapped                 ; 00F9          # 1.1  LATIN CAPITAL LETTER U WITH GRAVE\n00DA          ; mapped                 ; 00FA          # 1.1  LATIN CAPITAL LETTER U WITH ACUTE\n00DB          ; mapped                 ; 00FB          # 1.1  LATIN CAPITAL LETTER U WITH CIRCUMFLEX\n00DC          ; mapped                 ; 00FC          # 1.1  LATIN CAPITAL LETTER U WITH DIAERESIS\n00DD          ; mapped                 ; 00FD          # 1.1  LATIN CAPITAL LETTER Y WITH ACUTE\n00DE          ; mapped                 ; 00FE          # 1.1  LATIN CAPITAL LETTER THORN\n00DF          ; deviation              ; 0073 0073     # 1.1  LATIN SMALL LETTER SHARP S\n00E0..00F6    ; valid                                  # 1.1  LATIN SMALL LETTER A WITH GRAVE..LATIN SMALL LETTER O WITH DIAERESIS\n00F7          ; valid                  ;      ; NV8    # 1.1  DIVISION SIGN\n00F8..00FF    ; valid                                  # 1.1  LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER Y WITH DIAERESIS\n0100          ; mapped                 ; 0101          # 1.1  LATIN CAPITAL LETTER A WITH MACRON\n0101          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH MACRON\n0102          ; mapped                 ; 0103          # 1.1  LATIN CAPITAL LETTER A WITH BREVE\n0103          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH BREVE\n0104          ; mapped                 ; 0105          # 1.1  LATIN CAPITAL LETTER A WITH OGONEK\n0105          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH OGONEK\n0106          ; mapped                 ; 0107          # 1.1  LATIN CAPITAL LETTER C WITH ACUTE\n0107          ; valid                                  # 1.1  LATIN SMALL LETTER C WITH ACUTE\n0108          ; mapped                 ; 0109          # 1.1  LATIN CAPITAL LETTER C WITH CIRCUMFLEX\n0109          ; valid                                  # 1.1  LATIN SMALL LETTER C WITH CIRCUMFLEX\n010A          ; mapped                 ; 010B          # 1.1  LATIN CAPITAL LETTER C WITH DOT ABOVE\n010B          ; valid                                  # 1.1  LATIN SMALL LETTER C WITH DOT ABOVE\n010C          ; mapped                 ; 010D          # 1.1  LATIN CAPITAL LETTER C WITH CARON\n010D          ; valid                                  # 1.1  LATIN SMALL LETTER C WITH CARON\n010E          ; mapped                 ; 010F          # 1.1  LATIN CAPITAL LETTER D WITH CARON\n010F          ; valid                                  # 1.1  LATIN SMALL LETTER D WITH CARON\n0110          ; mapped                 ; 0111          # 1.1  LATIN CAPITAL LETTER D WITH STROKE\n0111          ; valid                                  # 1.1  LATIN SMALL LETTER D WITH STROKE\n0112          ; mapped                 ; 0113          # 1.1  LATIN CAPITAL LETTER E WITH MACRON\n0113          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH MACRON\n0114          ; mapped                 ; 0115          # 1.1  LATIN CAPITAL LETTER E WITH BREVE\n0115          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH BREVE\n0116          ; mapped                 ; 0117          # 1.1  LATIN CAPITAL LETTER E WITH DOT ABOVE\n0117          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH DOT ABOVE\n0118          ; mapped                 ; 0119          # 1.1  LATIN CAPITAL LETTER E WITH OGONEK\n0119          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH OGONEK\n011A          ; mapped                 ; 011B          # 1.1  LATIN CAPITAL LETTER E WITH CARON\n011B          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH CARON\n011C          ; mapped                 ; 011D          # 1.1  LATIN CAPITAL LETTER G WITH CIRCUMFLEX\n011D          ; valid                                  # 1.1  LATIN SMALL LETTER G WITH CIRCUMFLEX\n011E          ; mapped                 ; 011F          # 1.1  LATIN CAPITAL LETTER G WITH BREVE\n011F          ; valid                                  # 1.1  LATIN SMALL LETTER G WITH BREVE\n0120          ; mapped                 ; 0121          # 1.1  LATIN CAPITAL LETTER G WITH DOT ABOVE\n0121          ; valid                                  # 1.1  LATIN SMALL LETTER G WITH DOT ABOVE\n0122          ; mapped                 ; 0123          # 1.1  LATIN CAPITAL LETTER G WITH CEDILLA\n0123          ; valid                                  # 1.1  LATIN SMALL LETTER G WITH CEDILLA\n0124          ; mapped                 ; 0125          # 1.1  LATIN CAPITAL LETTER H WITH CIRCUMFLEX\n0125          ; valid                                  # 1.1  LATIN SMALL LETTER H WITH CIRCUMFLEX\n0126          ; mapped                 ; 0127          # 1.1  LATIN CAPITAL LETTER H WITH STROKE\n0127          ; valid                                  # 1.1  LATIN SMALL LETTER H WITH STROKE\n0128          ; mapped                 ; 0129          # 1.1  LATIN CAPITAL LETTER I WITH TILDE\n0129          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH TILDE\n012A          ; mapped                 ; 012B          # 1.1  LATIN CAPITAL LETTER I WITH MACRON\n012B          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH MACRON\n012C          ; mapped                 ; 012D          # 1.1  LATIN CAPITAL LETTER I WITH BREVE\n012D          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH BREVE\n012E          ; mapped                 ; 012F          # 1.1  LATIN CAPITAL LETTER I WITH OGONEK\n012F          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH OGONEK\n0130          ; mapped                 ; 0069 0307     # 1.1  LATIN CAPITAL LETTER I WITH DOT ABOVE\n0131          ; valid                                  # 1.1  LATIN SMALL LETTER DOTLESS I\n0132..0133    ; mapped                 ; 0069 006A     # 1.1  LATIN CAPITAL LIGATURE IJ..LATIN SMALL LIGATURE IJ\n0134          ; mapped                 ; 0135          # 1.1  LATIN CAPITAL LETTER J WITH CIRCUMFLEX\n0135          ; valid                                  # 1.1  LATIN SMALL LETTER J WITH CIRCUMFLEX\n0136          ; mapped                 ; 0137          # 1.1  LATIN CAPITAL LETTER K WITH CEDILLA\n0137..0138    ; valid                                  # 1.1  LATIN SMALL LETTER K WITH CEDILLA..LATIN SMALL LETTER KRA\n0139          ; mapped                 ; 013A          # 1.1  LATIN CAPITAL LETTER L WITH ACUTE\n013A          ; valid                                  # 1.1  LATIN SMALL LETTER L WITH ACUTE\n013B          ; mapped                 ; 013C          # 1.1  LATIN CAPITAL LETTER L WITH CEDILLA\n013C          ; valid                                  # 1.1  LATIN SMALL LETTER L WITH CEDILLA\n013D          ; mapped                 ; 013E          # 1.1  LATIN CAPITAL LETTER L WITH CARON\n013E          ; valid                                  # 1.1  LATIN SMALL LETTER L WITH CARON\n013F..0140    ; mapped                 ; 006C 00B7     # 1.1  LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATIN SMALL LETTER L WITH MIDDLE DOT\n0141          ; mapped                 ; 0142          # 1.1  LATIN CAPITAL LETTER L WITH STROKE\n0142          ; valid                                  # 1.1  LATIN SMALL LETTER L WITH STROKE\n0143          ; mapped                 ; 0144          # 1.1  LATIN CAPITAL LETTER N WITH ACUTE\n0144          ; valid                                  # 1.1  LATIN SMALL LETTER N WITH ACUTE\n0145          ; mapped                 ; 0146          # 1.1  LATIN CAPITAL LETTER N WITH CEDILLA\n0146          ; valid                                  # 1.1  LATIN SMALL LETTER N WITH CEDILLA\n0147          ; mapped                 ; 0148          # 1.1  LATIN CAPITAL LETTER N WITH CARON\n0148          ; valid                                  # 1.1  LATIN SMALL LETTER N WITH CARON\n0149          ; mapped                 ; 02BC 006E     # 1.1  LATIN SMALL LETTER N PRECEDED BY APOSTROPHE\n014A          ; mapped                 ; 014B          # 1.1  LATIN CAPITAL LETTER ENG\n014B          ; valid                                  # 1.1  LATIN SMALL LETTER ENG\n014C          ; mapped                 ; 014D          # 1.1  LATIN CAPITAL LETTER O WITH MACRON\n014D          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH MACRON\n014E          ; mapped                 ; 014F          # 1.1  LATIN CAPITAL LETTER O WITH BREVE\n014F          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH BREVE\n0150          ; mapped                 ; 0151          # 1.1  LATIN CAPITAL LETTER O WITH DOUBLE ACUTE\n0151          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH DOUBLE ACUTE\n0152          ; mapped                 ; 0153          # 1.1  LATIN CAPITAL LIGATURE OE\n0153          ; valid                                  # 1.1  LATIN SMALL LIGATURE OE\n0154          ; mapped                 ; 0155          # 1.1  LATIN CAPITAL LETTER R WITH ACUTE\n0155          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH ACUTE\n0156          ; mapped                 ; 0157          # 1.1  LATIN CAPITAL LETTER R WITH CEDILLA\n0157          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH CEDILLA\n0158          ; mapped                 ; 0159          # 1.1  LATIN CAPITAL LETTER R WITH CARON\n0159          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH CARON\n015A          ; mapped                 ; 015B          # 1.1  LATIN CAPITAL LETTER S WITH ACUTE\n015B          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH ACUTE\n015C          ; mapped                 ; 015D          # 1.1  LATIN CAPITAL LETTER S WITH CIRCUMFLEX\n015D          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH CIRCUMFLEX\n015E          ; mapped                 ; 015F          # 1.1  LATIN CAPITAL LETTER S WITH CEDILLA\n015F          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH CEDILLA\n0160          ; mapped                 ; 0161          # 1.1  LATIN CAPITAL LETTER S WITH CARON\n0161          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH CARON\n0162          ; mapped                 ; 0163          # 1.1  LATIN CAPITAL LETTER T WITH CEDILLA\n0163          ; valid                                  # 1.1  LATIN SMALL LETTER T WITH CEDILLA\n0164          ; mapped                 ; 0165          # 1.1  LATIN CAPITAL LETTER T WITH CARON\n0165          ; valid                                  # 1.1  LATIN SMALL LETTER T WITH CARON\n0166          ; mapped                 ; 0167          # 1.1  LATIN CAPITAL LETTER T WITH STROKE\n0167          ; valid                                  # 1.1  LATIN SMALL LETTER T WITH STROKE\n0168          ; mapped                 ; 0169          # 1.1  LATIN CAPITAL LETTER U WITH TILDE\n0169          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH TILDE\n016A          ; mapped                 ; 016B          # 1.1  LATIN CAPITAL LETTER U WITH MACRON\n016B          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH MACRON\n016C          ; mapped                 ; 016D          # 1.1  LATIN CAPITAL LETTER U WITH BREVE\n016D          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH BREVE\n016E          ; mapped                 ; 016F          # 1.1  LATIN CAPITAL LETTER U WITH RING ABOVE\n016F          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH RING ABOVE\n0170          ; mapped                 ; 0171          # 1.1  LATIN CAPITAL LETTER U WITH DOUBLE ACUTE\n0171          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH DOUBLE ACUTE\n0172          ; mapped                 ; 0173          # 1.1  LATIN CAPITAL LETTER U WITH OGONEK\n0173          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH OGONEK\n0174          ; mapped                 ; 0175          # 1.1  LATIN CAPITAL LETTER W WITH CIRCUMFLEX\n0175          ; valid                                  # 1.1  LATIN SMALL LETTER W WITH CIRCUMFLEX\n0176          ; mapped                 ; 0177          # 1.1  LATIN CAPITAL LETTER Y WITH CIRCUMFLEX\n0177          ; valid                                  # 1.1  LATIN SMALL LETTER Y WITH CIRCUMFLEX\n0178          ; mapped                 ; 00FF          # 1.1  LATIN CAPITAL LETTER Y WITH DIAERESIS\n0179          ; mapped                 ; 017A          # 1.1  LATIN CAPITAL LETTER Z WITH ACUTE\n017A          ; valid                                  # 1.1  LATIN SMALL LETTER Z WITH ACUTE\n017B          ; mapped                 ; 017C          # 1.1  LATIN CAPITAL LETTER Z WITH DOT ABOVE\n017C          ; valid                                  # 1.1  LATIN SMALL LETTER Z WITH DOT ABOVE\n017D          ; mapped                 ; 017E          # 1.1  LATIN CAPITAL LETTER Z WITH CARON\n017E          ; valid                                  # 1.1  LATIN SMALL LETTER Z WITH CARON\n017F          ; mapped                 ; 0073          # 1.1  LATIN SMALL LETTER LONG S\n0180          ; valid                                  # 1.1  LATIN SMALL LETTER B WITH STROKE\n0181          ; mapped                 ; 0253          # 1.1  LATIN CAPITAL LETTER B WITH HOOK\n0182          ; mapped                 ; 0183          # 1.1  LATIN CAPITAL LETTER B WITH TOPBAR\n0183          ; valid                                  # 1.1  LATIN SMALL LETTER B WITH TOPBAR\n0184          ; mapped                 ; 0185          # 1.1  LATIN CAPITAL LETTER TONE SIX\n0185          ; valid                                  # 1.1  LATIN SMALL LETTER TONE SIX\n0186          ; mapped                 ; 0254          # 1.1  LATIN CAPITAL LETTER OPEN O\n0187          ; mapped                 ; 0188          # 1.1  LATIN CAPITAL LETTER C WITH HOOK\n0188          ; valid                                  # 1.1  LATIN SMALL LETTER C WITH HOOK\n0189          ; mapped                 ; 0256          # 1.1  LATIN CAPITAL LETTER AFRICAN D\n018A          ; mapped                 ; 0257          # 1.1  LATIN CAPITAL LETTER D WITH HOOK\n018B          ; mapped                 ; 018C          # 1.1  LATIN CAPITAL LETTER D WITH TOPBAR\n018C..018D    ; valid                                  # 1.1  LATIN SMALL LETTER D WITH TOPBAR..LATIN SMALL LETTER TURNED DELTA\n018E          ; mapped                 ; 01DD          # 1.1  LATIN CAPITAL LETTER REVERSED E\n018F          ; mapped                 ; 0259          # 1.1  LATIN CAPITAL LETTER SCHWA\n0190          ; mapped                 ; 025B          # 1.1  LATIN CAPITAL LETTER OPEN E\n0191          ; mapped                 ; 0192          # 1.1  LATIN CAPITAL LETTER F WITH HOOK\n0192          ; valid                                  # 1.1  LATIN SMALL LETTER F WITH HOOK\n0193          ; mapped                 ; 0260          # 1.1  LATIN CAPITAL LETTER G WITH HOOK\n0194          ; mapped                 ; 0263          # 1.1  LATIN CAPITAL LETTER GAMMA\n0195          ; valid                                  # 1.1  LATIN SMALL LETTER HV\n0196          ; mapped                 ; 0269          # 1.1  LATIN CAPITAL LETTER IOTA\n0197          ; mapped                 ; 0268          # 1.1  LATIN CAPITAL LETTER I WITH STROKE\n0198          ; mapped                 ; 0199          # 1.1  LATIN CAPITAL LETTER K WITH HOOK\n0199..019B    ; valid                                  # 1.1  LATIN SMALL LETTER K WITH HOOK..LATIN SMALL LETTER LAMBDA WITH STROKE\n019C          ; mapped                 ; 026F          # 1.1  LATIN CAPITAL LETTER TURNED M\n019D          ; mapped                 ; 0272          # 1.1  LATIN CAPITAL LETTER N WITH LEFT HOOK\n019E          ; valid                                  # 1.1  LATIN SMALL LETTER N WITH LONG RIGHT LEG\n019F          ; mapped                 ; 0275          # 1.1  LATIN CAPITAL LETTER O WITH MIDDLE TILDE\n01A0          ; mapped                 ; 01A1          # 1.1  LATIN CAPITAL LETTER O WITH HORN\n01A1          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH HORN\n01A2          ; mapped                 ; 01A3          # 1.1  LATIN CAPITAL LETTER OI\n01A3          ; valid                                  # 1.1  LATIN SMALL LETTER OI\n01A4          ; mapped                 ; 01A5          # 1.1  LATIN CAPITAL LETTER P WITH HOOK\n01A5          ; valid                                  # 1.1  LATIN SMALL LETTER P WITH HOOK\n01A6          ; mapped                 ; 0280          # 1.1  LATIN LETTER YR\n01A7          ; mapped                 ; 01A8          # 1.1  LATIN CAPITAL LETTER TONE TWO\n01A8          ; valid                                  # 1.1  LATIN SMALL LETTER TONE TWO\n01A9          ; mapped                 ; 0283          # 1.1  LATIN CAPITAL LETTER ESH\n01AA..01AB    ; valid                                  # 1.1  LATIN LETTER REVERSED ESH LOOP..LATIN SMALL LETTER T WITH PALATAL HOOK\n01AC          ; mapped                 ; 01AD          # 1.1  LATIN CAPITAL LETTER T WITH HOOK\n01AD          ; valid                                  # 1.1  LATIN SMALL LETTER T WITH HOOK\n01AE          ; mapped                 ; 0288          # 1.1  LATIN CAPITAL LETTER T WITH RETROFLEX HOOK\n01AF          ; mapped                 ; 01B0          # 1.1  LATIN CAPITAL LETTER U WITH HORN\n01B0          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH HORN\n01B1          ; mapped                 ; 028A          # 1.1  LATIN CAPITAL LETTER UPSILON\n01B2          ; mapped                 ; 028B          # 1.1  LATIN CAPITAL LETTER V WITH HOOK\n01B3          ; mapped                 ; 01B4          # 1.1  LATIN CAPITAL LETTER Y WITH HOOK\n01B4          ; valid                                  # 1.1  LATIN SMALL LETTER Y WITH HOOK\n01B5          ; mapped                 ; 01B6          # 1.1  LATIN CAPITAL LETTER Z WITH STROKE\n01B6          ; valid                                  # 1.1  LATIN SMALL LETTER Z WITH STROKE\n01B7          ; mapped                 ; 0292          # 1.1  LATIN CAPITAL LETTER EZH\n01B8          ; mapped                 ; 01B9          # 1.1  LATIN CAPITAL LETTER EZH REVERSED\n01B9..01BB    ; valid                                  # 1.1  LATIN SMALL LETTER EZH REVERSED..LATIN LETTER TWO WITH STROKE\n01BC          ; mapped                 ; 01BD          # 1.1  LATIN CAPITAL LETTER TONE FIVE\n01BD..01C3    ; valid                                  # 1.1  LATIN SMALL LETTER TONE FIVE..LATIN LETTER RETROFLEX CLICK\n01C4..01C6    ; mapped                 ; 0064 017E     # 1.1  LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER DZ WITH CARON\n01C7..01C9    ; mapped                 ; 006C 006A     # 1.1  LATIN CAPITAL LETTER LJ..LATIN SMALL LETTER LJ\n01CA..01CC    ; mapped                 ; 006E 006A     # 1.1  LATIN CAPITAL LETTER NJ..LATIN SMALL LETTER NJ\n01CD          ; mapped                 ; 01CE          # 1.1  LATIN CAPITAL LETTER A WITH CARON\n01CE          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH CARON\n01CF          ; mapped                 ; 01D0          # 1.1  LATIN CAPITAL LETTER I WITH CARON\n01D0          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH CARON\n01D1          ; mapped                 ; 01D2          # 1.1  LATIN CAPITAL LETTER O WITH CARON\n01D2          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH CARON\n01D3          ; mapped                 ; 01D4          # 1.1  LATIN CAPITAL LETTER U WITH CARON\n01D4          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH CARON\n01D5          ; mapped                 ; 01D6          # 1.1  LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON\n01D6          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH DIAERESIS AND MACRON\n01D7          ; mapped                 ; 01D8          # 1.1  LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE\n01D8          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE\n01D9          ; mapped                 ; 01DA          # 1.1  LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON\n01DA          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH DIAERESIS AND CARON\n01DB          ; mapped                 ; 01DC          # 1.1  LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE\n01DC..01DD    ; valid                                  # 1.1  LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE..LATIN SMALL LETTER TURNED E\n01DE          ; mapped                 ; 01DF          # 1.1  LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON\n01DF          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH DIAERESIS AND MACRON\n01E0          ; mapped                 ; 01E1          # 1.1  LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON\n01E1          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON\n01E2          ; mapped                 ; 01E3          # 1.1  LATIN CAPITAL LETTER AE WITH MACRON\n01E3          ; valid                                  # 1.1  LATIN SMALL LETTER AE WITH MACRON\n01E4          ; mapped                 ; 01E5          # 1.1  LATIN CAPITAL LETTER G WITH STROKE\n01E5          ; valid                                  # 1.1  LATIN SMALL LETTER G WITH STROKE\n01E6          ; mapped                 ; 01E7          # 1.1  LATIN CAPITAL LETTER G WITH CARON\n01E7          ; valid                                  # 1.1  LATIN SMALL LETTER G WITH CARON\n01E8          ; mapped                 ; 01E9          # 1.1  LATIN CAPITAL LETTER K WITH CARON\n01E9          ; valid                                  # 1.1  LATIN SMALL LETTER K WITH CARON\n01EA          ; mapped                 ; 01EB          # 1.1  LATIN CAPITAL LETTER O WITH OGONEK\n01EB          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH OGONEK\n01EC          ; mapped                 ; 01ED          # 1.1  LATIN CAPITAL LETTER O WITH OGONEK AND MACRON\n01ED          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH OGONEK AND MACRON\n01EE          ; mapped                 ; 01EF          # 1.1  LATIN CAPITAL LETTER EZH WITH CARON\n01EF..01F0    ; valid                                  # 1.1  LATIN SMALL LETTER EZH WITH CARON..LATIN SMALL LETTER J WITH CARON\n01F1..01F3    ; mapped                 ; 0064 007A     # 1.1  LATIN CAPITAL LETTER DZ..LATIN SMALL LETTER DZ\n01F4          ; mapped                 ; 01F5          # 1.1  LATIN CAPITAL LETTER G WITH ACUTE\n01F5          ; valid                                  # 1.1  LATIN SMALL LETTER G WITH ACUTE\n01F6          ; mapped                 ; 0195          # 3.0  LATIN CAPITAL LETTER HWAIR\n01F7          ; mapped                 ; 01BF          # 3.0  LATIN CAPITAL LETTER WYNN\n01F8          ; mapped                 ; 01F9          # 3.0  LATIN CAPITAL LETTER N WITH GRAVE\n01F9          ; valid                                  # 3.0  LATIN SMALL LETTER N WITH GRAVE\n01FA          ; mapped                 ; 01FB          # 1.1  LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE\n01FB          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE\n01FC          ; mapped                 ; 01FD          # 1.1  LATIN CAPITAL LETTER AE WITH ACUTE\n01FD          ; valid                                  # 1.1  LATIN SMALL LETTER AE WITH ACUTE\n01FE          ; mapped                 ; 01FF          # 1.1  LATIN CAPITAL LETTER O WITH STROKE AND ACUTE\n01FF          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH STROKE AND ACUTE\n0200          ; mapped                 ; 0201          # 1.1  LATIN CAPITAL LETTER A WITH DOUBLE GRAVE\n0201          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH DOUBLE GRAVE\n0202          ; mapped                 ; 0203          # 1.1  LATIN CAPITAL LETTER A WITH INVERTED BREVE\n0203          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH INVERTED BREVE\n0204          ; mapped                 ; 0205          # 1.1  LATIN CAPITAL LETTER E WITH DOUBLE GRAVE\n0205          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH DOUBLE GRAVE\n0206          ; mapped                 ; 0207          # 1.1  LATIN CAPITAL LETTER E WITH INVERTED BREVE\n0207          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH INVERTED BREVE\n0208          ; mapped                 ; 0209          # 1.1  LATIN CAPITAL LETTER I WITH DOUBLE GRAVE\n0209          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH DOUBLE GRAVE\n020A          ; mapped                 ; 020B          # 1.1  LATIN CAPITAL LETTER I WITH INVERTED BREVE\n020B          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH INVERTED BREVE\n020C          ; mapped                 ; 020D          # 1.1  LATIN CAPITAL LETTER O WITH DOUBLE GRAVE\n020D          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH DOUBLE GRAVE\n020E          ; mapped                 ; 020F          # 1.1  LATIN CAPITAL LETTER O WITH INVERTED BREVE\n020F          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH INVERTED BREVE\n0210          ; mapped                 ; 0211          # 1.1  LATIN CAPITAL LETTER R WITH DOUBLE GRAVE\n0211          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH DOUBLE GRAVE\n0212          ; mapped                 ; 0213          # 1.1  LATIN CAPITAL LETTER R WITH INVERTED BREVE\n0213          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH INVERTED BREVE\n0214          ; mapped                 ; 0215          # 1.1  LATIN CAPITAL LETTER U WITH DOUBLE GRAVE\n0215          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH DOUBLE GRAVE\n0216          ; mapped                 ; 0217          # 1.1  LATIN CAPITAL LETTER U WITH INVERTED BREVE\n0217          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH INVERTED BREVE\n0218          ; mapped                 ; 0219          # 3.0  LATIN CAPITAL LETTER S WITH COMMA BELOW\n0219          ; valid                                  # 3.0  LATIN SMALL LETTER S WITH COMMA BELOW\n021A          ; mapped                 ; 021B          # 3.0  LATIN CAPITAL LETTER T WITH COMMA BELOW\n021B          ; valid                                  # 3.0  LATIN SMALL LETTER T WITH COMMA BELOW\n021C          ; mapped                 ; 021D          # 3.0  LATIN CAPITAL LETTER YOGH\n021D          ; valid                                  # 3.0  LATIN SMALL LETTER YOGH\n021E          ; mapped                 ; 021F          # 3.0  LATIN CAPITAL LETTER H WITH CARON\n021F          ; valid                                  # 3.0  LATIN SMALL LETTER H WITH CARON\n0220          ; mapped                 ; 019E          # 3.2  LATIN CAPITAL LETTER N WITH LONG RIGHT LEG\n0221          ; valid                                  # 4.0  LATIN SMALL LETTER D WITH CURL\n0222          ; mapped                 ; 0223          # 3.0  LATIN CAPITAL LETTER OU\n0223          ; valid                                  # 3.0  LATIN SMALL LETTER OU\n0224          ; mapped                 ; 0225          # 3.0  LATIN CAPITAL LETTER Z WITH HOOK\n0225          ; valid                                  # 3.0  LATIN SMALL LETTER Z WITH HOOK\n0226          ; mapped                 ; 0227          # 3.0  LATIN CAPITAL LETTER A WITH DOT ABOVE\n0227          ; valid                                  # 3.0  LATIN SMALL LETTER A WITH DOT ABOVE\n0228          ; mapped                 ; 0229          # 3.0  LATIN CAPITAL LETTER E WITH CEDILLA\n0229          ; valid                                  # 3.0  LATIN SMALL LETTER E WITH CEDILLA\n022A          ; mapped                 ; 022B          # 3.0  LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON\n022B          ; valid                                  # 3.0  LATIN SMALL LETTER O WITH DIAERESIS AND MACRON\n022C          ; mapped                 ; 022D          # 3.0  LATIN CAPITAL LETTER O WITH TILDE AND MACRON\n022D          ; valid                                  # 3.0  LATIN SMALL LETTER O WITH TILDE AND MACRON\n022E          ; mapped                 ; 022F          # 3.0  LATIN CAPITAL LETTER O WITH DOT ABOVE\n022F          ; valid                                  # 3.0  LATIN SMALL LETTER O WITH DOT ABOVE\n0230          ; mapped                 ; 0231          # 3.0  LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON\n0231          ; valid                                  # 3.0  LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON\n0232          ; mapped                 ; 0233          # 3.0  LATIN CAPITAL LETTER Y WITH MACRON\n0233          ; valid                                  # 3.0  LATIN SMALL LETTER Y WITH MACRON\n0234..0236    ; valid                                  # 4.0  LATIN SMALL LETTER L WITH CURL..LATIN SMALL LETTER T WITH CURL\n0237..0239    ; valid                                  # 4.1  LATIN SMALL LETTER DOTLESS J..LATIN SMALL LETTER QP DIGRAPH\n023A          ; mapped                 ; 2C65          # 4.1  LATIN CAPITAL LETTER A WITH STROKE\n023B          ; mapped                 ; 023C          # 4.1  LATIN CAPITAL LETTER C WITH STROKE\n023C          ; valid                                  # 4.1  LATIN SMALL LETTER C WITH STROKE\n023D          ; mapped                 ; 019A          # 4.1  LATIN CAPITAL LETTER L WITH BAR\n023E          ; mapped                 ; 2C66          # 4.1  LATIN CAPITAL LETTER T WITH DIAGONAL STROKE\n023F..0240    ; valid                                  # 4.1  LATIN SMALL LETTER S WITH SWASH TAIL..LATIN SMALL LETTER Z WITH SWASH TAIL\n0241          ; mapped                 ; 0242          # 4.1  LATIN CAPITAL LETTER GLOTTAL STOP\n0242          ; valid                                  # 5.0  LATIN SMALL LETTER GLOTTAL STOP\n0243          ; mapped                 ; 0180          # 5.0  LATIN CAPITAL LETTER B WITH STROKE\n0244          ; mapped                 ; 0289          # 5.0  LATIN CAPITAL LETTER U BAR\n0245          ; mapped                 ; 028C          # 5.0  LATIN CAPITAL LETTER TURNED V\n0246          ; mapped                 ; 0247          # 5.0  LATIN CAPITAL LETTER E WITH STROKE\n0247          ; valid                                  # 5.0  LATIN SMALL LETTER E WITH STROKE\n0248          ; mapped                 ; 0249          # 5.0  LATIN CAPITAL LETTER J WITH STROKE\n0249          ; valid                                  # 5.0  LATIN SMALL LETTER J WITH STROKE\n024A          ; mapped                 ; 024B          # 5.0  LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL\n024B          ; valid                                  # 5.0  LATIN SMALL LETTER Q WITH HOOK TAIL\n024C          ; mapped                 ; 024D          # 5.0  LATIN CAPITAL LETTER R WITH STROKE\n024D          ; valid                                  # 5.0  LATIN SMALL LETTER R WITH STROKE\n024E          ; mapped                 ; 024F          # 5.0  LATIN CAPITAL LETTER Y WITH STROKE\n024F          ; valid                                  # 5.0  LATIN SMALL LETTER Y WITH STROKE\n0250..02A8    ; valid                                  # 1.1  LATIN SMALL LETTER TURNED A..LATIN SMALL LETTER TC DIGRAPH WITH CURL\n02A9..02AD    ; valid                                  # 3.0  LATIN SMALL LETTER FENG DIGRAPH..LATIN LETTER BIDENTAL PERCUSSIVE\n02AE..02AF    ; valid                                  # 4.0  LATIN SMALL LETTER TURNED H WITH FISHHOOK..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL\n02B0          ; mapped                 ; 0068          # 1.1  MODIFIER LETTER SMALL H\n02B1          ; mapped                 ; 0266          # 1.1  MODIFIER LETTER SMALL H WITH HOOK\n02B2          ; mapped                 ; 006A          # 1.1  MODIFIER LETTER SMALL J\n02B3          ; mapped                 ; 0072          # 1.1  MODIFIER LETTER SMALL R\n02B4          ; mapped                 ; 0279          # 1.1  MODIFIER LETTER SMALL TURNED R\n02B5          ; mapped                 ; 027B          # 1.1  MODIFIER LETTER SMALL TURNED R WITH HOOK\n02B6          ; mapped                 ; 0281          # 1.1  MODIFIER LETTER SMALL CAPITAL INVERTED R\n02B7          ; mapped                 ; 0077          # 1.1  MODIFIER LETTER SMALL W\n02B8          ; mapped                 ; 0079          # 1.1  MODIFIER LETTER SMALL Y\n02B9..02C1    ; valid                                  # 1.1  MODIFIER LETTER PRIME..MODIFIER LETTER REVERSED GLOTTAL STOP\n02C2..02C5    ; valid                  ;      ; NV8    # 1.1  MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD\n02C6..02D1    ; valid                                  # 1.1  MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON\n02D2..02D7    ; valid                  ;      ; NV8    # 1.1  MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER MINUS SIGN\n02D8          ; disallowed_STD3_mapped ; 0020 0306     # 1.1  BREVE\n02D9          ; disallowed_STD3_mapped ; 0020 0307     # 1.1  DOT ABOVE\n02DA          ; disallowed_STD3_mapped ; 0020 030A     # 1.1  RING ABOVE\n02DB          ; disallowed_STD3_mapped ; 0020 0328     # 1.1  OGONEK\n02DC          ; disallowed_STD3_mapped ; 0020 0303     # 1.1  SMALL TILDE\n02DD          ; disallowed_STD3_mapped ; 0020 030B     # 1.1  DOUBLE ACUTE ACCENT\n02DE          ; valid                  ;      ; NV8    # 1.1  MODIFIER LETTER RHOTIC HOOK\n02DF          ; valid                  ;      ; NV8    # 3.0  MODIFIER LETTER CROSS ACCENT\n02E0          ; mapped                 ; 0263          # 1.1  MODIFIER LETTER SMALL GAMMA\n02E1          ; mapped                 ; 006C          # 1.1  MODIFIER LETTER SMALL L\n02E2          ; mapped                 ; 0073          # 1.1  MODIFIER LETTER SMALL S\n02E3          ; mapped                 ; 0078          # 1.1  MODIFIER LETTER SMALL X\n02E4          ; mapped                 ; 0295          # 1.1  MODIFIER LETTER SMALL REVERSED GLOTTAL STOP\n02E5..02E9    ; valid                  ;      ; NV8    # 1.1  MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER EXTRA-LOW TONE BAR\n02EA..02EB    ; valid                  ;      ; NV8    # 3.0  MODIFIER LETTER YIN DEPARTING TONE MARK..MODIFIER LETTER YANG DEPARTING TONE MARK\n02EC          ; valid                                  # 3.0  MODIFIER LETTER VOICING\n02ED          ; valid                  ;      ; NV8    # 3.0  MODIFIER LETTER UNASPIRATED\n02EE          ; valid                                  # 3.0  MODIFIER LETTER DOUBLE APOSTROPHE\n02EF..02FF    ; valid                  ;      ; NV8    # 4.0  MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW\n0300..033F    ; valid                                  # 1.1  COMBINING GRAVE ACCENT..COMBINING DOUBLE OVERLINE\n0340          ; mapped                 ; 0300          # 1.1  COMBINING GRAVE TONE MARK\n0341          ; mapped                 ; 0301          # 1.1  COMBINING ACUTE TONE MARK\n0342          ; valid                                  # 1.1  COMBINING GREEK PERISPOMENI\n0343          ; mapped                 ; 0313          # 1.1  COMBINING GREEK KORONIS\n0344          ; mapped                 ; 0308 0301     # 1.1  COMBINING GREEK DIALYTIKA TONOS\n0345          ; mapped                 ; 03B9          # 1.1  COMBINING GREEK YPOGEGRAMMENI\n0346..034E    ; valid                                  # 3.0  COMBINING BRIDGE ABOVE..COMBINING UPWARDS ARROW BELOW\n034F          ; ignored                                # 3.2  COMBINING GRAPHEME JOINER\n0350..0357    ; valid                                  # 4.0  COMBINING RIGHT ARROWHEAD ABOVE..COMBINING RIGHT HALF RING ABOVE\n0358..035C    ; valid                                  # 4.1  COMBINING DOT ABOVE RIGHT..COMBINING DOUBLE BREVE BELOW\n035D..035F    ; valid                                  # 4.0  COMBINING DOUBLE BREVE..COMBINING DOUBLE MACRON BELOW\n0360..0361    ; valid                                  # 1.1  COMBINING DOUBLE TILDE..COMBINING DOUBLE INVERTED BREVE\n0362          ; valid                                  # 3.0  COMBINING DOUBLE RIGHTWARDS ARROW BELOW\n0363..036F    ; valid                                  # 3.2  COMBINING LATIN SMALL LETTER A..COMBINING LATIN SMALL LETTER X\n0370          ; mapped                 ; 0371          # 5.1  GREEK CAPITAL LETTER HETA\n0371          ; valid                                  # 5.1  GREEK SMALL LETTER HETA\n0372          ; mapped                 ; 0373          # 5.1  GREEK CAPITAL LETTER ARCHAIC SAMPI\n0373          ; valid                                  # 5.1  GREEK SMALL LETTER ARCHAIC SAMPI\n0374          ; mapped                 ; 02B9          # 1.1  GREEK NUMERAL SIGN\n0375          ; valid                                  # 1.1  GREEK LOWER NUMERAL SIGN\n0376          ; mapped                 ; 0377          # 5.1  GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA\n0377          ; valid                                  # 5.1  GREEK SMALL LETTER PAMPHYLIAN DIGAMMA\n0378..0379    ; disallowed                             # NA   <reserved-0378>..<reserved-0379>\n037A          ; disallowed_STD3_mapped ; 0020 03B9     # 1.1  GREEK YPOGEGRAMMENI\n037B..037D    ; valid                                  # 5.0  GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL\n037E          ; disallowed_STD3_mapped ; 003B          # 1.1  GREEK QUESTION MARK\n037F          ; mapped                 ; 03F3          # 7.0  GREEK CAPITAL LETTER YOT\n0380..0383    ; disallowed                             # NA   <reserved-0380>..<reserved-0383>\n0384          ; disallowed_STD3_mapped ; 0020 0301     # 1.1  GREEK TONOS\n0385          ; disallowed_STD3_mapped ; 0020 0308 0301 #1.1  GREEK DIALYTIKA TONOS\n0386          ; mapped                 ; 03AC          # 1.1  GREEK CAPITAL LETTER ALPHA WITH TONOS\n0387          ; mapped                 ; 00B7          # 1.1  GREEK ANO TELEIA\n0388          ; mapped                 ; 03AD          # 1.1  GREEK CAPITAL LETTER EPSILON WITH TONOS\n0389          ; mapped                 ; 03AE          # 1.1  GREEK CAPITAL LETTER ETA WITH TONOS\n038A          ; mapped                 ; 03AF          # 1.1  GREEK CAPITAL LETTER IOTA WITH TONOS\n038B          ; disallowed                             # NA   <reserved-038B>\n038C          ; mapped                 ; 03CC          # 1.1  GREEK CAPITAL LETTER OMICRON WITH TONOS\n038D          ; disallowed                             # NA   <reserved-038D>\n038E          ; mapped                 ; 03CD          # 1.1  GREEK CAPITAL LETTER UPSILON WITH TONOS\n038F          ; mapped                 ; 03CE          # 1.1  GREEK CAPITAL LETTER OMEGA WITH TONOS\n0390          ; valid                                  # 1.1  GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS\n0391          ; mapped                 ; 03B1          # 1.1  GREEK CAPITAL LETTER ALPHA\n0392          ; mapped                 ; 03B2          # 1.1  GREEK CAPITAL LETTER BETA\n0393          ; mapped                 ; 03B3          # 1.1  GREEK CAPITAL LETTER GAMMA\n0394          ; mapped                 ; 03B4          # 1.1  GREEK CAPITAL LETTER DELTA\n0395          ; mapped                 ; 03B5          # 1.1  GREEK CAPITAL LETTER EPSILON\n0396          ; mapped                 ; 03B6          # 1.1  GREEK CAPITAL LETTER ZETA\n0397          ; mapped                 ; 03B7          # 1.1  GREEK CAPITAL LETTER ETA\n0398          ; mapped                 ; 03B8          # 1.1  GREEK CAPITAL LETTER THETA\n0399          ; mapped                 ; 03B9          # 1.1  GREEK CAPITAL LETTER IOTA\n039A          ; mapped                 ; 03BA          # 1.1  GREEK CAPITAL LETTER KAPPA\n039B          ; mapped                 ; 03BB          # 1.1  GREEK CAPITAL LETTER LAMDA\n039C          ; mapped                 ; 03BC          # 1.1  GREEK CAPITAL LETTER MU\n039D          ; mapped                 ; 03BD          # 1.1  GREEK CAPITAL LETTER NU\n039E          ; mapped                 ; 03BE          # 1.1  GREEK CAPITAL LETTER XI\n039F          ; mapped                 ; 03BF          # 1.1  GREEK CAPITAL LETTER OMICRON\n03A0          ; mapped                 ; 03C0          # 1.1  GREEK CAPITAL LETTER PI\n03A1          ; mapped                 ; 03C1          # 1.1  GREEK CAPITAL LETTER RHO\n03A2          ; disallowed                             # NA   <reserved-03A2>\n03A3          ; mapped                 ; 03C3          # 1.1  GREEK CAPITAL LETTER SIGMA\n03A4          ; mapped                 ; 03C4          # 1.1  GREEK CAPITAL LETTER TAU\n03A5          ; mapped                 ; 03C5          # 1.1  GREEK CAPITAL LETTER UPSILON\n03A6          ; mapped                 ; 03C6          # 1.1  GREEK CAPITAL LETTER PHI\n03A7          ; mapped                 ; 03C7          # 1.1  GREEK CAPITAL LETTER CHI\n03A8          ; mapped                 ; 03C8          # 1.1  GREEK CAPITAL LETTER PSI\n03A9          ; mapped                 ; 03C9          # 1.1  GREEK CAPITAL LETTER OMEGA\n03AA          ; mapped                 ; 03CA          # 1.1  GREEK CAPITAL LETTER IOTA WITH DIALYTIKA\n03AB          ; mapped                 ; 03CB          # 1.1  GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA\n03AC..03C1    ; valid                                  # 1.1  GREEK SMALL LETTER ALPHA WITH TONOS..GREEK SMALL LETTER RHO\n03C2          ; deviation              ; 03C3          # 1.1  GREEK SMALL LETTER FINAL SIGMA\n03C3..03CE    ; valid                                  # 1.1  GREEK SMALL LETTER SIGMA..GREEK SMALL LETTER OMEGA WITH TONOS\n03CF          ; mapped                 ; 03D7          # 5.1  GREEK CAPITAL KAI SYMBOL\n03D0          ; mapped                 ; 03B2          # 1.1  GREEK BETA SYMBOL\n03D1          ; mapped                 ; 03B8          # 1.1  GREEK THETA SYMBOL\n03D2          ; mapped                 ; 03C5          # 1.1  GREEK UPSILON WITH HOOK SYMBOL\n03D3          ; mapped                 ; 03CD          # 1.1  GREEK UPSILON WITH ACUTE AND HOOK SYMBOL\n03D4          ; mapped                 ; 03CB          # 1.1  GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL\n03D5          ; mapped                 ; 03C6          # 1.1  GREEK PHI SYMBOL\n03D6          ; mapped                 ; 03C0          # 1.1  GREEK PI SYMBOL\n03D7          ; valid                                  # 3.0  GREEK KAI SYMBOL\n03D8          ; mapped                 ; 03D9          # 3.2  GREEK LETTER ARCHAIC KOPPA\n03D9          ; valid                                  # 3.2  GREEK SMALL LETTER ARCHAIC KOPPA\n03DA          ; mapped                 ; 03DB          # 1.1  GREEK LETTER STIGMA\n03DB          ; valid                                  # 3.0  GREEK SMALL LETTER STIGMA\n03DC          ; mapped                 ; 03DD          # 1.1  GREEK LETTER DIGAMMA\n03DD          ; valid                                  # 3.0  GREEK SMALL LETTER DIGAMMA\n03DE          ; mapped                 ; 03DF          # 1.1  GREEK LETTER KOPPA\n03DF          ; valid                                  # 3.0  GREEK SMALL LETTER KOPPA\n03E0          ; mapped                 ; 03E1          # 1.1  GREEK LETTER SAMPI\n03E1          ; valid                                  # 3.0  GREEK SMALL LETTER SAMPI\n03E2          ; mapped                 ; 03E3          # 1.1  COPTIC CAPITAL LETTER SHEI\n03E3          ; valid                                  # 1.1  COPTIC SMALL LETTER SHEI\n03E4          ; mapped                 ; 03E5          # 1.1  COPTIC CAPITAL LETTER FEI\n03E5          ; valid                                  # 1.1  COPTIC SMALL LETTER FEI\n03E6          ; mapped                 ; 03E7          # 1.1  COPTIC CAPITAL LETTER KHEI\n03E7          ; valid                                  # 1.1  COPTIC SMALL LETTER KHEI\n03E8          ; mapped                 ; 03E9          # 1.1  COPTIC CAPITAL LETTER HORI\n03E9          ; valid                                  # 1.1  COPTIC SMALL LETTER HORI\n03EA          ; mapped                 ; 03EB          # 1.1  COPTIC CAPITAL LETTER GANGIA\n03EB          ; valid                                  # 1.1  COPTIC SMALL LETTER GANGIA\n03EC          ; mapped                 ; 03ED          # 1.1  COPTIC CAPITAL LETTER SHIMA\n03ED          ; valid                                  # 1.1  COPTIC SMALL LETTER SHIMA\n03EE          ; mapped                 ; 03EF          # 1.1  COPTIC CAPITAL LETTER DEI\n03EF          ; valid                                  # 1.1  COPTIC SMALL LETTER DEI\n03F0          ; mapped                 ; 03BA          # 1.1  GREEK KAPPA SYMBOL\n03F1          ; mapped                 ; 03C1          # 1.1  GREEK RHO SYMBOL\n03F2          ; mapped                 ; 03C3          # 1.1  GREEK LUNATE SIGMA SYMBOL\n03F3          ; valid                                  # 1.1  GREEK LETTER YOT\n03F4          ; mapped                 ; 03B8          # 3.1  GREEK CAPITAL THETA SYMBOL\n03F5          ; mapped                 ; 03B5          # 3.1  GREEK LUNATE EPSILON SYMBOL\n03F6          ; valid                  ;      ; NV8    # 3.2  GREEK REVERSED LUNATE EPSILON SYMBOL\n03F7          ; mapped                 ; 03F8          # 4.0  GREEK CAPITAL LETTER SHO\n03F8          ; valid                                  # 4.0  GREEK SMALL LETTER SHO\n03F9          ; mapped                 ; 03C3          # 4.0  GREEK CAPITAL LUNATE SIGMA SYMBOL\n03FA          ; mapped                 ; 03FB          # 4.0  GREEK CAPITAL LETTER SAN\n03FB          ; valid                                  # 4.0  GREEK SMALL LETTER SAN\n03FC          ; valid                                  # 4.1  GREEK RHO WITH STROKE SYMBOL\n03FD          ; mapped                 ; 037B          # 4.1  GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL\n03FE          ; mapped                 ; 037C          # 4.1  GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL\n03FF          ; mapped                 ; 037D          # 4.1  GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL\n0400          ; mapped                 ; 0450          # 3.0  CYRILLIC CAPITAL LETTER IE WITH GRAVE\n0401          ; mapped                 ; 0451          # 1.1  CYRILLIC CAPITAL LETTER IO\n0402          ; mapped                 ; 0452          # 1.1  CYRILLIC CAPITAL LETTER DJE\n0403          ; mapped                 ; 0453          # 1.1  CYRILLIC CAPITAL LETTER GJE\n0404          ; mapped                 ; 0454          # 1.1  CYRILLIC CAPITAL LETTER UKRAINIAN IE\n0405          ; mapped                 ; 0455          # 1.1  CYRILLIC CAPITAL LETTER DZE\n0406          ; mapped                 ; 0456          # 1.1  CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I\n0407          ; mapped                 ; 0457          # 1.1  CYRILLIC CAPITAL LETTER YI\n0408          ; mapped                 ; 0458          # 1.1  CYRILLIC CAPITAL LETTER JE\n0409          ; mapped                 ; 0459          # 1.1  CYRILLIC CAPITAL LETTER LJE\n040A          ; mapped                 ; 045A          # 1.1  CYRILLIC CAPITAL LETTER NJE\n040B          ; mapped                 ; 045B          # 1.1  CYRILLIC CAPITAL LETTER TSHE\n040C          ; mapped                 ; 045C          # 1.1  CYRILLIC CAPITAL LETTER KJE\n040D          ; mapped                 ; 045D          # 3.0  CYRILLIC CAPITAL LETTER I WITH GRAVE\n040E          ; mapped                 ; 045E          # 1.1  CYRILLIC CAPITAL LETTER SHORT U\n040F          ; mapped                 ; 045F          # 1.1  CYRILLIC CAPITAL LETTER DZHE\n0410          ; mapped                 ; 0430          # 1.1  CYRILLIC CAPITAL LETTER A\n0411          ; mapped                 ; 0431          # 1.1  CYRILLIC CAPITAL LETTER BE\n0412          ; mapped                 ; 0432          # 1.1  CYRILLIC CAPITAL LETTER VE\n0413          ; mapped                 ; 0433          # 1.1  CYRILLIC CAPITAL LETTER GHE\n0414          ; mapped                 ; 0434          # 1.1  CYRILLIC CAPITAL LETTER DE\n0415          ; mapped                 ; 0435          # 1.1  CYRILLIC CAPITAL LETTER IE\n0416          ; mapped                 ; 0436          # 1.1  CYRILLIC CAPITAL LETTER ZHE\n0417          ; mapped                 ; 0437          # 1.1  CYRILLIC CAPITAL LETTER ZE\n0418          ; mapped                 ; 0438          # 1.1  CYRILLIC CAPITAL LETTER I\n0419          ; mapped                 ; 0439          # 1.1  CYRILLIC CAPITAL LETTER SHORT I\n041A          ; mapped                 ; 043A          # 1.1  CYRILLIC CAPITAL LETTER KA\n041B          ; mapped                 ; 043B          # 1.1  CYRILLIC CAPITAL LETTER EL\n041C          ; mapped                 ; 043C          # 1.1  CYRILLIC CAPITAL LETTER EM\n041D          ; mapped                 ; 043D          # 1.1  CYRILLIC CAPITAL LETTER EN\n041E          ; mapped                 ; 043E          # 1.1  CYRILLIC CAPITAL LETTER O\n041F          ; mapped                 ; 043F          # 1.1  CYRILLIC CAPITAL LETTER PE\n0420          ; mapped                 ; 0440          # 1.1  CYRILLIC CAPITAL LETTER ER\n0421          ; mapped                 ; 0441          # 1.1  CYRILLIC CAPITAL LETTER ES\n0422          ; mapped                 ; 0442          # 1.1  CYRILLIC CAPITAL LETTER TE\n0423          ; mapped                 ; 0443          # 1.1  CYRILLIC CAPITAL LETTER U\n0424          ; mapped                 ; 0444          # 1.1  CYRILLIC CAPITAL LETTER EF\n0425          ; mapped                 ; 0445          # 1.1  CYRILLIC CAPITAL LETTER HA\n0426          ; mapped                 ; 0446          # 1.1  CYRILLIC CAPITAL LETTER TSE\n0427          ; mapped                 ; 0447          # 1.1  CYRILLIC CAPITAL LETTER CHE\n0428          ; mapped                 ; 0448          # 1.1  CYRILLIC CAPITAL LETTER SHA\n0429          ; mapped                 ; 0449          # 1.1  CYRILLIC CAPITAL LETTER SHCHA\n042A          ; mapped                 ; 044A          # 1.1  CYRILLIC CAPITAL LETTER HARD SIGN\n042B          ; mapped                 ; 044B          # 1.1  CYRILLIC CAPITAL LETTER YERU\n042C          ; mapped                 ; 044C          # 1.1  CYRILLIC CAPITAL LETTER SOFT SIGN\n042D          ; mapped                 ; 044D          # 1.1  CYRILLIC CAPITAL LETTER E\n042E          ; mapped                 ; 044E          # 1.1  CYRILLIC CAPITAL LETTER YU\n042F          ; mapped                 ; 044F          # 1.1  CYRILLIC CAPITAL LETTER YA\n0430..044F    ; valid                                  # 1.1  CYRILLIC SMALL LETTER A..CYRILLIC SMALL LETTER YA\n0450          ; valid                                  # 3.0  CYRILLIC SMALL LETTER IE WITH GRAVE\n0451..045C    ; valid                                  # 1.1  CYRILLIC SMALL LETTER IO..CYRILLIC SMALL LETTER KJE\n045D          ; valid                                  # 3.0  CYRILLIC SMALL LETTER I WITH GRAVE\n045E..045F    ; valid                                  # 1.1  CYRILLIC SMALL LETTER SHORT U..CYRILLIC SMALL LETTER DZHE\n0460          ; mapped                 ; 0461          # 1.1  CYRILLIC CAPITAL LETTER OMEGA\n0461          ; valid                                  # 1.1  CYRILLIC SMALL LETTER OMEGA\n0462          ; mapped                 ; 0463          # 1.1  CYRILLIC CAPITAL LETTER YAT\n0463          ; valid                                  # 1.1  CYRILLIC SMALL LETTER YAT\n0464          ; mapped                 ; 0465          # 1.1  CYRILLIC CAPITAL LETTER IOTIFIED E\n0465          ; valid                                  # 1.1  CYRILLIC SMALL LETTER IOTIFIED E\n0466          ; mapped                 ; 0467          # 1.1  CYRILLIC CAPITAL LETTER LITTLE YUS\n0467          ; valid                                  # 1.1  CYRILLIC SMALL LETTER LITTLE YUS\n0468          ; mapped                 ; 0469          # 1.1  CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS\n0469          ; valid                                  # 1.1  CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS\n046A          ; mapped                 ; 046B          # 1.1  CYRILLIC CAPITAL LETTER BIG YUS\n046B          ; valid                                  # 1.1  CYRILLIC SMALL LETTER BIG YUS\n046C          ; mapped                 ; 046D          # 1.1  CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS\n046D          ; valid                                  # 1.1  CYRILLIC SMALL LETTER IOTIFIED BIG YUS\n046E          ; mapped                 ; 046F          # 1.1  CYRILLIC CAPITAL LETTER KSI\n046F          ; valid                                  # 1.1  CYRILLIC SMALL LETTER KSI\n0470          ; mapped                 ; 0471          # 1.1  CYRILLIC CAPITAL LETTER PSI\n0471          ; valid                                  # 1.1  CYRILLIC SMALL LETTER PSI\n0472          ; mapped                 ; 0473          # 1.1  CYRILLIC CAPITAL LETTER FITA\n0473          ; valid                                  # 1.1  CYRILLIC SMALL LETTER FITA\n0474          ; mapped                 ; 0475          # 1.1  CYRILLIC CAPITAL LETTER IZHITSA\n0475          ; valid                                  # 1.1  CYRILLIC SMALL LETTER IZHITSA\n0476          ; mapped                 ; 0477          # 1.1  CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT\n0477          ; valid                                  # 1.1  CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT\n0478          ; mapped                 ; 0479          # 1.1  CYRILLIC CAPITAL LETTER UK\n0479          ; valid                                  # 1.1  CYRILLIC SMALL LETTER UK\n047A          ; mapped                 ; 047B          # 1.1  CYRILLIC CAPITAL LETTER ROUND OMEGA\n047B          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ROUND OMEGA\n047C          ; mapped                 ; 047D          # 1.1  CYRILLIC CAPITAL LETTER OMEGA WITH TITLO\n047D          ; valid                                  # 1.1  CYRILLIC SMALL LETTER OMEGA WITH TITLO\n047E          ; mapped                 ; 047F          # 1.1  CYRILLIC CAPITAL LETTER OT\n047F          ; valid                                  # 1.1  CYRILLIC SMALL LETTER OT\n0480          ; mapped                 ; 0481          # 1.1  CYRILLIC CAPITAL LETTER KOPPA\n0481          ; valid                                  # 1.1  CYRILLIC SMALL LETTER KOPPA\n0482          ; valid                  ;      ; NV8    # 1.1  CYRILLIC THOUSANDS SIGN\n0483..0486    ; valid                                  # 1.1  COMBINING CYRILLIC TITLO..COMBINING CYRILLIC PSILI PNEUMATA\n0487          ; valid                                  # 5.1  COMBINING CYRILLIC POKRYTIE\n0488..0489    ; valid                  ;      ; NV8    # 3.0  COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN\n048A          ; mapped                 ; 048B          # 3.2  CYRILLIC CAPITAL LETTER SHORT I WITH TAIL\n048B          ; valid                                  # 3.2  CYRILLIC SMALL LETTER SHORT I WITH TAIL\n048C          ; mapped                 ; 048D          # 3.0  CYRILLIC CAPITAL LETTER SEMISOFT SIGN\n048D          ; valid                                  # 3.0  CYRILLIC SMALL LETTER SEMISOFT SIGN\n048E          ; mapped                 ; 048F          # 3.0  CYRILLIC CAPITAL LETTER ER WITH TICK\n048F          ; valid                                  # 3.0  CYRILLIC SMALL LETTER ER WITH TICK\n0490          ; mapped                 ; 0491          # 1.1  CYRILLIC CAPITAL LETTER GHE WITH UPTURN\n0491          ; valid                                  # 1.1  CYRILLIC SMALL LETTER GHE WITH UPTURN\n0492          ; mapped                 ; 0493          # 1.1  CYRILLIC CAPITAL LETTER GHE WITH STROKE\n0493          ; valid                                  # 1.1  CYRILLIC SMALL LETTER GHE WITH STROKE\n0494          ; mapped                 ; 0495          # 1.1  CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK\n0495          ; valid                                  # 1.1  CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK\n0496          ; mapped                 ; 0497          # 1.1  CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER\n0497          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ZHE WITH DESCENDER\n0498          ; mapped                 ; 0499          # 1.1  CYRILLIC CAPITAL LETTER ZE WITH DESCENDER\n0499          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ZE WITH DESCENDER\n049A          ; mapped                 ; 049B          # 1.1  CYRILLIC CAPITAL LETTER KA WITH DESCENDER\n049B          ; valid                                  # 1.1  CYRILLIC SMALL LETTER KA WITH DESCENDER\n049C          ; mapped                 ; 049D          # 1.1  CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE\n049D          ; valid                                  # 1.1  CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE\n049E          ; mapped                 ; 049F          # 1.1  CYRILLIC CAPITAL LETTER KA WITH STROKE\n049F          ; valid                                  # 1.1  CYRILLIC SMALL LETTER KA WITH STROKE\n04A0          ; mapped                 ; 04A1          # 1.1  CYRILLIC CAPITAL LETTER BASHKIR KA\n04A1          ; valid                                  # 1.1  CYRILLIC SMALL LETTER BASHKIR KA\n04A2          ; mapped                 ; 04A3          # 1.1  CYRILLIC CAPITAL LETTER EN WITH DESCENDER\n04A3          ; valid                                  # 1.1  CYRILLIC SMALL LETTER EN WITH DESCENDER\n04A4          ; mapped                 ; 04A5          # 1.1  CYRILLIC CAPITAL LIGATURE EN GHE\n04A5          ; valid                                  # 1.1  CYRILLIC SMALL LIGATURE EN GHE\n04A6          ; mapped                 ; 04A7          # 1.1  CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK\n04A7          ; valid                                  # 1.1  CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK\n04A8          ; mapped                 ; 04A9          # 1.1  CYRILLIC CAPITAL LETTER ABKHASIAN HA\n04A9          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ABKHASIAN HA\n04AA          ; mapped                 ; 04AB          # 1.1  CYRILLIC CAPITAL LETTER ES WITH DESCENDER\n04AB          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ES WITH DESCENDER\n04AC          ; mapped                 ; 04AD          # 1.1  CYRILLIC CAPITAL LETTER TE WITH DESCENDER\n04AD          ; valid                                  # 1.1  CYRILLIC SMALL LETTER TE WITH DESCENDER\n04AE          ; mapped                 ; 04AF          # 1.1  CYRILLIC CAPITAL LETTER STRAIGHT U\n04AF          ; valid                                  # 1.1  CYRILLIC SMALL LETTER STRAIGHT U\n04B0          ; mapped                 ; 04B1          # 1.1  CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE\n04B1          ; valid                                  # 1.1  CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE\n04B2          ; mapped                 ; 04B3          # 1.1  CYRILLIC CAPITAL LETTER HA WITH DESCENDER\n04B3          ; valid                                  # 1.1  CYRILLIC SMALL LETTER HA WITH DESCENDER\n04B4          ; mapped                 ; 04B5          # 1.1  CYRILLIC CAPITAL LIGATURE TE TSE\n04B5          ; valid                                  # 1.1  CYRILLIC SMALL LIGATURE TE TSE\n04B6          ; mapped                 ; 04B7          # 1.1  CYRILLIC CAPITAL LETTER CHE WITH DESCENDER\n04B7          ; valid                                  # 1.1  CYRILLIC SMALL LETTER CHE WITH DESCENDER\n04B8          ; mapped                 ; 04B9          # 1.1  CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE\n04B9          ; valid                                  # 1.1  CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE\n04BA          ; mapped                 ; 04BB          # 1.1  CYRILLIC CAPITAL LETTER SHHA\n04BB          ; valid                                  # 1.1  CYRILLIC SMALL LETTER SHHA\n04BC          ; mapped                 ; 04BD          # 1.1  CYRILLIC CAPITAL LETTER ABKHASIAN CHE\n04BD          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ABKHASIAN CHE\n04BE          ; mapped                 ; 04BF          # 1.1  CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER\n04BF          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER\n04C0          ; disallowed                             # 1.1  CYRILLIC LETTER PALOCHKA\n04C1          ; mapped                 ; 04C2          # 1.1  CYRILLIC CAPITAL LETTER ZHE WITH BREVE\n04C2          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ZHE WITH BREVE\n04C3          ; mapped                 ; 04C4          # 1.1  CYRILLIC CAPITAL LETTER KA WITH HOOK\n04C4          ; valid                                  # 1.1  CYRILLIC SMALL LETTER KA WITH HOOK\n04C5          ; mapped                 ; 04C6          # 3.2  CYRILLIC CAPITAL LETTER EL WITH TAIL\n04C6          ; valid                                  # 3.2  CYRILLIC SMALL LETTER EL WITH TAIL\n04C7          ; mapped                 ; 04C8          # 1.1  CYRILLIC CAPITAL LETTER EN WITH HOOK\n04C8          ; valid                                  # 1.1  CYRILLIC SMALL LETTER EN WITH HOOK\n04C9          ; mapped                 ; 04CA          # 3.2  CYRILLIC CAPITAL LETTER EN WITH TAIL\n04CA          ; valid                                  # 3.2  CYRILLIC SMALL LETTER EN WITH TAIL\n04CB          ; mapped                 ; 04CC          # 1.1  CYRILLIC CAPITAL LETTER KHAKASSIAN CHE\n04CC          ; valid                                  # 1.1  CYRILLIC SMALL LETTER KHAKASSIAN CHE\n04CD          ; mapped                 ; 04CE          # 3.2  CYRILLIC CAPITAL LETTER EM WITH TAIL\n04CE          ; valid                                  # 3.2  CYRILLIC SMALL LETTER EM WITH TAIL\n04CF          ; valid                                  # 5.0  CYRILLIC SMALL LETTER PALOCHKA\n04D0          ; mapped                 ; 04D1          # 1.1  CYRILLIC CAPITAL LETTER A WITH BREVE\n04D1          ; valid                                  # 1.1  CYRILLIC SMALL LETTER A WITH BREVE\n04D2          ; mapped                 ; 04D3          # 1.1  CYRILLIC CAPITAL LETTER A WITH DIAERESIS\n04D3          ; valid                                  # 1.1  CYRILLIC SMALL LETTER A WITH DIAERESIS\n04D4          ; mapped                 ; 04D5          # 1.1  CYRILLIC CAPITAL LIGATURE A IE\n04D5          ; valid                                  # 1.1  CYRILLIC SMALL LIGATURE A IE\n04D6          ; mapped                 ; 04D7          # 1.1  CYRILLIC CAPITAL LETTER IE WITH BREVE\n04D7          ; valid                                  # 1.1  CYRILLIC SMALL LETTER IE WITH BREVE\n04D8          ; mapped                 ; 04D9          # 1.1  CYRILLIC CAPITAL LETTER SCHWA\n04D9          ; valid                                  # 1.1  CYRILLIC SMALL LETTER SCHWA\n04DA          ; mapped                 ; 04DB          # 1.1  CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS\n04DB          ; valid                                  # 1.1  CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS\n04DC          ; mapped                 ; 04DD          # 1.1  CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS\n04DD          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ZHE WITH DIAERESIS\n04DE          ; mapped                 ; 04DF          # 1.1  CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS\n04DF          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ZE WITH DIAERESIS\n04E0          ; mapped                 ; 04E1          # 1.1  CYRILLIC CAPITAL LETTER ABKHASIAN DZE\n04E1          ; valid                                  # 1.1  CYRILLIC SMALL LETTER ABKHASIAN DZE\n04E2          ; mapped                 ; 04E3          # 1.1  CYRILLIC CAPITAL LETTER I WITH MACRON\n04E3          ; valid                                  # 1.1  CYRILLIC SMALL LETTER I WITH MACRON\n04E4          ; mapped                 ; 04E5          # 1.1  CYRILLIC CAPITAL LETTER I WITH DIAERESIS\n04E5          ; valid                                  # 1.1  CYRILLIC SMALL LETTER I WITH DIAERESIS\n04E6          ; mapped                 ; 04E7          # 1.1  CYRILLIC CAPITAL LETTER O WITH DIAERESIS\n04E7          ; valid                                  # 1.1  CYRILLIC SMALL LETTER O WITH DIAERESIS\n04E8          ; mapped                 ; 04E9          # 1.1  CYRILLIC CAPITAL LETTER BARRED O\n04E9          ; valid                                  # 1.1  CYRILLIC SMALL LETTER BARRED O\n04EA          ; mapped                 ; 04EB          # 1.1  CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS\n04EB          ; valid                                  # 1.1  CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS\n04EC          ; mapped                 ; 04ED          # 3.0  CYRILLIC CAPITAL LETTER E WITH DIAERESIS\n04ED          ; valid                                  # 3.0  CYRILLIC SMALL LETTER E WITH DIAERESIS\n04EE          ; mapped                 ; 04EF          # 1.1  CYRILLIC CAPITAL LETTER U WITH MACRON\n04EF          ; valid                                  # 1.1  CYRILLIC SMALL LETTER U WITH MACRON\n04F0          ; mapped                 ; 04F1          # 1.1  CYRILLIC CAPITAL LETTER U WITH DIAERESIS\n04F1          ; valid                                  # 1.1  CYRILLIC SMALL LETTER U WITH DIAERESIS\n04F2          ; mapped                 ; 04F3          # 1.1  CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE\n04F3          ; valid                                  # 1.1  CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE\n04F4          ; mapped                 ; 04F5          # 1.1  CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS\n04F5          ; valid                                  # 1.1  CYRILLIC SMALL LETTER CHE WITH DIAERESIS\n04F6          ; mapped                 ; 04F7          # 4.1  CYRILLIC CAPITAL LETTER GHE WITH DESCENDER\n04F7          ; valid                                  # 4.1  CYRILLIC SMALL LETTER GHE WITH DESCENDER\n04F8          ; mapped                 ; 04F9          # 1.1  CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS\n04F9          ; valid                                  # 1.1  CYRILLIC SMALL LETTER YERU WITH DIAERESIS\n04FA          ; mapped                 ; 04FB          # 5.0  CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK\n04FB          ; valid                                  # 5.0  CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK\n04FC          ; mapped                 ; 04FD          # 5.0  CYRILLIC CAPITAL LETTER HA WITH HOOK\n04FD          ; valid                                  # 5.0  CYRILLIC SMALL LETTER HA WITH HOOK\n04FE          ; mapped                 ; 04FF          # 5.0  CYRILLIC CAPITAL LETTER HA WITH STROKE\n04FF          ; valid                                  # 5.0  CYRILLIC SMALL LETTER HA WITH STROKE\n0500          ; mapped                 ; 0501          # 3.2  CYRILLIC CAPITAL LETTER KOMI DE\n0501          ; valid                                  # 3.2  CYRILLIC SMALL LETTER KOMI DE\n0502          ; mapped                 ; 0503          # 3.2  CYRILLIC CAPITAL LETTER KOMI DJE\n0503          ; valid                                  # 3.2  CYRILLIC SMALL LETTER KOMI DJE\n0504          ; mapped                 ; 0505          # 3.2  CYRILLIC CAPITAL LETTER KOMI ZJE\n0505          ; valid                                  # 3.2  CYRILLIC SMALL LETTER KOMI ZJE\n0506          ; mapped                 ; 0507          # 3.2  CYRILLIC CAPITAL LETTER KOMI DZJE\n0507          ; valid                                  # 3.2  CYRILLIC SMALL LETTER KOMI DZJE\n0508          ; mapped                 ; 0509          # 3.2  CYRILLIC CAPITAL LETTER KOMI LJE\n0509          ; valid                                  # 3.2  CYRILLIC SMALL LETTER KOMI LJE\n050A          ; mapped                 ; 050B          # 3.2  CYRILLIC CAPITAL LETTER KOMI NJE\n050B          ; valid                                  # 3.2  CYRILLIC SMALL LETTER KOMI NJE\n050C          ; mapped                 ; 050D          # 3.2  CYRILLIC CAPITAL LETTER KOMI SJE\n050D          ; valid                                  # 3.2  CYRILLIC SMALL LETTER KOMI SJE\n050E          ; mapped                 ; 050F          # 3.2  CYRILLIC CAPITAL LETTER KOMI TJE\n050F          ; valid                                  # 3.2  CYRILLIC SMALL LETTER KOMI TJE\n0510          ; mapped                 ; 0511          # 5.0  CYRILLIC CAPITAL LETTER REVERSED ZE\n0511          ; valid                                  # 5.0  CYRILLIC SMALL LETTER REVERSED ZE\n0512          ; mapped                 ; 0513          # 5.0  CYRILLIC CAPITAL LETTER EL WITH HOOK\n0513          ; valid                                  # 5.0  CYRILLIC SMALL LETTER EL WITH HOOK\n0514          ; mapped                 ; 0515          # 5.1  CYRILLIC CAPITAL LETTER LHA\n0515          ; valid                                  # 5.1  CYRILLIC SMALL LETTER LHA\n0516          ; mapped                 ; 0517          # 5.1  CYRILLIC CAPITAL LETTER RHA\n0517          ; valid                                  # 5.1  CYRILLIC SMALL LETTER RHA\n0518          ; mapped                 ; 0519          # 5.1  CYRILLIC CAPITAL LETTER YAE\n0519          ; valid                                  # 5.1  CYRILLIC SMALL LETTER YAE\n051A          ; mapped                 ; 051B          # 5.1  CYRILLIC CAPITAL LETTER QA\n051B          ; valid                                  # 5.1  CYRILLIC SMALL LETTER QA\n051C          ; mapped                 ; 051D          # 5.1  CYRILLIC CAPITAL LETTER WE\n051D          ; valid                                  # 5.1  CYRILLIC SMALL LETTER WE\n051E          ; mapped                 ; 051F          # 5.1  CYRILLIC CAPITAL LETTER ALEUT KA\n051F          ; valid                                  # 5.1  CYRILLIC SMALL LETTER ALEUT KA\n0520          ; mapped                 ; 0521          # 5.1  CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK\n0521          ; valid                                  # 5.1  CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK\n0522          ; mapped                 ; 0523          # 5.1  CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK\n0523          ; valid                                  # 5.1  CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK\n0524          ; mapped                 ; 0525          # 5.2  CYRILLIC CAPITAL LETTER PE WITH DESCENDER\n0525          ; valid                                  # 5.2  CYRILLIC SMALL LETTER PE WITH DESCENDER\n0526          ; mapped                 ; 0527          # 6.0  CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER\n0527          ; valid                                  # 6.0  CYRILLIC SMALL LETTER SHHA WITH DESCENDER\n0528          ; mapped                 ; 0529          # 7.0  CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK\n0529          ; valid                                  # 7.0  CYRILLIC SMALL LETTER EN WITH LEFT HOOK\n052A          ; mapped                 ; 052B          # 7.0  CYRILLIC CAPITAL LETTER DZZHE\n052B          ; valid                                  # 7.0  CYRILLIC SMALL LETTER DZZHE\n052C          ; mapped                 ; 052D          # 7.0  CYRILLIC CAPITAL LETTER DCHE\n052D          ; valid                                  # 7.0  CYRILLIC SMALL LETTER DCHE\n052E          ; mapped                 ; 052F          # 7.0  CYRILLIC CAPITAL LETTER EL WITH DESCENDER\n052F          ; valid                                  # 7.0  CYRILLIC SMALL LETTER EL WITH DESCENDER\n0530          ; disallowed                             # NA   <reserved-0530>\n0531          ; mapped                 ; 0561          # 1.1  ARMENIAN CAPITAL LETTER AYB\n0532          ; mapped                 ; 0562          # 1.1  ARMENIAN CAPITAL LETTER BEN\n0533          ; mapped                 ; 0563          # 1.1  ARMENIAN CAPITAL LETTER GIM\n0534          ; mapped                 ; 0564          # 1.1  ARMENIAN CAPITAL LETTER DA\n0535          ; mapped                 ; 0565          # 1.1  ARMENIAN CAPITAL LETTER ECH\n0536          ; mapped                 ; 0566          # 1.1  ARMENIAN CAPITAL LETTER ZA\n0537          ; mapped                 ; 0567          # 1.1  ARMENIAN CAPITAL LETTER EH\n0538          ; mapped                 ; 0568          # 1.1  ARMENIAN CAPITAL LETTER ET\n0539          ; mapped                 ; 0569          # 1.1  ARMENIAN CAPITAL LETTER TO\n053A          ; mapped                 ; 056A          # 1.1  ARMENIAN CAPITAL LETTER ZHE\n053B          ; mapped                 ; 056B          # 1.1  ARMENIAN CAPITAL LETTER INI\n053C          ; mapped                 ; 056C          # 1.1  ARMENIAN CAPITAL LETTER LIWN\n053D          ; mapped                 ; 056D          # 1.1  ARMENIAN CAPITAL LETTER XEH\n053E          ; mapped                 ; 056E          # 1.1  ARMENIAN CAPITAL LETTER CA\n053F          ; mapped                 ; 056F          # 1.1  ARMENIAN CAPITAL LETTER KEN\n0540          ; mapped                 ; 0570          # 1.1  ARMENIAN CAPITAL LETTER HO\n0541          ; mapped                 ; 0571          # 1.1  ARMENIAN CAPITAL LETTER JA\n0542          ; mapped                 ; 0572          # 1.1  ARMENIAN CAPITAL LETTER GHAD\n0543          ; mapped                 ; 0573          # 1.1  ARMENIAN CAPITAL LETTER CHEH\n0544          ; mapped                 ; 0574          # 1.1  ARMENIAN CAPITAL LETTER MEN\n0545          ; mapped                 ; 0575          # 1.1  ARMENIAN CAPITAL LETTER YI\n0546          ; mapped                 ; 0576          # 1.1  ARMENIAN CAPITAL LETTER NOW\n0547          ; mapped                 ; 0577          # 1.1  ARMENIAN CAPITAL LETTER SHA\n0548          ; mapped                 ; 0578          # 1.1  ARMENIAN CAPITAL LETTER VO\n0549          ; mapped                 ; 0579          # 1.1  ARMENIAN CAPITAL LETTER CHA\n054A          ; mapped                 ; 057A          # 1.1  ARMENIAN CAPITAL LETTER PEH\n054B          ; mapped                 ; 057B          # 1.1  ARMENIAN CAPITAL LETTER JHEH\n054C          ; mapped                 ; 057C          # 1.1  ARMENIAN CAPITAL LETTER RA\n054D          ; mapped                 ; 057D          # 1.1  ARMENIAN CAPITAL LETTER SEH\n054E          ; mapped                 ; 057E          # 1.1  ARMENIAN CAPITAL LETTER VEW\n054F          ; mapped                 ; 057F          # 1.1  ARMENIAN CAPITAL LETTER TIWN\n0550          ; mapped                 ; 0580          # 1.1  ARMENIAN CAPITAL LETTER REH\n0551          ; mapped                 ; 0581          # 1.1  ARMENIAN CAPITAL LETTER CO\n0552          ; mapped                 ; 0582          # 1.1  ARMENIAN CAPITAL LETTER YIWN\n0553          ; mapped                 ; 0583          # 1.1  ARMENIAN CAPITAL LETTER PIWR\n0554          ; mapped                 ; 0584          # 1.1  ARMENIAN CAPITAL LETTER KEH\n0555          ; mapped                 ; 0585          # 1.1  ARMENIAN CAPITAL LETTER OH\n0556          ; mapped                 ; 0586          # 1.1  ARMENIAN CAPITAL LETTER FEH\n0557..0558    ; disallowed                             # NA   <reserved-0557>..<reserved-0558>\n0559          ; valid                                  # 1.1  ARMENIAN MODIFIER LETTER LEFT HALF RING\n055A..055F    ; valid                  ;      ; NV8    # 1.1  ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK\n0560          ; valid                                  # 11.0 ARMENIAN SMALL LETTER TURNED AYB\n0561..0586    ; valid                                  # 1.1  ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LETTER FEH\n0587          ; mapped                 ; 0565 0582     # 1.1  ARMENIAN SMALL LIGATURE ECH YIWN\n0588          ; valid                                  # 11.0 ARMENIAN SMALL LETTER YI WITH STROKE\n0589          ; valid                  ;      ; NV8    # 1.1  ARMENIAN FULL STOP\n058A          ; valid                  ;      ; NV8    # 3.0  ARMENIAN HYPHEN\n058B..058C    ; disallowed                             # NA   <reserved-058B>..<reserved-058C>\n058D..058E    ; valid                  ;      ; NV8    # 7.0  RIGHT-FACING ARMENIAN ETERNITY SIGN..LEFT-FACING ARMENIAN ETERNITY SIGN\n058F          ; valid                  ;      ; NV8    # 6.1  ARMENIAN DRAM SIGN\n0590          ; disallowed                             # NA   <reserved-0590>\n0591..05A1    ; valid                                  # 2.0  HEBREW ACCENT ETNAHTA..HEBREW ACCENT PAZER\n05A2          ; valid                                  # 4.1  HEBREW ACCENT ATNAH HAFUKH\n05A3..05AF    ; valid                                  # 2.0  HEBREW ACCENT MUNAH..HEBREW MARK MASORA CIRCLE\n05B0..05B9    ; valid                                  # 1.1  HEBREW POINT SHEVA..HEBREW POINT HOLAM\n05BA          ; valid                                  # 5.0  HEBREW POINT HOLAM HASER FOR VAV\n05BB..05BD    ; valid                                  # 1.1  HEBREW POINT QUBUTS..HEBREW POINT METEG\n05BE          ; valid                  ;      ; NV8    # 1.1  HEBREW PUNCTUATION MAQAF\n05BF          ; valid                                  # 1.1  HEBREW POINT RAFE\n05C0          ; valid                  ;      ; NV8    # 1.1  HEBREW PUNCTUATION PASEQ\n05C1..05C2    ; valid                                  # 1.1  HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT\n05C3          ; valid                  ;      ; NV8    # 1.1  HEBREW PUNCTUATION SOF PASUQ\n05C4          ; valid                                  # 2.0  HEBREW MARK UPPER DOT\n05C5          ; valid                                  # 4.1  HEBREW MARK LOWER DOT\n05C6          ; valid                  ;      ; NV8    # 4.1  HEBREW PUNCTUATION NUN HAFUKHA\n05C7          ; valid                                  # 4.1  HEBREW POINT QAMATS QATAN\n05C8..05CF    ; disallowed                             # NA   <reserved-05C8>..<reserved-05CF>\n05D0..05EA    ; valid                                  # 1.1  HEBREW LETTER ALEF..HEBREW LETTER TAV\n05EB..05EE    ; disallowed                             # NA   <reserved-05EB>..<reserved-05EE>\n05EF          ; valid                                  # 11.0 HEBREW YOD TRIANGLE\n05F0..05F4    ; valid                                  # 1.1  HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW PUNCTUATION GERSHAYIM\n05F5..05FF    ; disallowed                             # NA   <reserved-05F5>..<reserved-05FF>\n0600..0603    ; disallowed                             # 4.0  ARABIC NUMBER SIGN..ARABIC SIGN SAFHA\n0604          ; disallowed                             # 6.1  ARABIC SIGN SAMVAT\n0605          ; disallowed                             # 7.0  ARABIC NUMBER MARK ABOVE\n0606..060A    ; valid                  ;      ; NV8    # 5.1  ARABIC-INDIC CUBE ROOT..ARABIC-INDIC PER TEN THOUSAND SIGN\n060B          ; valid                  ;      ; NV8    # 4.1  AFGHANI SIGN\n060C          ; valid                  ;      ; NV8    # 1.1  ARABIC COMMA\n060D..060F    ; valid                  ;      ; NV8    # 4.0  ARABIC DATE SEPARATOR..ARABIC SIGN MISRA\n0610..0615    ; valid                                  # 4.0  ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL HIGH TAH\n0616..061A    ; valid                                  # 5.1  ARABIC SMALL HIGH LIGATURE ALEF WITH LAM WITH YEH..ARABIC SMALL KASRA\n061B          ; valid                  ;      ; NV8    # 1.1  ARABIC SEMICOLON\n061C          ; disallowed                             # 6.3  ARABIC LETTER MARK\n061D          ; valid                  ;      ; NV8    # 14.0 ARABIC END OF TEXT MARK\n061E          ; valid                  ;      ; NV8    # 4.1  ARABIC TRIPLE DOT PUNCTUATION MARK\n061F          ; valid                  ;      ; NV8    # 1.1  ARABIC QUESTION MARK\n0620          ; valid                                  # 6.0  ARABIC LETTER KASHMIRI YEH\n0621..063A    ; valid                                  # 1.1  ARABIC LETTER HAMZA..ARABIC LETTER GHAIN\n063B..063F    ; valid                                  # 5.1  ARABIC LETTER KEHEH WITH TWO DOTS ABOVE..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE\n0640          ; valid                  ;      ; NV8    # 1.1  ARABIC TATWEEL\n0641..0652    ; valid                                  # 1.1  ARABIC LETTER FEH..ARABIC SUKUN\n0653..0655    ; valid                                  # 3.0  ARABIC MADDAH ABOVE..ARABIC HAMZA BELOW\n0656..0658    ; valid                                  # 4.0  ARABIC SUBSCRIPT ALEF..ARABIC MARK NOON GHUNNA\n0659..065E    ; valid                                  # 4.1  ARABIC ZWARAKAY..ARABIC FATHA WITH TWO DOTS\n065F          ; valid                                  # 6.0  ARABIC WAVY HAMZA BELOW\n0660..0669    ; valid                                  # 1.1  ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE\n066A..066D    ; valid                  ;      ; NV8    # 1.1  ARABIC PERCENT SIGN..ARABIC FIVE POINTED STAR\n066E..066F    ; valid                                  # 3.2  ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF\n0670..0674    ; valid                                  # 1.1  ARABIC LETTER SUPERSCRIPT ALEF..ARABIC LETTER HIGH HAMZA\n0675          ; mapped                 ; 0627 0674     # 1.1  ARABIC LETTER HIGH HAMZA ALEF\n0676          ; mapped                 ; 0648 0674     # 1.1  ARABIC LETTER HIGH HAMZA WAW\n0677          ; mapped                 ; 06C7 0674     # 1.1  ARABIC LETTER U WITH HAMZA ABOVE\n0678          ; mapped                 ; 064A 0674     # 1.1  ARABIC LETTER HIGH HAMZA YEH\n0679..06B7    ; valid                                  # 1.1  ARABIC LETTER TTEH..ARABIC LETTER LAM WITH THREE DOTS ABOVE\n06B8..06B9    ; valid                                  # 3.0  ARABIC LETTER LAM WITH THREE DOTS BELOW..ARABIC LETTER NOON WITH DOT BELOW\n06BA..06BE    ; valid                                  # 1.1  ARABIC LETTER NOON GHUNNA..ARABIC LETTER HEH DOACHASHMEE\n06BF          ; valid                                  # 3.0  ARABIC LETTER TCHEH WITH DOT ABOVE\n06C0..06CE    ; valid                                  # 1.1  ARABIC LETTER HEH WITH YEH ABOVE..ARABIC LETTER YEH WITH SMALL V\n06CF          ; valid                                  # 3.0  ARABIC LETTER WAW WITH DOT ABOVE\n06D0..06D3    ; valid                                  # 1.1  ARABIC LETTER E..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE\n06D4          ; valid                  ;      ; NV8    # 1.1  ARABIC FULL STOP\n06D5..06DC    ; valid                                  # 1.1  ARABIC LETTER AE..ARABIC SMALL HIGH SEEN\n06DD          ; disallowed                             # 1.1  ARABIC END OF AYAH\n06DE          ; valid                  ;      ; NV8    # 1.1  ARABIC START OF RUB EL HIZB\n06DF..06E8    ; valid                                  # 1.1  ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH NOON\n06E9          ; valid                  ;      ; NV8    # 1.1  ARABIC PLACE OF SAJDAH\n06EA..06ED    ; valid                                  # 1.1  ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM\n06EE..06EF    ; valid                                  # 4.0  ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V\n06F0..06F9    ; valid                                  # 1.1  EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE\n06FA..06FE    ; valid                                  # 3.0  ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC SIGN SINDHI POSTPOSITION MEN\n06FF          ; valid                                  # 4.0  ARABIC LETTER HEH WITH INVERTED V\n0700..070D    ; valid                  ;      ; NV8    # 3.0  SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS\n070E          ; disallowed                             # NA   <reserved-070E>\n070F          ; disallowed                             # 3.0  SYRIAC ABBREVIATION MARK\n0710..072C    ; valid                                  # 3.0  SYRIAC LETTER ALAPH..SYRIAC LETTER TAW\n072D..072F    ; valid                                  # 4.0  SYRIAC LETTER PERSIAN BHETH..SYRIAC LETTER PERSIAN DHALATH\n0730..074A    ; valid                                  # 3.0  SYRIAC PTHAHA ABOVE..SYRIAC BARREKH\n074B..074C    ; disallowed                             # NA   <reserved-074B>..<reserved-074C>\n074D..074F    ; valid                                  # 4.0  SYRIAC LETTER SOGDIAN ZHAIN..SYRIAC LETTER SOGDIAN FE\n0750..076D    ; valid                                  # 4.1  ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW..ARABIC LETTER SEEN WITH TWO DOTS VERTICALLY ABOVE\n076E..077F    ; valid                                  # 5.1  ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH BELOW..ARABIC LETTER KAF WITH TWO DOTS ABOVE\n0780..07B0    ; valid                                  # 3.0  THAANA LETTER HAA..THAANA SUKUN\n07B1          ; valid                                  # 3.2  THAANA LETTER NAA\n07B2..07BF    ; disallowed                             # NA   <reserved-07B2>..<reserved-07BF>\n07C0..07F5    ; valid                                  # 5.0  NKO DIGIT ZERO..NKO LOW TONE APOSTROPHE\n07F6..07FA    ; valid                  ;      ; NV8    # 5.0  NKO SYMBOL OO DENNEN..NKO LAJANYALAN\n07FB..07FC    ; disallowed                             # NA   <reserved-07FB>..<reserved-07FC>\n07FD          ; valid                                  # 11.0 NKO DANTAYALAN\n07FE..07FF    ; valid                  ;      ; NV8    # 11.0 NKO DOROME SIGN..NKO TAMAN SIGN\n0800..082D    ; valid                                  # 5.2  SAMARITAN LETTER ALAF..SAMARITAN MARK NEQUDAA\n082E..082F    ; disallowed                             # NA   <reserved-082E>..<reserved-082F>\n0830..083E    ; valid                  ;      ; NV8    # 5.2  SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUNCTUATION ANNAAU\n083F          ; disallowed                             # NA   <reserved-083F>\n0840..085B    ; valid                                  # 6.0  MANDAIC LETTER HALQA..MANDAIC GEMINATION MARK\n085C..085D    ; disallowed                             # NA   <reserved-085C>..<reserved-085D>\n085E          ; valid                  ;      ; NV8    # 6.0  MANDAIC PUNCTUATION\n085F          ; disallowed                             # NA   <reserved-085F>\n0860..086A    ; valid                                  # 10.0 SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA\n086B..086F    ; disallowed                             # NA   <reserved-086B>..<reserved-086F>\n0870..0887    ; valid                                  # 14.0 ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT\n0888          ; valid                  ;      ; NV8    # 14.0 ARABIC RAISED ROUND DOT\n0889..088E    ; valid                                  # 14.0 ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL\n088F          ; disallowed                             # NA   <reserved-088F>\n0890..0891    ; disallowed                             # 14.0 ARABIC POUND MARK ABOVE..ARABIC PIASTRE MARK ABOVE\n0892..0897    ; disallowed                             # NA   <reserved-0892>..<reserved-0897>\n0898..089F    ; valid                                  # 14.0 ARABIC SMALL HIGH WORD AL-JUZ..ARABIC HALF MADDA OVER MADDA\n08A0          ; valid                                  # 6.1  ARABIC LETTER BEH WITH SMALL V BELOW\n08A1          ; valid                                  # 7.0  ARABIC LETTER BEH WITH HAMZA ABOVE\n08A2..08AC    ; valid                                  # 6.1  ARABIC LETTER JEEM WITH TWO DOTS ABOVE..ARABIC LETTER ROHINGYA YEH\n08AD..08B2    ; valid                                  # 7.0  ARABIC LETTER LOW ALEF..ARABIC LETTER ZAIN WITH INVERTED V ABOVE\n08B3..08B4    ; valid                                  # 8.0  ARABIC LETTER AIN WITH THREE DOTS BELOW..ARABIC LETTER KAF WITH DOT BELOW\n08B5          ; valid                                  # 14.0 ARABIC LETTER QAF WITH DOT BELOW AND NO DOTS ABOVE\n08B6..08BD    ; valid                                  # 9.0  ARABIC LETTER BEH WITH SMALL MEEM ABOVE..ARABIC LETTER AFRICAN NOON\n08BE..08C7    ; valid                                  # 13.0 ARABIC LETTER PEH WITH SMALL V..ARABIC LETTER LAM WITH SMALL ARABIC LETTER TAH ABOVE\n08C8..08D2    ; valid                                  # 14.0 ARABIC LETTER GRAF..ARABIC LARGE ROUND DOT INSIDE CIRCLE BELOW\n08D3          ; valid                                  # 11.0 ARABIC SMALL LOW WAW\n08D4..08E1    ; valid                                  # 9.0  ARABIC SMALL HIGH WORD AR-RUB..ARABIC SMALL HIGH SIGN SAFHA\n08E2          ; disallowed                             # 9.0  ARABIC DISPUTED END OF AYAH\n08E3          ; valid                                  # 8.0  ARABIC TURNED DAMMA BELOW\n08E4..08FE    ; valid                                  # 6.1  ARABIC CURLY FATHA..ARABIC DAMMA WITH DOT\n08FF          ; valid                                  # 7.0  ARABIC MARK SIDEWAYS NOON GHUNNA\n0900          ; valid                                  # 5.2  DEVANAGARI SIGN INVERTED CANDRABINDU\n0901..0903    ; valid                                  # 1.1  DEVANAGARI SIGN CANDRABINDU..DEVANAGARI SIGN VISARGA\n0904          ; valid                                  # 4.0  DEVANAGARI LETTER SHORT A\n0905..0939    ; valid                                  # 1.1  DEVANAGARI LETTER A..DEVANAGARI LETTER HA\n093A..093B    ; valid                                  # 6.0  DEVANAGARI VOWEL SIGN OE..DEVANAGARI VOWEL SIGN OOE\n093C..094D    ; valid                                  # 1.1  DEVANAGARI SIGN NUKTA..DEVANAGARI SIGN VIRAMA\n094E          ; valid                                  # 5.2  DEVANAGARI VOWEL SIGN PRISHTHAMATRA E\n094F          ; valid                                  # 6.0  DEVANAGARI VOWEL SIGN AW\n0950..0954    ; valid                                  # 1.1  DEVANAGARI OM..DEVANAGARI ACUTE ACCENT\n0955          ; valid                                  # 5.2  DEVANAGARI VOWEL SIGN CANDRA LONG E\n0956..0957    ; valid                                  # 6.0  DEVANAGARI VOWEL SIGN UE..DEVANAGARI VOWEL SIGN UUE\n0958          ; mapped                 ; 0915 093C     # 1.1  DEVANAGARI LETTER QA\n0959          ; mapped                 ; 0916 093C     # 1.1  DEVANAGARI LETTER KHHA\n095A          ; mapped                 ; 0917 093C     # 1.1  DEVANAGARI LETTER GHHA\n095B          ; mapped                 ; 091C 093C     # 1.1  DEVANAGARI LETTER ZA\n095C          ; mapped                 ; 0921 093C     # 1.1  DEVANAGARI LETTER DDDHA\n095D          ; mapped                 ; 0922 093C     # 1.1  DEVANAGARI LETTER RHA\n095E          ; mapped                 ; 092B 093C     # 1.1  DEVANAGARI LETTER FA\n095F          ; mapped                 ; 092F 093C     # 1.1  DEVANAGARI LETTER YYA\n0960..0963    ; valid                                  # 1.1  DEVANAGARI LETTER VOCALIC RR..DEVANAGARI VOWEL SIGN VOCALIC LL\n0964..0965    ; valid                  ;      ; NV8    # 1.1  DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA\n0966..096F    ; valid                                  # 1.1  DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE\n0970          ; valid                  ;      ; NV8    # 1.1  DEVANAGARI ABBREVIATION SIGN\n0971..0972    ; valid                                  # 5.1  DEVANAGARI SIGN HIGH SPACING DOT..DEVANAGARI LETTER CANDRA A\n0973..0977    ; valid                                  # 6.0  DEVANAGARI LETTER OE..DEVANAGARI LETTER UUE\n0978          ; valid                                  # 7.0  DEVANAGARI LETTER MARWARI DDA\n0979..097A    ; valid                                  # 5.2  DEVANAGARI LETTER ZHA..DEVANAGARI LETTER HEAVY YA\n097B..097C    ; valid                                  # 5.0  DEVANAGARI LETTER GGA..DEVANAGARI LETTER JJA\n097D          ; valid                                  # 4.1  DEVANAGARI LETTER GLOTTAL STOP\n097E..097F    ; valid                                  # 5.0  DEVANAGARI LETTER DDDA..DEVANAGARI LETTER BBA\n0980          ; valid                                  # 7.0  BENGALI ANJI\n0981..0983    ; valid                                  # 1.1  BENGALI SIGN CANDRABINDU..BENGALI SIGN VISARGA\n0984          ; disallowed                             # NA   <reserved-0984>\n0985..098C    ; valid                                  # 1.1  BENGALI LETTER A..BENGALI LETTER VOCALIC L\n098D..098E    ; disallowed                             # NA   <reserved-098D>..<reserved-098E>\n098F..0990    ; valid                                  # 1.1  BENGALI LETTER E..BENGALI LETTER AI\n0991..0992    ; disallowed                             # NA   <reserved-0991>..<reserved-0992>\n0993..09A8    ; valid                                  # 1.1  BENGALI LETTER O..BENGALI LETTER NA\n09A9          ; disallowed                             # NA   <reserved-09A9>\n09AA..09B0    ; valid                                  # 1.1  BENGALI LETTER PA..BENGALI LETTER RA\n09B1          ; disallowed                             # NA   <reserved-09B1>\n09B2          ; valid                                  # 1.1  BENGALI LETTER LA\n09B3..09B5    ; disallowed                             # NA   <reserved-09B3>..<reserved-09B5>\n09B6..09B9    ; valid                                  # 1.1  BENGALI LETTER SHA..BENGALI LETTER HA\n09BA..09BB    ; disallowed                             # NA   <reserved-09BA>..<reserved-09BB>\n09BC          ; valid                                  # 1.1  BENGALI SIGN NUKTA\n09BD          ; valid                                  # 4.0  BENGALI SIGN AVAGRAHA\n09BE..09C4    ; valid                                  # 1.1  BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN VOCALIC RR\n09C5..09C6    ; disallowed                             # NA   <reserved-09C5>..<reserved-09C6>\n09C7..09C8    ; valid                                  # 1.1  BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI\n09C9..09CA    ; disallowed                             # NA   <reserved-09C9>..<reserved-09CA>\n09CB..09CD    ; valid                                  # 1.1  BENGALI VOWEL SIGN O..BENGALI SIGN VIRAMA\n09CE          ; valid                                  # 4.1  BENGALI LETTER KHANDA TA\n09CF..09D6    ; disallowed                             # NA   <reserved-09CF>..<reserved-09D6>\n09D7          ; valid                                  # 1.1  BENGALI AU LENGTH MARK\n09D8..09DB    ; disallowed                             # NA   <reserved-09D8>..<reserved-09DB>\n09DC          ; mapped                 ; 09A1 09BC     # 1.1  BENGALI LETTER RRA\n09DD          ; mapped                 ; 09A2 09BC     # 1.1  BENGALI LETTER RHA\n09DE          ; disallowed                             # NA   <reserved-09DE>\n09DF          ; mapped                 ; 09AF 09BC     # 1.1  BENGALI LETTER YYA\n09E0..09E3    ; valid                                  # 1.1  BENGALI LETTER VOCALIC RR..BENGALI VOWEL SIGN VOCALIC LL\n09E4..09E5    ; disallowed                             # NA   <reserved-09E4>..<reserved-09E5>\n09E6..09F1    ; valid                                  # 1.1  BENGALI DIGIT ZERO..BENGALI LETTER RA WITH LOWER DIAGONAL\n09F2..09FA    ; valid                  ;      ; NV8    # 1.1  BENGALI RUPEE MARK..BENGALI ISSHAR\n09FB          ; valid                  ;      ; NV8    # 5.2  BENGALI GANDA MARK\n09FC          ; valid                                  # 10.0 BENGALI LETTER VEDIC ANUSVARA\n09FD          ; valid                  ;      ; NV8    # 10.0 BENGALI ABBREVIATION SIGN\n09FE          ; valid                                  # 11.0 BENGALI SANDHI MARK\n09FF..0A00    ; disallowed                             # NA   <reserved-09FF>..<reserved-0A00>\n0A01          ; valid                                  # 4.0  GURMUKHI SIGN ADAK BINDI\n0A02          ; valid                                  # 1.1  GURMUKHI SIGN BINDI\n0A03          ; valid                                  # 4.0  GURMUKHI SIGN VISARGA\n0A04          ; disallowed                             # NA   <reserved-0A04>\n0A05..0A0A    ; valid                                  # 1.1  GURMUKHI LETTER A..GURMUKHI LETTER UU\n0A0B..0A0E    ; disallowed                             # NA   <reserved-0A0B>..<reserved-0A0E>\n0A0F..0A10    ; valid                                  # 1.1  GURMUKHI LETTER EE..GURMUKHI LETTER AI\n0A11..0A12    ; disallowed                             # NA   <reserved-0A11>..<reserved-0A12>\n0A13..0A28    ; valid                                  # 1.1  GURMUKHI LETTER OO..GURMUKHI LETTER NA\n0A29          ; disallowed                             # NA   <reserved-0A29>\n0A2A..0A30    ; valid                                  # 1.1  GURMUKHI LETTER PA..GURMUKHI LETTER RA\n0A31          ; disallowed                             # NA   <reserved-0A31>\n0A32          ; valid                                  # 1.1  GURMUKHI LETTER LA\n0A33          ; mapped                 ; 0A32 0A3C     # 1.1  GURMUKHI LETTER LLA\n0A34          ; disallowed                             # NA   <reserved-0A34>\n0A35          ; valid                                  # 1.1  GURMUKHI LETTER VA\n0A36          ; mapped                 ; 0A38 0A3C     # 1.1  GURMUKHI LETTER SHA\n0A37          ; disallowed                             # NA   <reserved-0A37>\n0A38..0A39    ; valid                                  # 1.1  GURMUKHI LETTER SA..GURMUKHI LETTER HA\n0A3A..0A3B    ; disallowed                             # NA   <reserved-0A3A>..<reserved-0A3B>\n0A3C          ; valid                                  # 1.1  GURMUKHI SIGN NUKTA\n0A3D          ; disallowed                             # NA   <reserved-0A3D>\n0A3E..0A42    ; valid                                  # 1.1  GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN UU\n0A43..0A46    ; disallowed                             # NA   <reserved-0A43>..<reserved-0A46>\n0A47..0A48    ; valid                                  # 1.1  GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI\n0A49..0A4A    ; disallowed                             # NA   <reserved-0A49>..<reserved-0A4A>\n0A4B..0A4D    ; valid                                  # 1.1  GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA\n0A4E..0A50    ; disallowed                             # NA   <reserved-0A4E>..<reserved-0A50>\n0A51          ; valid                                  # 5.1  GURMUKHI SIGN UDAAT\n0A52..0A58    ; disallowed                             # NA   <reserved-0A52>..<reserved-0A58>\n0A59          ; mapped                 ; 0A16 0A3C     # 1.1  GURMUKHI LETTER KHHA\n0A5A          ; mapped                 ; 0A17 0A3C     # 1.1  GURMUKHI LETTER GHHA\n0A5B          ; mapped                 ; 0A1C 0A3C     # 1.1  GURMUKHI LETTER ZA\n0A5C          ; valid                                  # 1.1  GURMUKHI LETTER RRA\n0A5D          ; disallowed                             # NA   <reserved-0A5D>\n0A5E          ; mapped                 ; 0A2B 0A3C     # 1.1  GURMUKHI LETTER FA\n0A5F..0A65    ; disallowed                             # NA   <reserved-0A5F>..<reserved-0A65>\n0A66..0A74    ; valid                                  # 1.1  GURMUKHI DIGIT ZERO..GURMUKHI EK ONKAR\n0A75          ; valid                                  # 5.1  GURMUKHI SIGN YAKASH\n0A76          ; valid                  ;      ; NV8    # 11.0 GURMUKHI ABBREVIATION SIGN\n0A77..0A80    ; disallowed                             # NA   <reserved-0A77>..<reserved-0A80>\n0A81..0A83    ; valid                                  # 1.1  GUJARATI SIGN CANDRABINDU..GUJARATI SIGN VISARGA\n0A84          ; disallowed                             # NA   <reserved-0A84>\n0A85..0A8B    ; valid                                  # 1.1  GUJARATI LETTER A..GUJARATI LETTER VOCALIC R\n0A8C          ; valid                                  # 4.0  GUJARATI LETTER VOCALIC L\n0A8D          ; valid                                  # 1.1  GUJARATI VOWEL CANDRA E\n0A8E          ; disallowed                             # NA   <reserved-0A8E>\n0A8F..0A91    ; valid                                  # 1.1  GUJARATI LETTER E..GUJARATI VOWEL CANDRA O\n0A92          ; disallowed                             # NA   <reserved-0A92>\n0A93..0AA8    ; valid                                  # 1.1  GUJARATI LETTER O..GUJARATI LETTER NA\n0AA9          ; disallowed                             # NA   <reserved-0AA9>\n0AAA..0AB0    ; valid                                  # 1.1  GUJARATI LETTER PA..GUJARATI LETTER RA\n0AB1          ; disallowed                             # NA   <reserved-0AB1>\n0AB2..0AB3    ; valid                                  # 1.1  GUJARATI LETTER LA..GUJARATI LETTER LLA\n0AB4          ; disallowed                             # NA   <reserved-0AB4>\n0AB5..0AB9    ; valid                                  # 1.1  GUJARATI LETTER VA..GUJARATI LETTER HA\n0ABA..0ABB    ; disallowed                             # NA   <reserved-0ABA>..<reserved-0ABB>\n0ABC..0AC5    ; valid                                  # 1.1  GUJARATI SIGN NUKTA..GUJARATI VOWEL SIGN CANDRA E\n0AC6          ; disallowed                             # NA   <reserved-0AC6>\n0AC7..0AC9    ; valid                                  # 1.1  GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN CANDRA O\n0ACA          ; disallowed                             # NA   <reserved-0ACA>\n0ACB..0ACD    ; valid                                  # 1.1  GUJARATI VOWEL SIGN O..GUJARATI SIGN VIRAMA\n0ACE..0ACF    ; disallowed                             # NA   <reserved-0ACE>..<reserved-0ACF>\n0AD0          ; valid                                  # 1.1  GUJARATI OM\n0AD1..0ADF    ; disallowed                             # NA   <reserved-0AD1>..<reserved-0ADF>\n0AE0          ; valid                                  # 1.1  GUJARATI LETTER VOCALIC RR\n0AE1..0AE3    ; valid                                  # 4.0  GUJARATI LETTER VOCALIC LL..GUJARATI VOWEL SIGN VOCALIC LL\n0AE4..0AE5    ; disallowed                             # NA   <reserved-0AE4>..<reserved-0AE5>\n0AE6..0AEF    ; valid                                  # 1.1  GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE\n0AF0          ; valid                  ;      ; NV8    # 6.1  GUJARATI ABBREVIATION SIGN\n0AF1          ; valid                  ;      ; NV8    # 4.0  GUJARATI RUPEE SIGN\n0AF2..0AF8    ; disallowed                             # NA   <reserved-0AF2>..<reserved-0AF8>\n0AF9          ; valid                                  # 8.0  GUJARATI LETTER ZHA\n0AFA..0AFF    ; valid                                  # 10.0 GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE\n0B00          ; disallowed                             # NA   <reserved-0B00>\n0B01..0B03    ; valid                                  # 1.1  ORIYA SIGN CANDRABINDU..ORIYA SIGN VISARGA\n0B04          ; disallowed                             # NA   <reserved-0B04>\n0B05..0B0C    ; valid                                  # 1.1  ORIYA LETTER A..ORIYA LETTER VOCALIC L\n0B0D..0B0E    ; disallowed                             # NA   <reserved-0B0D>..<reserved-0B0E>\n0B0F..0B10    ; valid                                  # 1.1  ORIYA LETTER E..ORIYA LETTER AI\n0B11..0B12    ; disallowed                             # NA   <reserved-0B11>..<reserved-0B12>\n0B13..0B28    ; valid                                  # 1.1  ORIYA LETTER O..ORIYA LETTER NA\n0B29          ; disallowed                             # NA   <reserved-0B29>\n0B2A..0B30    ; valid                                  # 1.1  ORIYA LETTER PA..ORIYA LETTER RA\n0B31          ; disallowed                             # NA   <reserved-0B31>\n0B32..0B33    ; valid                                  # 1.1  ORIYA LETTER LA..ORIYA LETTER LLA\n0B34          ; disallowed                             # NA   <reserved-0B34>\n0B35          ; valid                                  # 4.0  ORIYA LETTER VA\n0B36..0B39    ; valid                                  # 1.1  ORIYA LETTER SHA..ORIYA LETTER HA\n0B3A..0B3B    ; disallowed                             # NA   <reserved-0B3A>..<reserved-0B3B>\n0B3C..0B43    ; valid                                  # 1.1  ORIYA SIGN NUKTA..ORIYA VOWEL SIGN VOCALIC R\n0B44          ; valid                                  # 5.1  ORIYA VOWEL SIGN VOCALIC RR\n0B45..0B46    ; disallowed                             # NA   <reserved-0B45>..<reserved-0B46>\n0B47..0B48    ; valid                                  # 1.1  ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI\n0B49..0B4A    ; disallowed                             # NA   <reserved-0B49>..<reserved-0B4A>\n0B4B..0B4D    ; valid                                  # 1.1  ORIYA VOWEL SIGN O..ORIYA SIGN VIRAMA\n0B4E..0B54    ; disallowed                             # NA   <reserved-0B4E>..<reserved-0B54>\n0B55          ; valid                                  # 13.0 ORIYA SIGN OVERLINE\n0B56..0B57    ; valid                                  # 1.1  ORIYA AI LENGTH MARK..ORIYA AU LENGTH MARK\n0B58..0B5B    ; disallowed                             # NA   <reserved-0B58>..<reserved-0B5B>\n0B5C          ; mapped                 ; 0B21 0B3C     # 1.1  ORIYA LETTER RRA\n0B5D          ; mapped                 ; 0B22 0B3C     # 1.1  ORIYA LETTER RHA\n0B5E          ; disallowed                             # NA   <reserved-0B5E>\n0B5F..0B61    ; valid                                  # 1.1  ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL\n0B62..0B63    ; valid                                  # 5.1  ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL\n0B64..0B65    ; disallowed                             # NA   <reserved-0B64>..<reserved-0B65>\n0B66..0B6F    ; valid                                  # 1.1  ORIYA DIGIT ZERO..ORIYA DIGIT NINE\n0B70          ; valid                  ;      ; NV8    # 1.1  ORIYA ISSHAR\n0B71          ; valid                                  # 4.0  ORIYA LETTER WA\n0B72..0B77    ; valid                  ;      ; NV8    # 6.0  ORIYA FRACTION ONE QUARTER..ORIYA FRACTION THREE SIXTEENTHS\n0B78..0B81    ; disallowed                             # NA   <reserved-0B78>..<reserved-0B81>\n0B82..0B83    ; valid                                  # 1.1  TAMIL SIGN ANUSVARA..TAMIL SIGN VISARGA\n0B84          ; disallowed                             # NA   <reserved-0B84>\n0B85..0B8A    ; valid                                  # 1.1  TAMIL LETTER A..TAMIL LETTER UU\n0B8B..0B8D    ; disallowed                             # NA   <reserved-0B8B>..<reserved-0B8D>\n0B8E..0B90    ; valid                                  # 1.1  TAMIL LETTER E..TAMIL LETTER AI\n0B91          ; disallowed                             # NA   <reserved-0B91>\n0B92..0B95    ; valid                                  # 1.1  TAMIL LETTER O..TAMIL LETTER KA\n0B96..0B98    ; disallowed                             # NA   <reserved-0B96>..<reserved-0B98>\n0B99..0B9A    ; valid                                  # 1.1  TAMIL LETTER NGA..TAMIL LETTER CA\n0B9B          ; disallowed                             # NA   <reserved-0B9B>\n0B9C          ; valid                                  # 1.1  TAMIL LETTER JA\n0B9D          ; disallowed                             # NA   <reserved-0B9D>\n0B9E..0B9F    ; valid                                  # 1.1  TAMIL LETTER NYA..TAMIL LETTER TTA\n0BA0..0BA2    ; disallowed                             # NA   <reserved-0BA0>..<reserved-0BA2>\n0BA3..0BA4    ; valid                                  # 1.1  TAMIL LETTER NNA..TAMIL LETTER TA\n0BA5..0BA7    ; disallowed                             # NA   <reserved-0BA5>..<reserved-0BA7>\n0BA8..0BAA    ; valid                                  # 1.1  TAMIL LETTER NA..TAMIL LETTER PA\n0BAB..0BAD    ; disallowed                             # NA   <reserved-0BAB>..<reserved-0BAD>\n0BAE..0BB5    ; valid                                  # 1.1  TAMIL LETTER MA..TAMIL LETTER VA\n0BB6          ; valid                                  # 4.1  TAMIL LETTER SHA\n0BB7..0BB9    ; valid                                  # 1.1  TAMIL LETTER SSA..TAMIL LETTER HA\n0BBA..0BBD    ; disallowed                             # NA   <reserved-0BBA>..<reserved-0BBD>\n0BBE..0BC2    ; valid                                  # 1.1  TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN UU\n0BC3..0BC5    ; disallowed                             # NA   <reserved-0BC3>..<reserved-0BC5>\n0BC6..0BC8    ; valid                                  # 1.1  TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI\n0BC9          ; disallowed                             # NA   <reserved-0BC9>\n0BCA..0BCD    ; valid                                  # 1.1  TAMIL VOWEL SIGN O..TAMIL SIGN VIRAMA\n0BCE..0BCF    ; disallowed                             # NA   <reserved-0BCE>..<reserved-0BCF>\n0BD0          ; valid                                  # 5.1  TAMIL OM\n0BD1..0BD6    ; disallowed                             # NA   <reserved-0BD1>..<reserved-0BD6>\n0BD7          ; valid                                  # 1.1  TAMIL AU LENGTH MARK\n0BD8..0BE5    ; disallowed                             # NA   <reserved-0BD8>..<reserved-0BE5>\n0BE6          ; valid                                  # 4.1  TAMIL DIGIT ZERO\n0BE7..0BEF    ; valid                                  # 1.1  TAMIL DIGIT ONE..TAMIL DIGIT NINE\n0BF0..0BF2    ; valid                  ;      ; NV8    # 1.1  TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND\n0BF3..0BFA    ; valid                  ;      ; NV8    # 4.0  TAMIL DAY SIGN..TAMIL NUMBER SIGN\n0BFB..0BFF    ; disallowed                             # NA   <reserved-0BFB>..<reserved-0BFF>\n0C00          ; valid                                  # 7.0  TELUGU SIGN COMBINING CANDRABINDU ABOVE\n0C01..0C03    ; valid                                  # 1.1  TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA\n0C04          ; valid                                  # 11.0 TELUGU SIGN COMBINING ANUSVARA ABOVE\n0C05..0C0C    ; valid                                  # 1.1  TELUGU LETTER A..TELUGU LETTER VOCALIC L\n0C0D          ; disallowed                             # NA   <reserved-0C0D>\n0C0E..0C10    ; valid                                  # 1.1  TELUGU LETTER E..TELUGU LETTER AI\n0C11          ; disallowed                             # NA   <reserved-0C11>\n0C12..0C28    ; valid                                  # 1.1  TELUGU LETTER O..TELUGU LETTER NA\n0C29          ; disallowed                             # NA   <reserved-0C29>\n0C2A..0C33    ; valid                                  # 1.1  TELUGU LETTER PA..TELUGU LETTER LLA\n0C34          ; valid                                  # 7.0  TELUGU LETTER LLLA\n0C35..0C39    ; valid                                  # 1.1  TELUGU LETTER VA..TELUGU LETTER HA\n0C3A..0C3B    ; disallowed                             # NA   <reserved-0C3A>..<reserved-0C3B>\n0C3C          ; valid                                  # 14.0 TELUGU SIGN NUKTA\n0C3D          ; valid                                  # 5.1  TELUGU SIGN AVAGRAHA\n0C3E..0C44    ; valid                                  # 1.1  TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN VOCALIC RR\n0C45          ; disallowed                             # NA   <reserved-0C45>\n0C46..0C48    ; valid                                  # 1.1  TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI\n0C49          ; disallowed                             # NA   <reserved-0C49>\n0C4A..0C4D    ; valid                                  # 1.1  TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA\n0C4E..0C54    ; disallowed                             # NA   <reserved-0C4E>..<reserved-0C54>\n0C55..0C56    ; valid                                  # 1.1  TELUGU LENGTH MARK..TELUGU AI LENGTH MARK\n0C57          ; disallowed                             # NA   <reserved-0C57>\n0C58..0C59    ; valid                                  # 5.1  TELUGU LETTER TSA..TELUGU LETTER DZA\n0C5A          ; valid                                  # 8.0  TELUGU LETTER RRRA\n0C5B..0C5C    ; disallowed                             # NA   <reserved-0C5B>..<reserved-0C5C>\n0C5D          ; valid                                  # 14.0 TELUGU LETTER NAKAARA POLLU\n0C5E..0C5F    ; disallowed                             # NA   <reserved-0C5E>..<reserved-0C5F>\n0C60..0C61    ; valid                                  # 1.1  TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL\n0C62..0C63    ; valid                                  # 5.1  TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL\n0C64..0C65    ; disallowed                             # NA   <reserved-0C64>..<reserved-0C65>\n0C66..0C6F    ; valid                                  # 1.1  TELUGU DIGIT ZERO..TELUGU DIGIT NINE\n0C70..0C76    ; disallowed                             # NA   <reserved-0C70>..<reserved-0C76>\n0C77          ; valid                  ;      ; NV8    # 12.0 TELUGU SIGN SIDDHAM\n0C78..0C7F    ; valid                  ;      ; NV8    # 5.1  TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU SIGN TUUMU\n0C80          ; valid                                  # 9.0  KANNADA SIGN SPACING CANDRABINDU\n0C81          ; valid                                  # 7.0  KANNADA SIGN CANDRABINDU\n0C82..0C83    ; valid                                  # 1.1  KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA\n0C84          ; valid                  ;      ; NV8    # 11.0 KANNADA SIGN SIDDHAM\n0C85..0C8C    ; valid                                  # 1.1  KANNADA LETTER A..KANNADA LETTER VOCALIC L\n0C8D          ; disallowed                             # NA   <reserved-0C8D>\n0C8E..0C90    ; valid                                  # 1.1  KANNADA LETTER E..KANNADA LETTER AI\n0C91          ; disallowed                             # NA   <reserved-0C91>\n0C92..0CA8    ; valid                                  # 1.1  KANNADA LETTER O..KANNADA LETTER NA\n0CA9          ; disallowed                             # NA   <reserved-0CA9>\n0CAA..0CB3    ; valid                                  # 1.1  KANNADA LETTER PA..KANNADA LETTER LLA\n0CB4          ; disallowed                             # NA   <reserved-0CB4>\n0CB5..0CB9    ; valid                                  # 1.1  KANNADA LETTER VA..KANNADA LETTER HA\n0CBA..0CBB    ; disallowed                             # NA   <reserved-0CBA>..<reserved-0CBB>\n0CBC..0CBD    ; valid                                  # 4.0  KANNADA SIGN NUKTA..KANNADA SIGN AVAGRAHA\n0CBE..0CC4    ; valid                                  # 1.1  KANNADA VOWEL SIGN AA..KANNADA VOWEL SIGN VOCALIC RR\n0CC5          ; disallowed                             # NA   <reserved-0CC5>\n0CC6..0CC8    ; valid                                  # 1.1  KANNADA VOWEL SIGN E..KANNADA VOWEL SIGN AI\n0CC9          ; disallowed                             # NA   <reserved-0CC9>\n0CCA..0CCD    ; valid                                  # 1.1  KANNADA VOWEL SIGN O..KANNADA SIGN VIRAMA\n0CCE..0CD4    ; disallowed                             # NA   <reserved-0CCE>..<reserved-0CD4>\n0CD5..0CD6    ; valid                                  # 1.1  KANNADA LENGTH MARK..KANNADA AI LENGTH MARK\n0CD7..0CDC    ; disallowed                             # NA   <reserved-0CD7>..<reserved-0CDC>\n0CDD          ; valid                                  # 14.0 KANNADA LETTER NAKAARA POLLU\n0CDE          ; valid                                  # 1.1  KANNADA LETTER FA\n0CDF          ; disallowed                             # NA   <reserved-0CDF>\n0CE0..0CE1    ; valid                                  # 1.1  KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL\n0CE2..0CE3    ; valid                                  # 5.0  KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL\n0CE4..0CE5    ; disallowed                             # NA   <reserved-0CE4>..<reserved-0CE5>\n0CE6..0CEF    ; valid                                  # 1.1  KANNADA DIGIT ZERO..KANNADA DIGIT NINE\n0CF0          ; disallowed                             # NA   <reserved-0CF0>\n0CF1..0CF2    ; valid                                  # 5.0  KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA\n0CF3          ; valid                                  # 15.0 KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT\n0CF4..0CFF    ; disallowed                             # NA   <reserved-0CF4>..<reserved-0CFF>\n0D00          ; valid                                  # 10.0 MALAYALAM SIGN COMBINING ANUSVARA ABOVE\n0D01          ; valid                                  # 7.0  MALAYALAM SIGN CANDRABINDU\n0D02..0D03    ; valid                                  # 1.1  MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA\n0D04          ; valid                                  # 13.0 MALAYALAM LETTER VEDIC ANUSVARA\n0D05..0D0C    ; valid                                  # 1.1  MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC L\n0D0D          ; disallowed                             # NA   <reserved-0D0D>\n0D0E..0D10    ; valid                                  # 1.1  MALAYALAM LETTER E..MALAYALAM LETTER AI\n0D11          ; disallowed                             # NA   <reserved-0D11>\n0D12..0D28    ; valid                                  # 1.1  MALAYALAM LETTER O..MALAYALAM LETTER NA\n0D29          ; valid                                  # 6.0  MALAYALAM LETTER NNNA\n0D2A..0D39    ; valid                                  # 1.1  MALAYALAM LETTER PA..MALAYALAM LETTER HA\n0D3A          ; valid                                  # 6.0  MALAYALAM LETTER TTTA\n0D3B..0D3C    ; valid                                  # 10.0 MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA\n0D3D          ; valid                                  # 5.1  MALAYALAM SIGN AVAGRAHA\n0D3E..0D43    ; valid                                  # 1.1  MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN VOCALIC R\n0D44          ; valid                                  # 5.1  MALAYALAM VOWEL SIGN VOCALIC RR\n0D45          ; disallowed                             # NA   <reserved-0D45>\n0D46..0D48    ; valid                                  # 1.1  MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI\n0D49          ; disallowed                             # NA   <reserved-0D49>\n0D4A..0D4D    ; valid                                  # 1.1  MALAYALAM VOWEL SIGN O..MALAYALAM SIGN VIRAMA\n0D4E          ; valid                                  # 6.0  MALAYALAM LETTER DOT REPH\n0D4F          ; valid                  ;      ; NV8    # 9.0  MALAYALAM SIGN PARA\n0D50..0D53    ; disallowed                             # NA   <reserved-0D50>..<reserved-0D53>\n0D54..0D56    ; valid                                  # 9.0  MALAYALAM LETTER CHILLU M..MALAYALAM LETTER CHILLU LLL\n0D57          ; valid                                  # 1.1  MALAYALAM AU LENGTH MARK\n0D58..0D5E    ; valid                  ;      ; NV8    # 9.0  MALAYALAM FRACTION ONE ONE-HUNDRED-AND-SIXTIETH..MALAYALAM FRACTION ONE FIFTH\n0D5F          ; valid                                  # 8.0  MALAYALAM LETTER ARCHAIC II\n0D60..0D61    ; valid                                  # 1.1  MALAYALAM LETTER VOCALIC RR..MALAYALAM LETTER VOCALIC LL\n0D62..0D63    ; valid                                  # 5.1  MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL\n0D64..0D65    ; disallowed                             # NA   <reserved-0D64>..<reserved-0D65>\n0D66..0D6F    ; valid                                  # 1.1  MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE\n0D70..0D75    ; valid                  ;      ; NV8    # 5.1  MALAYALAM NUMBER TEN..MALAYALAM FRACTION THREE QUARTERS\n0D76..0D78    ; valid                  ;      ; NV8    # 9.0  MALAYALAM FRACTION ONE SIXTEENTH..MALAYALAM FRACTION THREE SIXTEENTHS\n0D79          ; valid                  ;      ; NV8    # 5.1  MALAYALAM DATE MARK\n0D7A..0D7F    ; valid                                  # 5.1  MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K\n0D80          ; disallowed                             # NA   <reserved-0D80>\n0D81          ; valid                                  # 13.0 SINHALA SIGN CANDRABINDU\n0D82..0D83    ; valid                                  # 3.0  SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA\n0D84          ; disallowed                             # NA   <reserved-0D84>\n0D85..0D96    ; valid                                  # 3.0  SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA\n0D97..0D99    ; disallowed                             # NA   <reserved-0D97>..<reserved-0D99>\n0D9A..0DB1    ; valid                                  # 3.0  SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA\n0DB2          ; disallowed                             # NA   <reserved-0DB2>\n0DB3..0DBB    ; valid                                  # 3.0  SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA\n0DBC          ; disallowed                             # NA   <reserved-0DBC>\n0DBD          ; valid                                  # 3.0  SINHALA LETTER DANTAJA LAYANNA\n0DBE..0DBF    ; disallowed                             # NA   <reserved-0DBE>..<reserved-0DBF>\n0DC0..0DC6    ; valid                                  # 3.0  SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA\n0DC7..0DC9    ; disallowed                             # NA   <reserved-0DC7>..<reserved-0DC9>\n0DCA          ; valid                                  # 3.0  SINHALA SIGN AL-LAKUNA\n0DCB..0DCE    ; disallowed                             # NA   <reserved-0DCB>..<reserved-0DCE>\n0DCF..0DD4    ; valid                                  # 3.0  SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA\n0DD5          ; disallowed                             # NA   <reserved-0DD5>\n0DD6          ; valid                                  # 3.0  SINHALA VOWEL SIGN DIGA PAA-PILLA\n0DD7          ; disallowed                             # NA   <reserved-0DD7>\n0DD8..0DDF    ; valid                                  # 3.0  SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA\n0DE0..0DE5    ; disallowed                             # NA   <reserved-0DE0>..<reserved-0DE5>\n0DE6..0DEF    ; valid                                  # 7.0  SINHALA LITH DIGIT ZERO..SINHALA LITH DIGIT NINE\n0DF0..0DF1    ; disallowed                             # NA   <reserved-0DF0>..<reserved-0DF1>\n0DF2..0DF3    ; valid                                  # 3.0  SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA\n0DF4          ; valid                  ;      ; NV8    # 3.0  SINHALA PUNCTUATION KUNDDALIYA\n0DF5..0E00    ; disallowed                             # NA   <reserved-0DF5>..<reserved-0E00>\n0E01..0E32    ; valid                                  # 1.1  THAI CHARACTER KO KAI..THAI CHARACTER SARA AA\n0E33          ; mapped                 ; 0E4D 0E32     # 1.1  THAI CHARACTER SARA AM\n0E34..0E3A    ; valid                                  # 1.1  THAI CHARACTER SARA I..THAI CHARACTER PHINTHU\n0E3B..0E3E    ; disallowed                             # NA   <reserved-0E3B>..<reserved-0E3E>\n0E3F          ; valid                  ;      ; NV8    # 1.1  THAI CURRENCY SYMBOL BAHT\n0E40..0E4E    ; valid                                  # 1.1  THAI CHARACTER SARA E..THAI CHARACTER YAMAKKAN\n0E4F          ; valid                  ;      ; NV8    # 1.1  THAI CHARACTER FONGMAN\n0E50..0E59    ; valid                                  # 1.1  THAI DIGIT ZERO..THAI DIGIT NINE\n0E5A..0E5B    ; valid                  ;      ; NV8    # 1.1  THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT\n0E5C..0E80    ; disallowed                             # NA   <reserved-0E5C>..<reserved-0E80>\n0E81..0E82    ; valid                                  # 1.1  LAO LETTER KO..LAO LETTER KHO SUNG\n0E83          ; disallowed                             # NA   <reserved-0E83>\n0E84          ; valid                                  # 1.1  LAO LETTER KHO TAM\n0E85          ; disallowed                             # NA   <reserved-0E85>\n0E86          ; valid                                  # 12.0 LAO LETTER PALI GHA\n0E87..0E88    ; valid                                  # 1.1  LAO LETTER NGO..LAO LETTER CO\n0E89          ; valid                                  # 12.0 LAO LETTER PALI CHA\n0E8A          ; valid                                  # 1.1  LAO LETTER SO TAM\n0E8B          ; disallowed                             # NA   <reserved-0E8B>\n0E8C          ; valid                                  # 12.0 LAO LETTER PALI JHA\n0E8D          ; valid                                  # 1.1  LAO LETTER NYO\n0E8E..0E93    ; valid                                  # 12.0 LAO LETTER PALI NYA..LAO LETTER PALI NNA\n0E94..0E97    ; valid                                  # 1.1  LAO LETTER DO..LAO LETTER THO TAM\n0E98          ; valid                                  # 12.0 LAO LETTER PALI DHA\n0E99..0E9F    ; valid                                  # 1.1  LAO LETTER NO..LAO LETTER FO SUNG\n0EA0          ; valid                                  # 12.0 LAO LETTER PALI BHA\n0EA1..0EA3    ; valid                                  # 1.1  LAO LETTER MO..LAO LETTER LO LING\n0EA4          ; disallowed                             # NA   <reserved-0EA4>\n0EA5          ; valid                                  # 1.1  LAO LETTER LO LOOT\n0EA6          ; disallowed                             # NA   <reserved-0EA6>\n0EA7          ; valid                                  # 1.1  LAO LETTER WO\n0EA8..0EA9    ; valid                                  # 12.0 LAO LETTER SANSKRIT SHA..LAO LETTER SANSKRIT SSA\n0EAA..0EAB    ; valid                                  # 1.1  LAO LETTER SO SUNG..LAO LETTER HO SUNG\n0EAC          ; valid                                  # 12.0 LAO LETTER PALI LLA\n0EAD..0EB2    ; valid                                  # 1.1  LAO LETTER O..LAO VOWEL SIGN AA\n0EB3          ; mapped                 ; 0ECD 0EB2     # 1.1  LAO VOWEL SIGN AM\n0EB4..0EB9    ; valid                                  # 1.1  LAO VOWEL SIGN I..LAO VOWEL SIGN UU\n0EBA          ; valid                                  # 12.0 LAO SIGN PALI VIRAMA\n0EBB..0EBD    ; valid                                  # 1.1  LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN NYO\n0EBE..0EBF    ; disallowed                             # NA   <reserved-0EBE>..<reserved-0EBF>\n0EC0..0EC4    ; valid                                  # 1.1  LAO VOWEL SIGN E..LAO VOWEL SIGN AI\n0EC5          ; disallowed                             # NA   <reserved-0EC5>\n0EC6          ; valid                                  # 1.1  LAO KO LA\n0EC7          ; disallowed                             # NA   <reserved-0EC7>\n0EC8..0ECD    ; valid                                  # 1.1  LAO TONE MAI EK..LAO NIGGAHITA\n0ECE          ; valid                                  # 15.0 LAO YAMAKKAN\n0ECF          ; disallowed                             # NA   <reserved-0ECF>\n0ED0..0ED9    ; valid                                  # 1.1  LAO DIGIT ZERO..LAO DIGIT NINE\n0EDA..0EDB    ; disallowed                             # NA   <reserved-0EDA>..<reserved-0EDB>\n0EDC          ; mapped                 ; 0EAB 0E99     # 1.1  LAO HO NO\n0EDD          ; mapped                 ; 0EAB 0EA1     # 1.1  LAO HO MO\n0EDE..0EDF    ; valid                                  # 6.1  LAO LETTER KHMU GO..LAO LETTER KHMU NYO\n0EE0..0EFF    ; disallowed                             # NA   <reserved-0EE0>..<reserved-0EFF>\n0F00          ; valid                                  # 2.0  TIBETAN SYLLABLE OM\n0F01..0F0A    ; valid                  ;      ; NV8    # 2.0  TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK BKA- SHOG YIG MGO\n0F0B          ; valid                                  # 2.0  TIBETAN MARK INTERSYLLABIC TSHEG\n0F0C          ; mapped                 ; 0F0B          # 2.0  TIBETAN MARK DELIMITER TSHEG BSTAR\n0F0D..0F17    ; valid                  ;      ; NV8    # 2.0  TIBETAN MARK SHAD..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS\n0F18..0F19    ; valid                                  # 2.0  TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS\n0F1A..0F1F    ; valid                  ;      ; NV8    # 2.0  TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG\n0F20..0F29    ; valid                                  # 2.0  TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE\n0F2A..0F34    ; valid                  ;      ; NV8    # 2.0  TIBETAN DIGIT HALF ONE..TIBETAN MARK BSDUS RTAGS\n0F35          ; valid                                  # 2.0  TIBETAN MARK NGAS BZUNG NYI ZLA\n0F36          ; valid                  ;      ; NV8    # 2.0  TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN\n0F37          ; valid                                  # 2.0  TIBETAN MARK NGAS BZUNG SGOR RTAGS\n0F38          ; valid                  ;      ; NV8    # 2.0  TIBETAN MARK CHE MGO\n0F39          ; valid                                  # 2.0  TIBETAN MARK TSA -PHRU\n0F3A..0F3D    ; valid                  ;      ; NV8    # 2.0  TIBETAN MARK GUG RTAGS GYON..TIBETAN MARK ANG KHANG GYAS\n0F3E..0F42    ; valid                                  # 2.0  TIBETAN SIGN YAR TSHES..TIBETAN LETTER GA\n0F43          ; mapped                 ; 0F42 0FB7     # 2.0  TIBETAN LETTER GHA\n0F44..0F47    ; valid                                  # 2.0  TIBETAN LETTER NGA..TIBETAN LETTER JA\n0F48          ; disallowed                             # NA   <reserved-0F48>\n0F49..0F4C    ; valid                                  # 2.0  TIBETAN LETTER NYA..TIBETAN LETTER DDA\n0F4D          ; mapped                 ; 0F4C 0FB7     # 2.0  TIBETAN LETTER DDHA\n0F4E..0F51    ; valid                                  # 2.0  TIBETAN LETTER NNA..TIBETAN LETTER DA\n0F52          ; mapped                 ; 0F51 0FB7     # 2.0  TIBETAN LETTER DHA\n0F53..0F56    ; valid                                  # 2.0  TIBETAN LETTER NA..TIBETAN LETTER BA\n0F57          ; mapped                 ; 0F56 0FB7     # 2.0  TIBETAN LETTER BHA\n0F58..0F5B    ; valid                                  # 2.0  TIBETAN LETTER MA..TIBETAN LETTER DZA\n0F5C          ; mapped                 ; 0F5B 0FB7     # 2.0  TIBETAN LETTER DZHA\n0F5D..0F68    ; valid                                  # 2.0  TIBETAN LETTER WA..TIBETAN LETTER A\n0F69          ; mapped                 ; 0F40 0FB5     # 2.0  TIBETAN LETTER KSSA\n0F6A          ; valid                                  # 3.0  TIBETAN LETTER FIXED-FORM RA\n0F6B..0F6C    ; valid                                  # 5.1  TIBETAN LETTER KKA..TIBETAN LETTER RRA\n0F6D..0F70    ; disallowed                             # NA   <reserved-0F6D>..<reserved-0F70>\n0F71..0F72    ; valid                                  # 2.0  TIBETAN VOWEL SIGN AA..TIBETAN VOWEL SIGN I\n0F73          ; mapped                 ; 0F71 0F72     # 2.0  TIBETAN VOWEL SIGN II\n0F74          ; valid                                  # 2.0  TIBETAN VOWEL SIGN U\n0F75          ; mapped                 ; 0F71 0F74     # 2.0  TIBETAN VOWEL SIGN UU\n0F76          ; mapped                 ; 0FB2 0F80     # 2.0  TIBETAN VOWEL SIGN VOCALIC R\n0F77          ; mapped                 ; 0FB2 0F71 0F80 #2.0  TIBETAN VOWEL SIGN VOCALIC RR\n0F78          ; mapped                 ; 0FB3 0F80     # 2.0  TIBETAN VOWEL SIGN VOCALIC L\n0F79          ; mapped                 ; 0FB3 0F71 0F80 #2.0  TIBETAN VOWEL SIGN VOCALIC LL\n0F7A..0F80    ; valid                                  # 2.0  TIBETAN VOWEL SIGN E..TIBETAN VOWEL SIGN REVERSED I\n0F81          ; mapped                 ; 0F71 0F80     # 2.0  TIBETAN VOWEL SIGN REVERSED II\n0F82..0F84    ; valid                                  # 2.0  TIBETAN SIGN NYI ZLA NAA DA..TIBETAN MARK HALANTA\n0F85          ; valid                  ;      ; NV8    # 2.0  TIBETAN MARK PALUTA\n0F86..0F8B    ; valid                                  # 2.0  TIBETAN SIGN LCI RTAGS..TIBETAN SIGN GRU MED RGYINGS\n0F8C..0F8F    ; valid                                  # 6.0  TIBETAN SIGN INVERTED MCHU CAN..TIBETAN SUBJOINED SIGN INVERTED MCHU CAN\n0F90..0F92    ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER KA..TIBETAN SUBJOINED LETTER GA\n0F93          ; mapped                 ; 0F92 0FB7     # 2.0  TIBETAN SUBJOINED LETTER GHA\n0F94..0F95    ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER NGA..TIBETAN SUBJOINED LETTER CA\n0F96          ; valid                                  # 3.0  TIBETAN SUBJOINED LETTER CHA\n0F97          ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER JA\n0F98          ; disallowed                             # NA   <reserved-0F98>\n0F99..0F9C    ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER DDA\n0F9D          ; mapped                 ; 0F9C 0FB7     # 2.0  TIBETAN SUBJOINED LETTER DDHA\n0F9E..0FA1    ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER NNA..TIBETAN SUBJOINED LETTER DA\n0FA2          ; mapped                 ; 0FA1 0FB7     # 2.0  TIBETAN SUBJOINED LETTER DHA\n0FA3..0FA6    ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER NA..TIBETAN SUBJOINED LETTER BA\n0FA7          ; mapped                 ; 0FA6 0FB7     # 2.0  TIBETAN SUBJOINED LETTER BHA\n0FA8..0FAB    ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER MA..TIBETAN SUBJOINED LETTER DZA\n0FAC          ; mapped                 ; 0FAB 0FB7     # 2.0  TIBETAN SUBJOINED LETTER DZHA\n0FAD          ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER WA\n0FAE..0FB0    ; valid                                  # 3.0  TIBETAN SUBJOINED LETTER ZHA..TIBETAN SUBJOINED LETTER -A\n0FB1..0FB7    ; valid                                  # 2.0  TIBETAN SUBJOINED LETTER YA..TIBETAN SUBJOINED LETTER HA\n0FB8          ; valid                                  # 3.0  TIBETAN SUBJOINED LETTER A\n0FB9          ; mapped                 ; 0F90 0FB5     # 2.0  TIBETAN SUBJOINED LETTER KSSA\n0FBA..0FBC    ; valid                                  # 3.0  TIBETAN SUBJOINED LETTER FIXED-FORM WA..TIBETAN SUBJOINED LETTER FIXED-FORM RA\n0FBD          ; disallowed                             # NA   <reserved-0FBD>\n0FBE..0FC5    ; valid                  ;      ; NV8    # 3.0  TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE\n0FC6          ; valid                                  # 3.0  TIBETAN SYMBOL PADMA GDAN\n0FC7..0FCC    ; valid                  ;      ; NV8    # 3.0  TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL\n0FCD          ; disallowed                             # NA   <reserved-0FCD>\n0FCE          ; valid                  ;      ; NV8    # 5.1  TIBETAN SIGN RDEL NAG RDEL DKAR\n0FCF          ; valid                  ;      ; NV8    # 3.0  TIBETAN SIGN RDEL NAG GSUM\n0FD0..0FD1    ; valid                  ;      ; NV8    # 4.1  TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK MNYAM YIG GI MGO RGYAN\n0FD2..0FD4    ; valid                  ;      ; NV8    # 5.1  TIBETAN MARK NYIS TSHEG..TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA\n0FD5..0FD8    ; valid                  ;      ; NV8    # 5.2  RIGHT-FACING SVASTI SIGN..LEFT-FACING SVASTI SIGN WITH DOTS\n0FD9..0FDA    ; valid                  ;      ; NV8    # 6.0  TIBETAN MARK LEADING MCHAN RTAGS..TIBETAN MARK TRAILING MCHAN RTAGS\n0FDB..0FFF    ; disallowed                             # NA   <reserved-0FDB>..<reserved-0FFF>\n1000..1021    ; valid                                  # 3.0  MYANMAR LETTER KA..MYANMAR LETTER A\n1022          ; valid                                  # 5.1  MYANMAR LETTER SHAN A\n1023..1027    ; valid                                  # 3.0  MYANMAR LETTER I..MYANMAR LETTER E\n1028          ; valid                                  # 5.1  MYANMAR LETTER MON E\n1029..102A    ; valid                                  # 3.0  MYANMAR LETTER O..MYANMAR LETTER AU\n102B          ; valid                                  # 5.1  MYANMAR VOWEL SIGN TALL AA\n102C..1032    ; valid                                  # 3.0  MYANMAR VOWEL SIGN AA..MYANMAR VOWEL SIGN AI\n1033..1035    ; valid                                  # 5.1  MYANMAR VOWEL SIGN MON II..MYANMAR VOWEL SIGN E ABOVE\n1036..1039    ; valid                                  # 3.0  MYANMAR SIGN ANUSVARA..MYANMAR SIGN VIRAMA\n103A..103F    ; valid                                  # 5.1  MYANMAR SIGN ASAT..MYANMAR LETTER GREAT SA\n1040..1049    ; valid                                  # 3.0  MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE\n104A..104F    ; valid                  ;      ; NV8    # 3.0  MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE\n1050..1059    ; valid                                  # 3.0  MYANMAR LETTER SHA..MYANMAR VOWEL SIGN VOCALIC LL\n105A..1099    ; valid                                  # 5.1  MYANMAR LETTER MON NGA..MYANMAR SHAN DIGIT NINE\n109A..109D    ; valid                                  # 5.2  MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON AI\n109E..109F    ; valid                  ;      ; NV8    # 5.1  MYANMAR SYMBOL SHAN ONE..MYANMAR SYMBOL SHAN EXCLAMATION\n10A0..10C5    ; disallowed                             # 1.1  GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE\n10C6          ; disallowed                             # NA   <reserved-10C6>\n10C7          ; mapped                 ; 2D27          # 6.1  GEORGIAN CAPITAL LETTER YN\n10C8..10CC    ; disallowed                             # NA   <reserved-10C8>..<reserved-10CC>\n10CD          ; mapped                 ; 2D2D          # 6.1  GEORGIAN CAPITAL LETTER AEN\n10CE..10CF    ; disallowed                             # NA   <reserved-10CE>..<reserved-10CF>\n10D0..10F6    ; valid                                  # 1.1  GEORGIAN LETTER AN..GEORGIAN LETTER FI\n10F7..10F8    ; valid                                  # 3.2  GEORGIAN LETTER YN..GEORGIAN LETTER ELIFI\n10F9..10FA    ; valid                                  # 4.1  GEORGIAN LETTER TURNED GAN..GEORGIAN LETTER AIN\n10FB          ; valid                  ;      ; NV8    # 1.1  GEORGIAN PARAGRAPH SEPARATOR\n10FC          ; mapped                 ; 10DC          # 4.1  MODIFIER LETTER GEORGIAN NAR\n10FD..10FF    ; valid                                  # 6.1  GEORGIAN LETTER AEN..GEORGIAN LETTER LABIAL SIGN\n1100..1159    ; valid                  ;      ; NV8    # 1.1  HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG YEORINHIEUH\n115A..115E    ; valid                  ;      ; NV8    # 5.2  HANGUL CHOSEONG KIYEOK-TIKEUT..HANGUL CHOSEONG TIKEUT-RIEUL\n115F..1160    ; disallowed                             # 1.1  HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER\n1161..11A2    ; valid                  ;      ; NV8    # 1.1  HANGUL JUNGSEONG A..HANGUL JUNGSEONG SSANGARAEA\n11A3..11A7    ; valid                  ;      ; NV8    # 5.2  HANGUL JUNGSEONG A-EU..HANGUL JUNGSEONG O-YAE\n11A8..11F9    ; valid                  ;      ; NV8    # 1.1  HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG YEORINHIEUH\n11FA..11FF    ; valid                  ;      ; NV8    # 5.2  HANGUL JONGSEONG KIYEOK-NIEUN..HANGUL JONGSEONG SSANGNIEUN\n1200..1206    ; valid                                  # 3.0  ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE HO\n1207          ; valid                                  # 4.1  ETHIOPIC SYLLABLE HOA\n1208..1246    ; valid                                  # 3.0  ETHIOPIC SYLLABLE LA..ETHIOPIC SYLLABLE QO\n1247          ; valid                                  # 4.1  ETHIOPIC SYLLABLE QOA\n1248          ; valid                                  # 3.0  ETHIOPIC SYLLABLE QWA\n1249          ; disallowed                             # NA   <reserved-1249>\n124A..124D    ; valid                                  # 3.0  ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE\n124E..124F    ; disallowed                             # NA   <reserved-124E>..<reserved-124F>\n1250..1256    ; valid                                  # 3.0  ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO\n1257          ; disallowed                             # NA   <reserved-1257>\n1258          ; valid                                  # 3.0  ETHIOPIC SYLLABLE QHWA\n1259          ; disallowed                             # NA   <reserved-1259>\n125A..125D    ; valid                                  # 3.0  ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE\n125E..125F    ; disallowed                             # NA   <reserved-125E>..<reserved-125F>\n1260..1286    ; valid                                  # 3.0  ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XO\n1287          ; valid                                  # 4.1  ETHIOPIC SYLLABLE XOA\n1288          ; valid                                  # 3.0  ETHIOPIC SYLLABLE XWA\n1289          ; disallowed                             # NA   <reserved-1289>\n128A..128D    ; valid                                  # 3.0  ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE\n128E..128F    ; disallowed                             # NA   <reserved-128E>..<reserved-128F>\n1290..12AE    ; valid                                  # 3.0  ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KO\n12AF          ; valid                                  # 4.1  ETHIOPIC SYLLABLE KOA\n12B0          ; valid                                  # 3.0  ETHIOPIC SYLLABLE KWA\n12B1          ; disallowed                             # NA   <reserved-12B1>\n12B2..12B5    ; valid                                  # 3.0  ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE\n12B6..12B7    ; disallowed                             # NA   <reserved-12B6>..<reserved-12B7>\n12B8..12BE    ; valid                                  # 3.0  ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO\n12BF          ; disallowed                             # NA   <reserved-12BF>\n12C0          ; valid                                  # 3.0  ETHIOPIC SYLLABLE KXWA\n12C1          ; disallowed                             # NA   <reserved-12C1>\n12C2..12C5    ; valid                                  # 3.0  ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE\n12C6..12C7    ; disallowed                             # NA   <reserved-12C6>..<reserved-12C7>\n12C8..12CE    ; valid                                  # 3.0  ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE WO\n12CF          ; valid                                  # 4.1  ETHIOPIC SYLLABLE WOA\n12D0..12D6    ; valid                                  # 3.0  ETHIOPIC SYLLABLE PHARYNGEAL A..ETHIOPIC SYLLABLE PHARYNGEAL O\n12D7          ; disallowed                             # NA   <reserved-12D7>\n12D8..12EE    ; valid                                  # 3.0  ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE YO\n12EF          ; valid                                  # 4.1  ETHIOPIC SYLLABLE YOA\n12F0..130E    ; valid                                  # 3.0  ETHIOPIC SYLLABLE DA..ETHIOPIC SYLLABLE GO\n130F          ; valid                                  # 4.1  ETHIOPIC SYLLABLE GOA\n1310          ; valid                                  # 3.0  ETHIOPIC SYLLABLE GWA\n1311          ; disallowed                             # NA   <reserved-1311>\n1312..1315    ; valid                                  # 3.0  ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE\n1316..1317    ; disallowed                             # NA   <reserved-1316>..<reserved-1317>\n1318..131E    ; valid                                  # 3.0  ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE GGO\n131F          ; valid                                  # 4.1  ETHIOPIC SYLLABLE GGWAA\n1320..1346    ; valid                                  # 3.0  ETHIOPIC SYLLABLE THA..ETHIOPIC SYLLABLE TZO\n1347          ; valid                                  # 4.1  ETHIOPIC SYLLABLE TZOA\n1348..135A    ; valid                                  # 3.0  ETHIOPIC SYLLABLE FA..ETHIOPIC SYLLABLE FYA\n135B..135C    ; disallowed                             # NA   <reserved-135B>..<reserved-135C>\n135D..135E    ; valid                                  # 6.0  ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING VOWEL LENGTH MARK\n135F          ; valid                                  # 4.1  ETHIOPIC COMBINING GEMINATION MARK\n1360          ; valid                  ;      ; NV8    # 4.1  ETHIOPIC SECTION MARK\n1361..137C    ; valid                  ;      ; NV8    # 3.0  ETHIOPIC WORDSPACE..ETHIOPIC NUMBER TEN THOUSAND\n137D..137F    ; disallowed                             # NA   <reserved-137D>..<reserved-137F>\n1380..138F    ; valid                                  # 4.1  ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE\n1390..1399    ; valid                  ;      ; NV8    # 4.1  ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT\n139A..139F    ; disallowed                             # NA   <reserved-139A>..<reserved-139F>\n13A0..13F4    ; valid                                  # 3.0  CHEROKEE LETTER A..CHEROKEE LETTER YV\n13F5          ; valid                                  # 8.0  CHEROKEE LETTER MV\n13F6..13F7    ; disallowed                             # NA   <reserved-13F6>..<reserved-13F7>\n13F8          ; mapped                 ; 13F0          # 8.0  CHEROKEE SMALL LETTER YE\n13F9          ; mapped                 ; 13F1          # 8.0  CHEROKEE SMALL LETTER YI\n13FA          ; mapped                 ; 13F2          # 8.0  CHEROKEE SMALL LETTER YO\n13FB          ; mapped                 ; 13F3          # 8.0  CHEROKEE SMALL LETTER YU\n13FC          ; mapped                 ; 13F4          # 8.0  CHEROKEE SMALL LETTER YV\n13FD          ; mapped                 ; 13F5          # 8.0  CHEROKEE SMALL LETTER MV\n13FE..13FF    ; disallowed                             # NA   <reserved-13FE>..<reserved-13FF>\n1400          ; valid                  ;      ; NV8    # 5.2  CANADIAN SYLLABICS HYPHEN\n1401..166C    ; valid                                  # 3.0  CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA\n166D..166E    ; valid                  ;      ; NV8    # 3.0  CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP\n166F..1676    ; valid                                  # 3.0  CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS NNGAA\n1677..167F    ; valid                                  # 5.2  CANADIAN SYLLABICS WOODS-CREE THWEE..CANADIAN SYLLABICS BLACKFOOT W\n1680          ; disallowed                             # 3.0  OGHAM SPACE MARK\n1681..169A    ; valid                                  # 3.0  OGHAM LETTER BEITH..OGHAM LETTER PEITH\n169B..169C    ; valid                  ;      ; NV8    # 3.0  OGHAM FEATHER MARK..OGHAM REVERSED FEATHER MARK\n169D..169F    ; disallowed                             # NA   <reserved-169D>..<reserved-169F>\n16A0..16EA    ; valid                                  # 3.0  RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X\n16EB..16F0    ; valid                  ;      ; NV8    # 3.0  RUNIC SINGLE PUNCTUATION..RUNIC BELGTHOR SYMBOL\n16F1..16F8    ; valid                                  # 7.0  RUNIC LETTER K..RUNIC LETTER FRANKS CASKET AESC\n16F9..16FF    ; disallowed                             # NA   <reserved-16F9>..<reserved-16FF>\n1700..170C    ; valid                                  # 3.2  TAGALOG LETTER A..TAGALOG LETTER YA\n170D          ; valid                                  # 14.0 TAGALOG LETTER RA\n170E..1714    ; valid                                  # 3.2  TAGALOG LETTER LA..TAGALOG SIGN VIRAMA\n1715          ; valid                                  # 14.0 TAGALOG SIGN PAMUDPOD\n1716..171E    ; disallowed                             # NA   <reserved-1716>..<reserved-171E>\n171F          ; valid                                  # 14.0 TAGALOG LETTER ARCHAIC RA\n1720..1734    ; valid                                  # 3.2  HANUNOO LETTER A..HANUNOO SIGN PAMUDPOD\n1735..1736    ; valid                  ;      ; NV8    # 3.2  PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION\n1737..173F    ; disallowed                             # NA   <reserved-1737>..<reserved-173F>\n1740..1753    ; valid                                  # 3.2  BUHID LETTER A..BUHID VOWEL SIGN U\n1754..175F    ; disallowed                             # NA   <reserved-1754>..<reserved-175F>\n1760..176C    ; valid                                  # 3.2  TAGBANWA LETTER A..TAGBANWA LETTER YA\n176D          ; disallowed                             # NA   <reserved-176D>\n176E..1770    ; valid                                  # 3.2  TAGBANWA LETTER LA..TAGBANWA LETTER SA\n1771          ; disallowed                             # NA   <reserved-1771>\n1772..1773    ; valid                                  # 3.2  TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U\n1774..177F    ; disallowed                             # NA   <reserved-1774>..<reserved-177F>\n1780..17B3    ; valid                                  # 3.0  KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU\n17B4..17B5    ; disallowed                             # 3.0  KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA\n17B6..17D3    ; valid                                  # 3.0  KHMER VOWEL SIGN AA..KHMER SIGN BATHAMASAT\n17D4..17D6    ; valid                  ;      ; NV8    # 3.0  KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH\n17D7          ; valid                                  # 3.0  KHMER SIGN LEK TOO\n17D8..17DB    ; valid                  ;      ; NV8    # 3.0  KHMER SIGN BEYYAL..KHMER CURRENCY SYMBOL RIEL\n17DC          ; valid                                  # 3.0  KHMER SIGN AVAKRAHASANYA\n17DD          ; valid                                  # 4.0  KHMER SIGN ATTHACAN\n17DE..17DF    ; disallowed                             # NA   <reserved-17DE>..<reserved-17DF>\n17E0..17E9    ; valid                                  # 3.0  KHMER DIGIT ZERO..KHMER DIGIT NINE\n17EA..17EF    ; disallowed                             # NA   <reserved-17EA>..<reserved-17EF>\n17F0..17F9    ; valid                  ;      ; NV8    # 4.0  KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON\n17FA..17FF    ; disallowed                             # NA   <reserved-17FA>..<reserved-17FF>\n1800..1805    ; valid                  ;      ; NV8    # 3.0  MONGOLIAN BIRGA..MONGOLIAN FOUR DOTS\n1806          ; disallowed                             # 3.0  MONGOLIAN TODO SOFT HYPHEN\n1807..180A    ; valid                  ;      ; NV8    # 3.0  MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU\n180B..180D    ; ignored                                # 3.0  MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE\n180E          ; disallowed                             # 3.0  MONGOLIAN VOWEL SEPARATOR\n180F          ; ignored                                # 14.0 MONGOLIAN FREE VARIATION SELECTOR FOUR\n1810..1819    ; valid                                  # 3.0  MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE\n181A..181F    ; disallowed                             # NA   <reserved-181A>..<reserved-181F>\n1820..1877    ; valid                                  # 3.0  MONGOLIAN LETTER A..MONGOLIAN LETTER MANCHU ZHA\n1878          ; valid                                  # 11.0 MONGOLIAN LETTER CHA WITH TWO DOTS\n1879..187F    ; disallowed                             # NA   <reserved-1879>..<reserved-187F>\n1880..18A9    ; valid                                  # 3.0  MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER ALI GALI DAGALGA\n18AA          ; valid                                  # 5.1  MONGOLIAN LETTER MANCHU ALI GALI LHA\n18AB..18AF    ; disallowed                             # NA   <reserved-18AB>..<reserved-18AF>\n18B0..18F5    ; valid                                  # 5.2  CANADIAN SYLLABICS OY..CANADIAN SYLLABICS CARRIER DENTAL S\n18F6..18FF    ; disallowed                             # NA   <reserved-18F6>..<reserved-18FF>\n1900..191C    ; valid                                  # 4.0  LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER HA\n191D..191E    ; valid                                  # 7.0  LIMBU LETTER GYAN..LIMBU LETTER TRA\n191F          ; disallowed                             # NA   <reserved-191F>\n1920..192B    ; valid                                  # 4.0  LIMBU VOWEL SIGN A..LIMBU SUBJOINED LETTER WA\n192C..192F    ; disallowed                             # NA   <reserved-192C>..<reserved-192F>\n1930..193B    ; valid                                  # 4.0  LIMBU SMALL LETTER KA..LIMBU SIGN SA-I\n193C..193F    ; disallowed                             # NA   <reserved-193C>..<reserved-193F>\n1940          ; valid                  ;      ; NV8    # 4.0  LIMBU SIGN LOO\n1941..1943    ; disallowed                             # NA   <reserved-1941>..<reserved-1943>\n1944..1945    ; valid                  ;      ; NV8    # 4.0  LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK\n1946..196D    ; valid                                  # 4.0  LIMBU DIGIT ZERO..TAI LE LETTER AI\n196E..196F    ; disallowed                             # NA   <reserved-196E>..<reserved-196F>\n1970..1974    ; valid                                  # 4.0  TAI LE LETTER TONE-2..TAI LE LETTER TONE-6\n1975..197F    ; disallowed                             # NA   <reserved-1975>..<reserved-197F>\n1980..19A9    ; valid                                  # 4.1  NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW XVA\n19AA..19AB    ; valid                                  # 5.2  NEW TAI LUE LETTER HIGH SUA..NEW TAI LUE LETTER LOW SUA\n19AC..19AF    ; disallowed                             # NA   <reserved-19AC>..<reserved-19AF>\n19B0..19C9    ; valid                                  # 4.1  NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE TONE MARK-2\n19CA..19CF    ; disallowed                             # NA   <reserved-19CA>..<reserved-19CF>\n19D0..19D9    ; valid                                  # 4.1  NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE\n19DA          ; valid                  ;      ; XV8    # 5.2  NEW TAI LUE THAM DIGIT ONE\n19DB..19DD    ; disallowed                             # NA   <reserved-19DB>..<reserved-19DD>\n19DE..19DF    ; valid                  ;      ; NV8    # 4.1  NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV\n19E0..19FF    ; valid                  ;      ; NV8    # 4.0  KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC\n1A00..1A1B    ; valid                                  # 4.1  BUGINESE LETTER KA..BUGINESE VOWEL SIGN AE\n1A1C..1A1D    ; disallowed                             # NA   <reserved-1A1C>..<reserved-1A1D>\n1A1E..1A1F    ; valid                  ;      ; NV8    # 4.1  BUGINESE PALLAWA..BUGINESE END OF SECTION\n1A20..1A5E    ; valid                                  # 5.2  TAI THAM LETTER HIGH KA..TAI THAM CONSONANT SIGN SA\n1A5F          ; disallowed                             # NA   <reserved-1A5F>\n1A60..1A7C    ; valid                                  # 5.2  TAI THAM SIGN SAKOT..TAI THAM SIGN KHUEN-LUE KARAN\n1A7D..1A7E    ; disallowed                             # NA   <reserved-1A7D>..<reserved-1A7E>\n1A7F..1A89    ; valid                                  # 5.2  TAI THAM COMBINING CRYPTOGRAMMIC DOT..TAI THAM HORA DIGIT NINE\n1A8A..1A8F    ; disallowed                             # NA   <reserved-1A8A>..<reserved-1A8F>\n1A90..1A99    ; valid                                  # 5.2  TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE\n1A9A..1A9F    ; disallowed                             # NA   <reserved-1A9A>..<reserved-1A9F>\n1AA0..1AA6    ; valid                  ;      ; NV8    # 5.2  TAI THAM SIGN WIANG..TAI THAM SIGN REVERSED ROTATED RANA\n1AA7          ; valid                                  # 5.2  TAI THAM SIGN MAI YAMOK\n1AA8..1AAD    ; valid                  ;      ; NV8    # 5.2  TAI THAM SIGN KAAN..TAI THAM SIGN CAANG\n1AAE..1AAF    ; disallowed                             # NA   <reserved-1AAE>..<reserved-1AAF>\n1AB0..1ABD    ; valid                                  # 7.0  COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW\n1ABE          ; valid                  ;      ; NV8    # 7.0  COMBINING PARENTHESES OVERLAY\n1ABF..1AC0    ; valid                                  # 13.0 COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER TURNED W BELOW\n1AC1..1ACE    ; valid                                  # 14.0 COMBINING LEFT PARENTHESIS ABOVE LEFT..COMBINING LATIN SMALL LETTER INSULAR T\n1ACF..1AFF    ; disallowed                             # NA   <reserved-1ACF>..<reserved-1AFF>\n1B00..1B4B    ; valid                                  # 5.0  BALINESE SIGN ULU RICEM..BALINESE LETTER ASYURA SASAK\n1B4C          ; valid                                  # 14.0 BALINESE LETTER ARCHAIC JNYA\n1B4D..1B4F    ; disallowed                             # NA   <reserved-1B4D>..<reserved-1B4F>\n1B50..1B59    ; valid                                  # 5.0  BALINESE DIGIT ZERO..BALINESE DIGIT NINE\n1B5A..1B6A    ; valid                  ;      ; NV8    # 5.0  BALINESE PANTI..BALINESE MUSICAL SYMBOL DANG GEDE\n1B6B..1B73    ; valid                                  # 5.0  BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG\n1B74..1B7C    ; valid                  ;      ; NV8    # 5.0  BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING\n1B7D..1B7E    ; valid                  ;      ; NV8    # 14.0 BALINESE PANTI LANTANG..BALINESE PAMADA LANTANG\n1B7F          ; disallowed                             # NA   <reserved-1B7F>\n1B80..1BAA    ; valid                                  # 5.1  SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PAMAAEH\n1BAB..1BAD    ; valid                                  # 6.1  SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA\n1BAE..1BB9    ; valid                                  # 5.1  SUNDANESE LETTER KHA..SUNDANESE DIGIT NINE\n1BBA..1BBF    ; valid                                  # 6.1  SUNDANESE AVAGRAHA..SUNDANESE LETTER FINAL M\n1BC0..1BF3    ; valid                                  # 6.0  BATAK LETTER A..BATAK PANONGONAN\n1BF4..1BFB    ; disallowed                             # NA   <reserved-1BF4>..<reserved-1BFB>\n1BFC..1BFF    ; valid                  ;      ; NV8    # 6.0  BATAK SYMBOL BINDU NA METEK..BATAK SYMBOL BINDU PANGOLAT\n1C00..1C37    ; valid                                  # 5.1  LEPCHA LETTER KA..LEPCHA SIGN NUKTA\n1C38..1C3A    ; disallowed                             # NA   <reserved-1C38>..<reserved-1C3A>\n1C3B..1C3F    ; valid                  ;      ; NV8    # 5.1  LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK\n1C40..1C49    ; valid                                  # 5.1  LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE\n1C4A..1C4C    ; disallowed                             # NA   <reserved-1C4A>..<reserved-1C4C>\n1C4D..1C7D    ; valid                                  # 5.1  LEPCHA LETTER TTA..OL CHIKI AHAD\n1C7E..1C7F    ; valid                  ;      ; NV8    # 5.1  OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD\n1C80          ; mapped                 ; 0432          # 9.0  CYRILLIC SMALL LETTER ROUNDED VE\n1C81          ; mapped                 ; 0434          # 9.0  CYRILLIC SMALL LETTER LONG-LEGGED DE\n1C82          ; mapped                 ; 043E          # 9.0  CYRILLIC SMALL LETTER NARROW O\n1C83          ; mapped                 ; 0441          # 9.0  CYRILLIC SMALL LETTER WIDE ES\n1C84..1C85    ; mapped                 ; 0442          # 9.0  CYRILLIC SMALL LETTER TALL TE..CYRILLIC SMALL LETTER THREE-LEGGED TE\n1C86          ; mapped                 ; 044A          # 9.0  CYRILLIC SMALL LETTER TALL HARD SIGN\n1C87          ; mapped                 ; 0463          # 9.0  CYRILLIC SMALL LETTER TALL YAT\n1C88          ; mapped                 ; A64B          # 9.0  CYRILLIC SMALL LETTER UNBLENDED UK\n1C89..1C8F    ; disallowed                             # NA   <reserved-1C89>..<reserved-1C8F>\n1C90          ; mapped                 ; 10D0          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER AN\n1C91          ; mapped                 ; 10D1          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER BAN\n1C92          ; mapped                 ; 10D2          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER GAN\n1C93          ; mapped                 ; 10D3          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER DON\n1C94          ; mapped                 ; 10D4          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER EN\n1C95          ; mapped                 ; 10D5          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER VIN\n1C96          ; mapped                 ; 10D6          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER ZEN\n1C97          ; mapped                 ; 10D7          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER TAN\n1C98          ; mapped                 ; 10D8          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER IN\n1C99          ; mapped                 ; 10D9          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER KAN\n1C9A          ; mapped                 ; 10DA          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER LAS\n1C9B          ; mapped                 ; 10DB          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER MAN\n1C9C          ; mapped                 ; 10DC          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER NAR\n1C9D          ; mapped                 ; 10DD          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER ON\n1C9E          ; mapped                 ; 10DE          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER PAR\n1C9F          ; mapped                 ; 10DF          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER ZHAR\n1CA0          ; mapped                 ; 10E0          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER RAE\n1CA1          ; mapped                 ; 10E1          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER SAN\n1CA2          ; mapped                 ; 10E2          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER TAR\n1CA3          ; mapped                 ; 10E3          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER UN\n1CA4          ; mapped                 ; 10E4          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER PHAR\n1CA5          ; mapped                 ; 10E5          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER KHAR\n1CA6          ; mapped                 ; 10E6          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER GHAN\n1CA7          ; mapped                 ; 10E7          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER QAR\n1CA8          ; mapped                 ; 10E8          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER SHIN\n1CA9          ; mapped                 ; 10E9          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER CHIN\n1CAA          ; mapped                 ; 10EA          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER CAN\n1CAB          ; mapped                 ; 10EB          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER JIL\n1CAC          ; mapped                 ; 10EC          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER CIL\n1CAD          ; mapped                 ; 10ED          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER CHAR\n1CAE          ; mapped                 ; 10EE          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER XAN\n1CAF          ; mapped                 ; 10EF          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER JHAN\n1CB0          ; mapped                 ; 10F0          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER HAE\n1CB1          ; mapped                 ; 10F1          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER HE\n1CB2          ; mapped                 ; 10F2          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER HIE\n1CB3          ; mapped                 ; 10F3          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER WE\n1CB4          ; mapped                 ; 10F4          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER HAR\n1CB5          ; mapped                 ; 10F5          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER HOE\n1CB6          ; mapped                 ; 10F6          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER FI\n1CB7          ; mapped                 ; 10F7          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER YN\n1CB8          ; mapped                 ; 10F8          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER ELIFI\n1CB9          ; mapped                 ; 10F9          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER TURNED GAN\n1CBA          ; mapped                 ; 10FA          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER AIN\n1CBB..1CBC    ; disallowed                             # NA   <reserved-1CBB>..<reserved-1CBC>\n1CBD          ; mapped                 ; 10FD          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER AEN\n1CBE          ; mapped                 ; 10FE          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER HARD SIGN\n1CBF          ; mapped                 ; 10FF          # 11.0 GEORGIAN MTAVRULI CAPITAL LETTER LABIAL SIGN\n1CC0..1CC7    ; valid                  ;      ; NV8    # 6.1  SUNDANESE PUNCTUATION BINDU SURYA..SUNDANESE PUNCTUATION BINDU BA SATANGA\n1CC8..1CCF    ; disallowed                             # NA   <reserved-1CC8>..<reserved-1CCF>\n1CD0..1CD2    ; valid                                  # 5.2  VEDIC TONE KARSHANA..VEDIC TONE PRENKHA\n1CD3          ; valid                  ;      ; NV8    # 5.2  VEDIC SIGN NIHSHVASA\n1CD4..1CF2    ; valid                                  # 5.2  VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC SIGN ARDHAVISARGA\n1CF3..1CF6    ; valid                                  # 6.1  VEDIC SIGN ROTATED ARDHAVISARGA..VEDIC SIGN UPADHMANIYA\n1CF7          ; valid                                  # 10.0 VEDIC SIGN ATIKRAMA\n1CF8..1CF9    ; valid                                  # 7.0  VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE\n1CFA          ; valid                                  # 12.0 VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA\n1CFB..1CFF    ; disallowed                             # NA   <reserved-1CFB>..<reserved-1CFF>\n1D00..1D2B    ; valid                                  # 4.0  LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL\n1D2C          ; mapped                 ; 0061          # 4.0  MODIFIER LETTER CAPITAL A\n1D2D          ; mapped                 ; 00E6          # 4.0  MODIFIER LETTER CAPITAL AE\n1D2E          ; mapped                 ; 0062          # 4.0  MODIFIER LETTER CAPITAL B\n1D2F          ; valid                                  # 4.0  MODIFIER LETTER CAPITAL BARRED B\n1D30          ; mapped                 ; 0064          # 4.0  MODIFIER LETTER CAPITAL D\n1D31          ; mapped                 ; 0065          # 4.0  MODIFIER LETTER CAPITAL E\n1D32          ; mapped                 ; 01DD          # 4.0  MODIFIER LETTER CAPITAL REVERSED E\n1D33          ; mapped                 ; 0067          # 4.0  MODIFIER LETTER CAPITAL G\n1D34          ; mapped                 ; 0068          # 4.0  MODIFIER LETTER CAPITAL H\n1D35          ; mapped                 ; 0069          # 4.0  MODIFIER LETTER CAPITAL I\n1D36          ; mapped                 ; 006A          # 4.0  MODIFIER LETTER CAPITAL J\n1D37          ; mapped                 ; 006B          # 4.0  MODIFIER LETTER CAPITAL K\n1D38          ; mapped                 ; 006C          # 4.0  MODIFIER LETTER CAPITAL L\n1D39          ; mapped                 ; 006D          # 4.0  MODIFIER LETTER CAPITAL M\n1D3A          ; mapped                 ; 006E          # 4.0  MODIFIER LETTER CAPITAL N\n1D3B          ; valid                                  # 4.0  MODIFIER LETTER CAPITAL REVERSED N\n1D3C          ; mapped                 ; 006F          # 4.0  MODIFIER LETTER CAPITAL O\n1D3D          ; mapped                 ; 0223          # 4.0  MODIFIER LETTER CAPITAL OU\n1D3E          ; mapped                 ; 0070          # 4.0  MODIFIER LETTER CAPITAL P\n1D3F          ; mapped                 ; 0072          # 4.0  MODIFIER LETTER CAPITAL R\n1D40          ; mapped                 ; 0074          # 4.0  MODIFIER LETTER CAPITAL T\n1D41          ; mapped                 ; 0075          # 4.0  MODIFIER LETTER CAPITAL U\n1D42          ; mapped                 ; 0077          # 4.0  MODIFIER LETTER CAPITAL W\n1D43          ; mapped                 ; 0061          # 4.0  MODIFIER LETTER SMALL A\n1D44          ; mapped                 ; 0250          # 4.0  MODIFIER LETTER SMALL TURNED A\n1D45          ; mapped                 ; 0251          # 4.0  MODIFIER LETTER SMALL ALPHA\n1D46          ; mapped                 ; 1D02          # 4.0  MODIFIER LETTER SMALL TURNED AE\n1D47          ; mapped                 ; 0062          # 4.0  MODIFIER LETTER SMALL B\n1D48          ; mapped                 ; 0064          # 4.0  MODIFIER LETTER SMALL D\n1D49          ; mapped                 ; 0065          # 4.0  MODIFIER LETTER SMALL E\n1D4A          ; mapped                 ; 0259          # 4.0  MODIFIER LETTER SMALL SCHWA\n1D4B          ; mapped                 ; 025B          # 4.0  MODIFIER LETTER SMALL OPEN E\n1D4C          ; mapped                 ; 025C          # 4.0  MODIFIER LETTER SMALL TURNED OPEN E\n1D4D          ; mapped                 ; 0067          # 4.0  MODIFIER LETTER SMALL G\n1D4E          ; valid                                  # 4.0  MODIFIER LETTER SMALL TURNED I\n1D4F          ; mapped                 ; 006B          # 4.0  MODIFIER LETTER SMALL K\n1D50          ; mapped                 ; 006D          # 4.0  MODIFIER LETTER SMALL M\n1D51          ; mapped                 ; 014B          # 4.0  MODIFIER LETTER SMALL ENG\n1D52          ; mapped                 ; 006F          # 4.0  MODIFIER LETTER SMALL O\n1D53          ; mapped                 ; 0254          # 4.0  MODIFIER LETTER SMALL OPEN O\n1D54          ; mapped                 ; 1D16          # 4.0  MODIFIER LETTER SMALL TOP HALF O\n1D55          ; mapped                 ; 1D17          # 4.0  MODIFIER LETTER SMALL BOTTOM HALF O\n1D56          ; mapped                 ; 0070          # 4.0  MODIFIER LETTER SMALL P\n1D57          ; mapped                 ; 0074          # 4.0  MODIFIER LETTER SMALL T\n1D58          ; mapped                 ; 0075          # 4.0  MODIFIER LETTER SMALL U\n1D59          ; mapped                 ; 1D1D          # 4.0  MODIFIER LETTER SMALL SIDEWAYS U\n1D5A          ; mapped                 ; 026F          # 4.0  MODIFIER LETTER SMALL TURNED M\n1D5B          ; mapped                 ; 0076          # 4.0  MODIFIER LETTER SMALL V\n1D5C          ; mapped                 ; 1D25          # 4.0  MODIFIER LETTER SMALL AIN\n1D5D          ; mapped                 ; 03B2          # 4.0  MODIFIER LETTER SMALL BETA\n1D5E          ; mapped                 ; 03B3          # 4.0  MODIFIER LETTER SMALL GREEK GAMMA\n1D5F          ; mapped                 ; 03B4          # 4.0  MODIFIER LETTER SMALL DELTA\n1D60          ; mapped                 ; 03C6          # 4.0  MODIFIER LETTER SMALL GREEK PHI\n1D61          ; mapped                 ; 03C7          # 4.0  MODIFIER LETTER SMALL CHI\n1D62          ; mapped                 ; 0069          # 4.0  LATIN SUBSCRIPT SMALL LETTER I\n1D63          ; mapped                 ; 0072          # 4.0  LATIN SUBSCRIPT SMALL LETTER R\n1D64          ; mapped                 ; 0075          # 4.0  LATIN SUBSCRIPT SMALL LETTER U\n1D65          ; mapped                 ; 0076          # 4.0  LATIN SUBSCRIPT SMALL LETTER V\n1D66          ; mapped                 ; 03B2          # 4.0  GREEK SUBSCRIPT SMALL LETTER BETA\n1D67          ; mapped                 ; 03B3          # 4.0  GREEK SUBSCRIPT SMALL LETTER GAMMA\n1D68          ; mapped                 ; 03C1          # 4.0  GREEK SUBSCRIPT SMALL LETTER RHO\n1D69          ; mapped                 ; 03C6          # 4.0  GREEK SUBSCRIPT SMALL LETTER PHI\n1D6A          ; mapped                 ; 03C7          # 4.0  GREEK SUBSCRIPT SMALL LETTER CHI\n1D6B          ; valid                                  # 4.0  LATIN SMALL LETTER UE\n1D6C..1D77    ; valid                                  # 4.1  LATIN SMALL LETTER B WITH MIDDLE TILDE..LATIN SMALL LETTER TURNED G\n1D78          ; mapped                 ; 043D          # 4.1  MODIFIER LETTER CYRILLIC EN\n1D79..1D9A    ; valid                                  # 4.1  LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK\n1D9B          ; mapped                 ; 0252          # 4.1  MODIFIER LETTER SMALL TURNED ALPHA\n1D9C          ; mapped                 ; 0063          # 4.1  MODIFIER LETTER SMALL C\n1D9D          ; mapped                 ; 0255          # 4.1  MODIFIER LETTER SMALL C WITH CURL\n1D9E          ; mapped                 ; 00F0          # 4.1  MODIFIER LETTER SMALL ETH\n1D9F          ; mapped                 ; 025C          # 4.1  MODIFIER LETTER SMALL REVERSED OPEN E\n1DA0          ; mapped                 ; 0066          # 4.1  MODIFIER LETTER SMALL F\n1DA1          ; mapped                 ; 025F          # 4.1  MODIFIER LETTER SMALL DOTLESS J WITH STROKE\n1DA2          ; mapped                 ; 0261          # 4.1  MODIFIER LETTER SMALL SCRIPT G\n1DA3          ; mapped                 ; 0265          # 4.1  MODIFIER LETTER SMALL TURNED H\n1DA4          ; mapped                 ; 0268          # 4.1  MODIFIER LETTER SMALL I WITH STROKE\n1DA5          ; mapped                 ; 0269          # 4.1  MODIFIER LETTER SMALL IOTA\n1DA6          ; mapped                 ; 026A          # 4.1  MODIFIER LETTER SMALL CAPITAL I\n1DA7          ; mapped                 ; 1D7B          # 4.1  MODIFIER LETTER SMALL CAPITAL I WITH STROKE\n1DA8          ; mapped                 ; 029D          # 4.1  MODIFIER LETTER SMALL J WITH CROSSED-TAIL\n1DA9          ; mapped                 ; 026D          # 4.1  MODIFIER LETTER SMALL L WITH RETROFLEX HOOK\n1DAA          ; mapped                 ; 1D85          # 4.1  MODIFIER LETTER SMALL L WITH PALATAL HOOK\n1DAB          ; mapped                 ; 029F          # 4.1  MODIFIER LETTER SMALL CAPITAL L\n1DAC          ; mapped                 ; 0271          # 4.1  MODIFIER LETTER SMALL M WITH HOOK\n1DAD          ; mapped                 ; 0270          # 4.1  MODIFIER LETTER SMALL TURNED M WITH LONG LEG\n1DAE          ; mapped                 ; 0272          # 4.1  MODIFIER LETTER SMALL N WITH LEFT HOOK\n1DAF          ; mapped                 ; 0273          # 4.1  MODIFIER LETTER SMALL N WITH RETROFLEX HOOK\n1DB0          ; mapped                 ; 0274          # 4.1  MODIFIER LETTER SMALL CAPITAL N\n1DB1          ; mapped                 ; 0275          # 4.1  MODIFIER LETTER SMALL BARRED O\n1DB2          ; mapped                 ; 0278          # 4.1  MODIFIER LETTER SMALL PHI\n1DB3          ; mapped                 ; 0282          # 4.1  MODIFIER LETTER SMALL S WITH HOOK\n1DB4          ; mapped                 ; 0283          # 4.1  MODIFIER LETTER SMALL ESH\n1DB5          ; mapped                 ; 01AB          # 4.1  MODIFIER LETTER SMALL T WITH PALATAL HOOK\n1DB6          ; mapped                 ; 0289          # 4.1  MODIFIER LETTER SMALL U BAR\n1DB7          ; mapped                 ; 028A          # 4.1  MODIFIER LETTER SMALL UPSILON\n1DB8          ; mapped                 ; 1D1C          # 4.1  MODIFIER LETTER SMALL CAPITAL U\n1DB9          ; mapped                 ; 028B          # 4.1  MODIFIER LETTER SMALL V WITH HOOK\n1DBA          ; mapped                 ; 028C          # 4.1  MODIFIER LETTER SMALL TURNED V\n1DBB          ; mapped                 ; 007A          # 4.1  MODIFIER LETTER SMALL Z\n1DBC          ; mapped                 ; 0290          # 4.1  MODIFIER LETTER SMALL Z WITH RETROFLEX HOOK\n1DBD          ; mapped                 ; 0291          # 4.1  MODIFIER LETTER SMALL Z WITH CURL\n1DBE          ; mapped                 ; 0292          # 4.1  MODIFIER LETTER SMALL EZH\n1DBF          ; mapped                 ; 03B8          # 4.1  MODIFIER LETTER SMALL THETA\n1DC0..1DC3    ; valid                                  # 4.1  COMBINING DOTTED GRAVE ACCENT..COMBINING SUSPENSION MARK\n1DC4..1DCA    ; valid                                  # 5.0  COMBINING MACRON-ACUTE..COMBINING LATIN SMALL LETTER R BELOW\n1DCB..1DE6    ; valid                                  # 5.1  COMBINING BREVE-MACRON..COMBINING LATIN SMALL LETTER Z\n1DE7..1DF5    ; valid                                  # 7.0  COMBINING LATIN SMALL LETTER ALPHA..COMBINING UP TACK ABOVE\n1DF6..1DF9    ; valid                                  # 10.0 COMBINING KAVYKA ABOVE RIGHT..COMBINING WIDE INVERTED BRIDGE BELOW\n1DFA          ; valid                                  # 14.0 COMBINING DOT BELOW LEFT\n1DFB          ; valid                                  # 9.0  COMBINING DELETION MARK\n1DFC          ; valid                                  # 6.0  COMBINING DOUBLE INVERTED BREVE BELOW\n1DFD          ; valid                                  # 5.2  COMBINING ALMOST EQUAL TO BELOW\n1DFE..1DFF    ; valid                                  # 5.0  COMBINING LEFT ARROWHEAD ABOVE..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW\n1E00          ; mapped                 ; 1E01          # 1.1  LATIN CAPITAL LETTER A WITH RING BELOW\n1E01          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH RING BELOW\n1E02          ; mapped                 ; 1E03          # 1.1  LATIN CAPITAL LETTER B WITH DOT ABOVE\n1E03          ; valid                                  # 1.1  LATIN SMALL LETTER B WITH DOT ABOVE\n1E04          ; mapped                 ; 1E05          # 1.1  LATIN CAPITAL LETTER B WITH DOT BELOW\n1E05          ; valid                                  # 1.1  LATIN SMALL LETTER B WITH DOT BELOW\n1E06          ; mapped                 ; 1E07          # 1.1  LATIN CAPITAL LETTER B WITH LINE BELOW\n1E07          ; valid                                  # 1.1  LATIN SMALL LETTER B WITH LINE BELOW\n1E08          ; mapped                 ; 1E09          # 1.1  LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE\n1E09          ; valid                                  # 1.1  LATIN SMALL LETTER C WITH CEDILLA AND ACUTE\n1E0A          ; mapped                 ; 1E0B          # 1.1  LATIN CAPITAL LETTER D WITH DOT ABOVE\n1E0B          ; valid                                  # 1.1  LATIN SMALL LETTER D WITH DOT ABOVE\n1E0C          ; mapped                 ; 1E0D          # 1.1  LATIN CAPITAL LETTER D WITH DOT BELOW\n1E0D          ; valid                                  # 1.1  LATIN SMALL LETTER D WITH DOT BELOW\n1E0E          ; mapped                 ; 1E0F          # 1.1  LATIN CAPITAL LETTER D WITH LINE BELOW\n1E0F          ; valid                                  # 1.1  LATIN SMALL LETTER D WITH LINE BELOW\n1E10          ; mapped                 ; 1E11          # 1.1  LATIN CAPITAL LETTER D WITH CEDILLA\n1E11          ; valid                                  # 1.1  LATIN SMALL LETTER D WITH CEDILLA\n1E12          ; mapped                 ; 1E13          # 1.1  LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW\n1E13          ; valid                                  # 1.1  LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW\n1E14          ; mapped                 ; 1E15          # 1.1  LATIN CAPITAL LETTER E WITH MACRON AND GRAVE\n1E15          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH MACRON AND GRAVE\n1E16          ; mapped                 ; 1E17          # 1.1  LATIN CAPITAL LETTER E WITH MACRON AND ACUTE\n1E17          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH MACRON AND ACUTE\n1E18          ; mapped                 ; 1E19          # 1.1  LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW\n1E19          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW\n1E1A          ; mapped                 ; 1E1B          # 1.1  LATIN CAPITAL LETTER E WITH TILDE BELOW\n1E1B          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH TILDE BELOW\n1E1C          ; mapped                 ; 1E1D          # 1.1  LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE\n1E1D          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH CEDILLA AND BREVE\n1E1E          ; mapped                 ; 1E1F          # 1.1  LATIN CAPITAL LETTER F WITH DOT ABOVE\n1E1F          ; valid                                  # 1.1  LATIN SMALL LETTER F WITH DOT ABOVE\n1E20          ; mapped                 ; 1E21          # 1.1  LATIN CAPITAL LETTER G WITH MACRON\n1E21          ; valid                                  # 1.1  LATIN SMALL LETTER G WITH MACRON\n1E22          ; mapped                 ; 1E23          # 1.1  LATIN CAPITAL LETTER H WITH DOT ABOVE\n1E23          ; valid                                  # 1.1  LATIN SMALL LETTER H WITH DOT ABOVE\n1E24          ; mapped                 ; 1E25          # 1.1  LATIN CAPITAL LETTER H WITH DOT BELOW\n1E25          ; valid                                  # 1.1  LATIN SMALL LETTER H WITH DOT BELOW\n1E26          ; mapped                 ; 1E27          # 1.1  LATIN CAPITAL LETTER H WITH DIAERESIS\n1E27          ; valid                                  # 1.1  LATIN SMALL LETTER H WITH DIAERESIS\n1E28          ; mapped                 ; 1E29          # 1.1  LATIN CAPITAL LETTER H WITH CEDILLA\n1E29          ; valid                                  # 1.1  LATIN SMALL LETTER H WITH CEDILLA\n1E2A          ; mapped                 ; 1E2B          # 1.1  LATIN CAPITAL LETTER H WITH BREVE BELOW\n1E2B          ; valid                                  # 1.1  LATIN SMALL LETTER H WITH BREVE BELOW\n1E2C          ; mapped                 ; 1E2D          # 1.1  LATIN CAPITAL LETTER I WITH TILDE BELOW\n1E2D          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH TILDE BELOW\n1E2E          ; mapped                 ; 1E2F          # 1.1  LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE\n1E2F          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE\n1E30          ; mapped                 ; 1E31          # 1.1  LATIN CAPITAL LETTER K WITH ACUTE\n1E31          ; valid                                  # 1.1  LATIN SMALL LETTER K WITH ACUTE\n1E32          ; mapped                 ; 1E33          # 1.1  LATIN CAPITAL LETTER K WITH DOT BELOW\n1E33          ; valid                                  # 1.1  LATIN SMALL LETTER K WITH DOT BELOW\n1E34          ; mapped                 ; 1E35          # 1.1  LATIN CAPITAL LETTER K WITH LINE BELOW\n1E35          ; valid                                  # 1.1  LATIN SMALL LETTER K WITH LINE BELOW\n1E36          ; mapped                 ; 1E37          # 1.1  LATIN CAPITAL LETTER L WITH DOT BELOW\n1E37          ; valid                                  # 1.1  LATIN SMALL LETTER L WITH DOT BELOW\n1E38          ; mapped                 ; 1E39          # 1.1  LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON\n1E39          ; valid                                  # 1.1  LATIN SMALL LETTER L WITH DOT BELOW AND MACRON\n1E3A          ; mapped                 ; 1E3B          # 1.1  LATIN CAPITAL LETTER L WITH LINE BELOW\n1E3B          ; valid                                  # 1.1  LATIN SMALL LETTER L WITH LINE BELOW\n1E3C          ; mapped                 ; 1E3D          # 1.1  LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW\n1E3D          ; valid                                  # 1.1  LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW\n1E3E          ; mapped                 ; 1E3F          # 1.1  LATIN CAPITAL LETTER M WITH ACUTE\n1E3F          ; valid                                  # 1.1  LATIN SMALL LETTER M WITH ACUTE\n1E40          ; mapped                 ; 1E41          # 1.1  LATIN CAPITAL LETTER M WITH DOT ABOVE\n1E41          ; valid                                  # 1.1  LATIN SMALL LETTER M WITH DOT ABOVE\n1E42          ; mapped                 ; 1E43          # 1.1  LATIN CAPITAL LETTER M WITH DOT BELOW\n1E43          ; valid                                  # 1.1  LATIN SMALL LETTER M WITH DOT BELOW\n1E44          ; mapped                 ; 1E45          # 1.1  LATIN CAPITAL LETTER N WITH DOT ABOVE\n1E45          ; valid                                  # 1.1  LATIN SMALL LETTER N WITH DOT ABOVE\n1E46          ; mapped                 ; 1E47          # 1.1  LATIN CAPITAL LETTER N WITH DOT BELOW\n1E47          ; valid                                  # 1.1  LATIN SMALL LETTER N WITH DOT BELOW\n1E48          ; mapped                 ; 1E49          # 1.1  LATIN CAPITAL LETTER N WITH LINE BELOW\n1E49          ; valid                                  # 1.1  LATIN SMALL LETTER N WITH LINE BELOW\n1E4A          ; mapped                 ; 1E4B          # 1.1  LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW\n1E4B          ; valid                                  # 1.1  LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW\n1E4C          ; mapped                 ; 1E4D          # 1.1  LATIN CAPITAL LETTER O WITH TILDE AND ACUTE\n1E4D          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH TILDE AND ACUTE\n1E4E          ; mapped                 ; 1E4F          # 1.1  LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS\n1E4F          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH TILDE AND DIAERESIS\n1E50          ; mapped                 ; 1E51          # 1.1  LATIN CAPITAL LETTER O WITH MACRON AND GRAVE\n1E51          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH MACRON AND GRAVE\n1E52          ; mapped                 ; 1E53          # 1.1  LATIN CAPITAL LETTER O WITH MACRON AND ACUTE\n1E53          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH MACRON AND ACUTE\n1E54          ; mapped                 ; 1E55          # 1.1  LATIN CAPITAL LETTER P WITH ACUTE\n1E55          ; valid                                  # 1.1  LATIN SMALL LETTER P WITH ACUTE\n1E56          ; mapped                 ; 1E57          # 1.1  LATIN CAPITAL LETTER P WITH DOT ABOVE\n1E57          ; valid                                  # 1.1  LATIN SMALL LETTER P WITH DOT ABOVE\n1E58          ; mapped                 ; 1E59          # 1.1  LATIN CAPITAL LETTER R WITH DOT ABOVE\n1E59          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH DOT ABOVE\n1E5A          ; mapped                 ; 1E5B          # 1.1  LATIN CAPITAL LETTER R WITH DOT BELOW\n1E5B          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH DOT BELOW\n1E5C          ; mapped                 ; 1E5D          # 1.1  LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON\n1E5D          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH DOT BELOW AND MACRON\n1E5E          ; mapped                 ; 1E5F          # 1.1  LATIN CAPITAL LETTER R WITH LINE BELOW\n1E5F          ; valid                                  # 1.1  LATIN SMALL LETTER R WITH LINE BELOW\n1E60          ; mapped                 ; 1E61          # 1.1  LATIN CAPITAL LETTER S WITH DOT ABOVE\n1E61          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH DOT ABOVE\n1E62          ; mapped                 ; 1E63          # 1.1  LATIN CAPITAL LETTER S WITH DOT BELOW\n1E63          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH DOT BELOW\n1E64          ; mapped                 ; 1E65          # 1.1  LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE\n1E65          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE\n1E66          ; mapped                 ; 1E67          # 1.1  LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE\n1E67          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH CARON AND DOT ABOVE\n1E68          ; mapped                 ; 1E69          # 1.1  LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE\n1E69          ; valid                                  # 1.1  LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE\n1E6A          ; mapped                 ; 1E6B          # 1.1  LATIN CAPITAL LETTER T WITH DOT ABOVE\n1E6B          ; valid                                  # 1.1  LATIN SMALL LETTER T WITH DOT ABOVE\n1E6C          ; mapped                 ; 1E6D          # 1.1  LATIN CAPITAL LETTER T WITH DOT BELOW\n1E6D          ; valid                                  # 1.1  LATIN SMALL LETTER T WITH DOT BELOW\n1E6E          ; mapped                 ; 1E6F          # 1.1  LATIN CAPITAL LETTER T WITH LINE BELOW\n1E6F          ; valid                                  # 1.1  LATIN SMALL LETTER T WITH LINE BELOW\n1E70          ; mapped                 ; 1E71          # 1.1  LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW\n1E71          ; valid                                  # 1.1  LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW\n1E72          ; mapped                 ; 1E73          # 1.1  LATIN CAPITAL LETTER U WITH DIAERESIS BELOW\n1E73          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH DIAERESIS BELOW\n1E74          ; mapped                 ; 1E75          # 1.1  LATIN CAPITAL LETTER U WITH TILDE BELOW\n1E75          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH TILDE BELOW\n1E76          ; mapped                 ; 1E77          # 1.1  LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW\n1E77          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW\n1E78          ; mapped                 ; 1E79          # 1.1  LATIN CAPITAL LETTER U WITH TILDE AND ACUTE\n1E79          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH TILDE AND ACUTE\n1E7A          ; mapped                 ; 1E7B          # 1.1  LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS\n1E7B          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH MACRON AND DIAERESIS\n1E7C          ; mapped                 ; 1E7D          # 1.1  LATIN CAPITAL LETTER V WITH TILDE\n1E7D          ; valid                                  # 1.1  LATIN SMALL LETTER V WITH TILDE\n1E7E          ; mapped                 ; 1E7F          # 1.1  LATIN CAPITAL LETTER V WITH DOT BELOW\n1E7F          ; valid                                  # 1.1  LATIN SMALL LETTER V WITH DOT BELOW\n1E80          ; mapped                 ; 1E81          # 1.1  LATIN CAPITAL LETTER W WITH GRAVE\n1E81          ; valid                                  # 1.1  LATIN SMALL LETTER W WITH GRAVE\n1E82          ; mapped                 ; 1E83          # 1.1  LATIN CAPITAL LETTER W WITH ACUTE\n1E83          ; valid                                  # 1.1  LATIN SMALL LETTER W WITH ACUTE\n1E84          ; mapped                 ; 1E85          # 1.1  LATIN CAPITAL LETTER W WITH DIAERESIS\n1E85          ; valid                                  # 1.1  LATIN SMALL LETTER W WITH DIAERESIS\n1E86          ; mapped                 ; 1E87          # 1.1  LATIN CAPITAL LETTER W WITH DOT ABOVE\n1E87          ; valid                                  # 1.1  LATIN SMALL LETTER W WITH DOT ABOVE\n1E88          ; mapped                 ; 1E89          # 1.1  LATIN CAPITAL LETTER W WITH DOT BELOW\n1E89          ; valid                                  # 1.1  LATIN SMALL LETTER W WITH DOT BELOW\n1E8A          ; mapped                 ; 1E8B          # 1.1  LATIN CAPITAL LETTER X WITH DOT ABOVE\n1E8B          ; valid                                  # 1.1  LATIN SMALL LETTER X WITH DOT ABOVE\n1E8C          ; mapped                 ; 1E8D          # 1.1  LATIN CAPITAL LETTER X WITH DIAERESIS\n1E8D          ; valid                                  # 1.1  LATIN SMALL LETTER X WITH DIAERESIS\n1E8E          ; mapped                 ; 1E8F          # 1.1  LATIN CAPITAL LETTER Y WITH DOT ABOVE\n1E8F          ; valid                                  # 1.1  LATIN SMALL LETTER Y WITH DOT ABOVE\n1E90          ; mapped                 ; 1E91          # 1.1  LATIN CAPITAL LETTER Z WITH CIRCUMFLEX\n1E91          ; valid                                  # 1.1  LATIN SMALL LETTER Z WITH CIRCUMFLEX\n1E92          ; mapped                 ; 1E93          # 1.1  LATIN CAPITAL LETTER Z WITH DOT BELOW\n1E93          ; valid                                  # 1.1  LATIN SMALL LETTER Z WITH DOT BELOW\n1E94          ; mapped                 ; 1E95          # 1.1  LATIN CAPITAL LETTER Z WITH LINE BELOW\n1E95..1E99    ; valid                                  # 1.1  LATIN SMALL LETTER Z WITH LINE BELOW..LATIN SMALL LETTER Y WITH RING ABOVE\n1E9A          ; mapped                 ; 0061 02BE     # 1.1  LATIN SMALL LETTER A WITH RIGHT HALF RING\n1E9B          ; mapped                 ; 1E61          # 2.0  LATIN SMALL LETTER LONG S WITH DOT ABOVE\n1E9C..1E9D    ; valid                                  # 5.1  LATIN SMALL LETTER LONG S WITH DIAGONAL STROKE..LATIN SMALL LETTER LONG S WITH HIGH STROKE\n1E9E          ; mapped                 ; 00DF          # 5.1  LATIN CAPITAL LETTER SHARP S\n1E9F          ; valid                                  # 5.1  LATIN SMALL LETTER DELTA\n1EA0          ; mapped                 ; 1EA1          # 1.1  LATIN CAPITAL LETTER A WITH DOT BELOW\n1EA1          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH DOT BELOW\n1EA2          ; mapped                 ; 1EA3          # 1.1  LATIN CAPITAL LETTER A WITH HOOK ABOVE\n1EA3          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH HOOK ABOVE\n1EA4          ; mapped                 ; 1EA5          # 1.1  LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE\n1EA5          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE\n1EA6          ; mapped                 ; 1EA7          # 1.1  LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE\n1EA7          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE\n1EA8          ; mapped                 ; 1EA9          # 1.1  LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE\n1EA9          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE\n1EAA          ; mapped                 ; 1EAB          # 1.1  LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE\n1EAB          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE\n1EAC          ; mapped                 ; 1EAD          # 1.1  LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW\n1EAD          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW\n1EAE          ; mapped                 ; 1EAF          # 1.1  LATIN CAPITAL LETTER A WITH BREVE AND ACUTE\n1EAF          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH BREVE AND ACUTE\n1EB0          ; mapped                 ; 1EB1          # 1.1  LATIN CAPITAL LETTER A WITH BREVE AND GRAVE\n1EB1          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH BREVE AND GRAVE\n1EB2          ; mapped                 ; 1EB3          # 1.1  LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE\n1EB3          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE\n1EB4          ; mapped                 ; 1EB5          # 1.1  LATIN CAPITAL LETTER A WITH BREVE AND TILDE\n1EB5          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH BREVE AND TILDE\n1EB6          ; mapped                 ; 1EB7          # 1.1  LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW\n1EB7          ; valid                                  # 1.1  LATIN SMALL LETTER A WITH BREVE AND DOT BELOW\n1EB8          ; mapped                 ; 1EB9          # 1.1  LATIN CAPITAL LETTER E WITH DOT BELOW\n1EB9          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH DOT BELOW\n1EBA          ; mapped                 ; 1EBB          # 1.1  LATIN CAPITAL LETTER E WITH HOOK ABOVE\n1EBB          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH HOOK ABOVE\n1EBC          ; mapped                 ; 1EBD          # 1.1  LATIN CAPITAL LETTER E WITH TILDE\n1EBD          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH TILDE\n1EBE          ; mapped                 ; 1EBF          # 1.1  LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE\n1EBF          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE\n1EC0          ; mapped                 ; 1EC1          # 1.1  LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE\n1EC1          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE\n1EC2          ; mapped                 ; 1EC3          # 1.1  LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE\n1EC3          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE\n1EC4          ; mapped                 ; 1EC5          # 1.1  LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE\n1EC5          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE\n1EC6          ; mapped                 ; 1EC7          # 1.1  LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW\n1EC7          ; valid                                  # 1.1  LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW\n1EC8          ; mapped                 ; 1EC9          # 1.1  LATIN CAPITAL LETTER I WITH HOOK ABOVE\n1EC9          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH HOOK ABOVE\n1ECA          ; mapped                 ; 1ECB          # 1.1  LATIN CAPITAL LETTER I WITH DOT BELOW\n1ECB          ; valid                                  # 1.1  LATIN SMALL LETTER I WITH DOT BELOW\n1ECC          ; mapped                 ; 1ECD          # 1.1  LATIN CAPITAL LETTER O WITH DOT BELOW\n1ECD          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH DOT BELOW\n1ECE          ; mapped                 ; 1ECF          # 1.1  LATIN CAPITAL LETTER O WITH HOOK ABOVE\n1ECF          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH HOOK ABOVE\n1ED0          ; mapped                 ; 1ED1          # 1.1  LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE\n1ED1          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE\n1ED2          ; mapped                 ; 1ED3          # 1.1  LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE\n1ED3          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE\n1ED4          ; mapped                 ; 1ED5          # 1.1  LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE\n1ED5          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE\n1ED6          ; mapped                 ; 1ED7          # 1.1  LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE\n1ED7          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE\n1ED8          ; mapped                 ; 1ED9          # 1.1  LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW\n1ED9          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW\n1EDA          ; mapped                 ; 1EDB          # 1.1  LATIN CAPITAL LETTER O WITH HORN AND ACUTE\n1EDB          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH HORN AND ACUTE\n1EDC          ; mapped                 ; 1EDD          # 1.1  LATIN CAPITAL LETTER O WITH HORN AND GRAVE\n1EDD          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH HORN AND GRAVE\n1EDE          ; mapped                 ; 1EDF          # 1.1  LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE\n1EDF          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE\n1EE0          ; mapped                 ; 1EE1          # 1.1  LATIN CAPITAL LETTER O WITH HORN AND TILDE\n1EE1          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH HORN AND TILDE\n1EE2          ; mapped                 ; 1EE3          # 1.1  LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW\n1EE3          ; valid                                  # 1.1  LATIN SMALL LETTER O WITH HORN AND DOT BELOW\n1EE4          ; mapped                 ; 1EE5          # 1.1  LATIN CAPITAL LETTER U WITH DOT BELOW\n1EE5          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH DOT BELOW\n1EE6          ; mapped                 ; 1EE7          # 1.1  LATIN CAPITAL LETTER U WITH HOOK ABOVE\n1EE7          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH HOOK ABOVE\n1EE8          ; mapped                 ; 1EE9          # 1.1  LATIN CAPITAL LETTER U WITH HORN AND ACUTE\n1EE9          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH HORN AND ACUTE\n1EEA          ; mapped                 ; 1EEB          # 1.1  LATIN CAPITAL LETTER U WITH HORN AND GRAVE\n1EEB          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH HORN AND GRAVE\n1EEC          ; mapped                 ; 1EED          # 1.1  LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE\n1EED          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE\n1EEE          ; mapped                 ; 1EEF          # 1.1  LATIN CAPITAL LETTER U WITH HORN AND TILDE\n1EEF          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH HORN AND TILDE\n1EF0          ; mapped                 ; 1EF1          # 1.1  LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW\n1EF1          ; valid                                  # 1.1  LATIN SMALL LETTER U WITH HORN AND DOT BELOW\n1EF2          ; mapped                 ; 1EF3          # 1.1  LATIN CAPITAL LETTER Y WITH GRAVE\n1EF3          ; valid                                  # 1.1  LATIN SMALL LETTER Y WITH GRAVE\n1EF4          ; mapped                 ; 1EF5          # 1.1  LATIN CAPITAL LETTER Y WITH DOT BELOW\n1EF5          ; valid                                  # 1.1  LATIN SMALL LETTER Y WITH DOT BELOW\n1EF6          ; mapped                 ; 1EF7          # 1.1  LATIN CAPITAL LETTER Y WITH HOOK ABOVE\n1EF7          ; valid                                  # 1.1  LATIN SMALL LETTER Y WITH HOOK ABOVE\n1EF8          ; mapped                 ; 1EF9          # 1.1  LATIN CAPITAL LETTER Y WITH TILDE\n1EF9          ; valid                                  # 1.1  LATIN SMALL LETTER Y WITH TILDE\n1EFA          ; mapped                 ; 1EFB          # 5.1  LATIN CAPITAL LETTER MIDDLE-WELSH LL\n1EFB          ; valid                                  # 5.1  LATIN SMALL LETTER MIDDLE-WELSH LL\n1EFC          ; mapped                 ; 1EFD          # 5.1  LATIN CAPITAL LETTER MIDDLE-WELSH V\n1EFD          ; valid                                  # 5.1  LATIN SMALL LETTER MIDDLE-WELSH V\n1EFE          ; mapped                 ; 1EFF          # 5.1  LATIN CAPITAL LETTER Y WITH LOOP\n1EFF          ; valid                                  # 5.1  LATIN SMALL LETTER Y WITH LOOP\n1F00..1F07    ; valid                                  # 1.1  GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI\n1F08          ; mapped                 ; 1F00          # 1.1  GREEK CAPITAL LETTER ALPHA WITH PSILI\n1F09          ; mapped                 ; 1F01          # 1.1  GREEK CAPITAL LETTER ALPHA WITH DASIA\n1F0A          ; mapped                 ; 1F02          # 1.1  GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA\n1F0B          ; mapped                 ; 1F03          # 1.1  GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA\n1F0C          ; mapped                 ; 1F04          # 1.1  GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA\n1F0D          ; mapped                 ; 1F05          # 1.1  GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA\n1F0E          ; mapped                 ; 1F06          # 1.1  GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI\n1F0F          ; mapped                 ; 1F07          # 1.1  GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI\n1F10..1F15    ; valid                                  # 1.1  GREEK SMALL LETTER EPSILON WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA\n1F16..1F17    ; disallowed                             # NA   <reserved-1F16>..<reserved-1F17>\n1F18          ; mapped                 ; 1F10          # 1.1  GREEK CAPITAL LETTER EPSILON WITH PSILI\n1F19          ; mapped                 ; 1F11          # 1.1  GREEK CAPITAL LETTER EPSILON WITH DASIA\n1F1A          ; mapped                 ; 1F12          # 1.1  GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA\n1F1B          ; mapped                 ; 1F13          # 1.1  GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA\n1F1C          ; mapped                 ; 1F14          # 1.1  GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA\n1F1D          ; mapped                 ; 1F15          # 1.1  GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA\n1F1E..1F1F    ; disallowed                             # NA   <reserved-1F1E>..<reserved-1F1F>\n1F20..1F27    ; valid                                  # 1.1  GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI\n1F28          ; mapped                 ; 1F20          # 1.1  GREEK CAPITAL LETTER ETA WITH PSILI\n1F29          ; mapped                 ; 1F21          # 1.1  GREEK CAPITAL LETTER ETA WITH DASIA\n1F2A          ; mapped                 ; 1F22          # 1.1  GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA\n1F2B          ; mapped                 ; 1F23          # 1.1  GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA\n1F2C          ; mapped                 ; 1F24          # 1.1  GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA\n1F2D          ; mapped                 ; 1F25          # 1.1  GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA\n1F2E          ; mapped                 ; 1F26          # 1.1  GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI\n1F2F          ; mapped                 ; 1F27          # 1.1  GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI\n1F30..1F37    ; valid                                  # 1.1  GREEK SMALL LETTER IOTA WITH PSILI..GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI\n1F38          ; mapped                 ; 1F30          # 1.1  GREEK CAPITAL LETTER IOTA WITH PSILI\n1F39          ; mapped                 ; 1F31          # 1.1  GREEK CAPITAL LETTER IOTA WITH DASIA\n1F3A          ; mapped                 ; 1F32          # 1.1  GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA\n1F3B          ; mapped                 ; 1F33          # 1.1  GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA\n1F3C          ; mapped                 ; 1F34          # 1.1  GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA\n1F3D          ; mapped                 ; 1F35          # 1.1  GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA\n1F3E          ; mapped                 ; 1F36          # 1.1  GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI\n1F3F          ; mapped                 ; 1F37          # 1.1  GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI\n1F40..1F45    ; valid                                  # 1.1  GREEK SMALL LETTER OMICRON WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA\n1F46..1F47    ; disallowed                             # NA   <reserved-1F46>..<reserved-1F47>\n1F48          ; mapped                 ; 1F40          # 1.1  GREEK CAPITAL LETTER OMICRON WITH PSILI\n1F49          ; mapped                 ; 1F41          # 1.1  GREEK CAPITAL LETTER OMICRON WITH DASIA\n1F4A          ; mapped                 ; 1F42          # 1.1  GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA\n1F4B          ; mapped                 ; 1F43          # 1.1  GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA\n1F4C          ; mapped                 ; 1F44          # 1.1  GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA\n1F4D          ; mapped                 ; 1F45          # 1.1  GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA\n1F4E..1F4F    ; disallowed                             # NA   <reserved-1F4E>..<reserved-1F4F>\n1F50..1F57    ; valid                                  # 1.1  GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI\n1F58          ; disallowed                             # NA   <reserved-1F58>\n1F59          ; mapped                 ; 1F51          # 1.1  GREEK CAPITAL LETTER UPSILON WITH DASIA\n1F5A          ; disallowed                             # NA   <reserved-1F5A>\n1F5B          ; mapped                 ; 1F53          # 1.1  GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA\n1F5C          ; disallowed                             # NA   <reserved-1F5C>\n1F5D          ; mapped                 ; 1F55          # 1.1  GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA\n1F5E          ; disallowed                             # NA   <reserved-1F5E>\n1F5F          ; mapped                 ; 1F57          # 1.1  GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI\n1F60..1F67    ; valid                                  # 1.1  GREEK SMALL LETTER OMEGA WITH PSILI..GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI\n1F68          ; mapped                 ; 1F60          # 1.1  GREEK CAPITAL LETTER OMEGA WITH PSILI\n1F69          ; mapped                 ; 1F61          # 1.1  GREEK CAPITAL LETTER OMEGA WITH DASIA\n1F6A          ; mapped                 ; 1F62          # 1.1  GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA\n1F6B          ; mapped                 ; 1F63          # 1.1  GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA\n1F6C          ; mapped                 ; 1F64          # 1.1  GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA\n1F6D          ; mapped                 ; 1F65          # 1.1  GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA\n1F6E          ; mapped                 ; 1F66          # 1.1  GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI\n1F6F          ; mapped                 ; 1F67          # 1.1  GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI\n1F70          ; valid                                  # 1.1  GREEK SMALL LETTER ALPHA WITH VARIA\n1F71          ; mapped                 ; 03AC          # 1.1  GREEK SMALL LETTER ALPHA WITH OXIA\n1F72          ; valid                                  # 1.1  GREEK SMALL LETTER EPSILON WITH VARIA\n1F73          ; mapped                 ; 03AD          # 1.1  GREEK SMALL LETTER EPSILON WITH OXIA\n1F74          ; valid                                  # 1.1  GREEK SMALL LETTER ETA WITH VARIA\n1F75          ; mapped                 ; 03AE          # 1.1  GREEK SMALL LETTER ETA WITH OXIA\n1F76          ; valid                                  # 1.1  GREEK SMALL LETTER IOTA WITH VARIA\n1F77          ; mapped                 ; 03AF          # 1.1  GREEK SMALL LETTER IOTA WITH OXIA\n1F78          ; valid                                  # 1.1  GREEK SMALL LETTER OMICRON WITH VARIA\n1F79          ; mapped                 ; 03CC          # 1.1  GREEK SMALL LETTER OMICRON WITH OXIA\n1F7A          ; valid                                  # 1.1  GREEK SMALL LETTER UPSILON WITH VARIA\n1F7B          ; mapped                 ; 03CD          # 1.1  GREEK SMALL LETTER UPSILON WITH OXIA\n1F7C          ; valid                                  # 1.1  GREEK SMALL LETTER OMEGA WITH VARIA\n1F7D          ; mapped                 ; 03CE          # 1.1  GREEK SMALL LETTER OMEGA WITH OXIA\n1F7E..1F7F    ; disallowed                             # NA   <reserved-1F7E>..<reserved-1F7F>\n1F80          ; mapped                 ; 1F00 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI\n1F81          ; mapped                 ; 1F01 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI\n1F82          ; mapped                 ; 1F02 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI\n1F83          ; mapped                 ; 1F03 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI\n1F84          ; mapped                 ; 1F04 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI\n1F85          ; mapped                 ; 1F05 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI\n1F86          ; mapped                 ; 1F06 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI\n1F87          ; mapped                 ; 1F07 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI\n1F88          ; mapped                 ; 1F00 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI\n1F89          ; mapped                 ; 1F01 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI\n1F8A          ; mapped                 ; 1F02 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI\n1F8B          ; mapped                 ; 1F03 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI\n1F8C          ; mapped                 ; 1F04 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI\n1F8D          ; mapped                 ; 1F05 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI\n1F8E          ; mapped                 ; 1F06 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI\n1F8F          ; mapped                 ; 1F07 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI\n1F90          ; mapped                 ; 1F20 03B9     # 1.1  GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI\n1F91          ; mapped                 ; 1F21 03B9     # 1.1  GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI\n1F92          ; mapped                 ; 1F22 03B9     # 1.1  GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI\n1F93          ; mapped                 ; 1F23 03B9     # 1.1  GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI\n1F94          ; mapped                 ; 1F24 03B9     # 1.1  GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI\n1F95          ; mapped                 ; 1F25 03B9     # 1.1  GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI\n1F96          ; mapped                 ; 1F26 03B9     # 1.1  GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI\n1F97          ; mapped                 ; 1F27 03B9     # 1.1  GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI\n1F98          ; mapped                 ; 1F20 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI\n1F99          ; mapped                 ; 1F21 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI\n1F9A          ; mapped                 ; 1F22 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI\n1F9B          ; mapped                 ; 1F23 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI\n1F9C          ; mapped                 ; 1F24 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI\n1F9D          ; mapped                 ; 1F25 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI\n1F9E          ; mapped                 ; 1F26 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI\n1F9F          ; mapped                 ; 1F27 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI\n1FA0          ; mapped                 ; 1F60 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI\n1FA1          ; mapped                 ; 1F61 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI\n1FA2          ; mapped                 ; 1F62 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI\n1FA3          ; mapped                 ; 1F63 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI\n1FA4          ; mapped                 ; 1F64 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI\n1FA5          ; mapped                 ; 1F65 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI\n1FA6          ; mapped                 ; 1F66 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI\n1FA7          ; mapped                 ; 1F67 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI\n1FA8          ; mapped                 ; 1F60 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI\n1FA9          ; mapped                 ; 1F61 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI\n1FAA          ; mapped                 ; 1F62 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI\n1FAB          ; mapped                 ; 1F63 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI\n1FAC          ; mapped                 ; 1F64 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI\n1FAD          ; mapped                 ; 1F65 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI\n1FAE          ; mapped                 ; 1F66 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI\n1FAF          ; mapped                 ; 1F67 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI\n1FB0..1FB1    ; valid                                  # 1.1  GREEK SMALL LETTER ALPHA WITH VRACHY..GREEK SMALL LETTER ALPHA WITH MACRON\n1FB2          ; mapped                 ; 1F70 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI\n1FB3          ; mapped                 ; 03B1 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI\n1FB4          ; mapped                 ; 03AC 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI\n1FB5          ; disallowed                             # NA   <reserved-1FB5>\n1FB6          ; valid                                  # 1.1  GREEK SMALL LETTER ALPHA WITH PERISPOMENI\n1FB7          ; mapped                 ; 1FB6 03B9     # 1.1  GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI\n1FB8          ; mapped                 ; 1FB0          # 1.1  GREEK CAPITAL LETTER ALPHA WITH VRACHY\n1FB9          ; mapped                 ; 1FB1          # 1.1  GREEK CAPITAL LETTER ALPHA WITH MACRON\n1FBA          ; mapped                 ; 1F70          # 1.1  GREEK CAPITAL LETTER ALPHA WITH VARIA\n1FBB          ; mapped                 ; 03AC          # 1.1  GREEK CAPITAL LETTER ALPHA WITH OXIA\n1FBC          ; mapped                 ; 03B1 03B9     # 1.1  GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI\n1FBD          ; disallowed_STD3_mapped ; 0020 0313     # 1.1  GREEK KORONIS\n1FBE          ; mapped                 ; 03B9          # 1.1  GREEK PROSGEGRAMMENI\n1FBF          ; disallowed_STD3_mapped ; 0020 0313     # 1.1  GREEK PSILI\n1FC0          ; disallowed_STD3_mapped ; 0020 0342     # 1.1  GREEK PERISPOMENI\n1FC1          ; disallowed_STD3_mapped ; 0020 0308 0342 #1.1  GREEK DIALYTIKA AND PERISPOMENI\n1FC2          ; mapped                 ; 1F74 03B9     # 1.1  GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI\n1FC3          ; mapped                 ; 03B7 03B9     # 1.1  GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI\n1FC4          ; mapped                 ; 03AE 03B9     # 1.1  GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI\n1FC5          ; disallowed                             # NA   <reserved-1FC5>\n1FC6          ; valid                                  # 1.1  GREEK SMALL LETTER ETA WITH PERISPOMENI\n1FC7          ; mapped                 ; 1FC6 03B9     # 1.1  GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI\n1FC8          ; mapped                 ; 1F72          # 1.1  GREEK CAPITAL LETTER EPSILON WITH VARIA\n1FC9          ; mapped                 ; 03AD          # 1.1  GREEK CAPITAL LETTER EPSILON WITH OXIA\n1FCA          ; mapped                 ; 1F74          # 1.1  GREEK CAPITAL LETTER ETA WITH VARIA\n1FCB          ; mapped                 ; 03AE          # 1.1  GREEK CAPITAL LETTER ETA WITH OXIA\n1FCC          ; mapped                 ; 03B7 03B9     # 1.1  GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI\n1FCD          ; disallowed_STD3_mapped ; 0020 0313 0300 #1.1  GREEK PSILI AND VARIA\n1FCE          ; disallowed_STD3_mapped ; 0020 0313 0301 #1.1  GREEK PSILI AND OXIA\n1FCF          ; disallowed_STD3_mapped ; 0020 0313 0342 #1.1  GREEK PSILI AND PERISPOMENI\n1FD0..1FD2    ; valid                                  # 1.1  GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA\n1FD3          ; mapped                 ; 0390          # 1.1  GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA\n1FD4..1FD5    ; disallowed                             # NA   <reserved-1FD4>..<reserved-1FD5>\n1FD6..1FD7    ; valid                                  # 1.1  GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI\n1FD8          ; mapped                 ; 1FD0          # 1.1  GREEK CAPITAL LETTER IOTA WITH VRACHY\n1FD9          ; mapped                 ; 1FD1          # 1.1  GREEK CAPITAL LETTER IOTA WITH MACRON\n1FDA          ; mapped                 ; 1F76          # 1.1  GREEK CAPITAL LETTER IOTA WITH VARIA\n1FDB          ; mapped                 ; 03AF          # 1.1  GREEK CAPITAL LETTER IOTA WITH OXIA\n1FDC          ; disallowed                             # NA   <reserved-1FDC>\n1FDD          ; disallowed_STD3_mapped ; 0020 0314 0300 #1.1  GREEK DASIA AND VARIA\n1FDE          ; disallowed_STD3_mapped ; 0020 0314 0301 #1.1  GREEK DASIA AND OXIA\n1FDF          ; disallowed_STD3_mapped ; 0020 0314 0342 #1.1  GREEK DASIA AND PERISPOMENI\n1FE0..1FE2    ; valid                                  # 1.1  GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA\n1FE3          ; mapped                 ; 03B0          # 1.1  GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA\n1FE4..1FE7    ; valid                                  # 1.1  GREEK SMALL LETTER RHO WITH PSILI..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI\n1FE8          ; mapped                 ; 1FE0          # 1.1  GREEK CAPITAL LETTER UPSILON WITH VRACHY\n1FE9          ; mapped                 ; 1FE1          # 1.1  GREEK CAPITAL LETTER UPSILON WITH MACRON\n1FEA          ; mapped                 ; 1F7A          # 1.1  GREEK CAPITAL LETTER UPSILON WITH VARIA\n1FEB          ; mapped                 ; 03CD          # 1.1  GREEK CAPITAL LETTER UPSILON WITH OXIA\n1FEC          ; mapped                 ; 1FE5          # 1.1  GREEK CAPITAL LETTER RHO WITH DASIA\n1FED          ; disallowed_STD3_mapped ; 0020 0308 0300 #1.1  GREEK DIALYTIKA AND VARIA\n1FEE          ; disallowed_STD3_mapped ; 0020 0308 0301 #1.1  GREEK DIALYTIKA AND OXIA\n1FEF          ; disallowed_STD3_mapped ; 0060          # 1.1  GREEK VARIA\n1FF0..1FF1    ; disallowed                             # NA   <reserved-1FF0>..<reserved-1FF1>\n1FF2          ; mapped                 ; 1F7C 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI\n1FF3          ; mapped                 ; 03C9 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI\n1FF4          ; mapped                 ; 03CE 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI\n1FF5          ; disallowed                             # NA   <reserved-1FF5>\n1FF6          ; valid                                  # 1.1  GREEK SMALL LETTER OMEGA WITH PERISPOMENI\n1FF7          ; mapped                 ; 1FF6 03B9     # 1.1  GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI\n1FF8          ; mapped                 ; 1F78          # 1.1  GREEK CAPITAL LETTER OMICRON WITH VARIA\n1FF9          ; mapped                 ; 03CC          # 1.1  GREEK CAPITAL LETTER OMICRON WITH OXIA\n1FFA          ; mapped                 ; 1F7C          # 1.1  GREEK CAPITAL LETTER OMEGA WITH VARIA\n1FFB          ; mapped                 ; 03CE          # 1.1  GREEK CAPITAL LETTER OMEGA WITH OXIA\n1FFC          ; mapped                 ; 03C9 03B9     # 1.1  GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI\n1FFD          ; disallowed_STD3_mapped ; 0020 0301     # 1.1  GREEK OXIA\n1FFE          ; disallowed_STD3_mapped ; 0020 0314     # 1.1  GREEK DASIA\n1FFF          ; disallowed                             # NA   <reserved-1FFF>\n2000..200A    ; disallowed_STD3_mapped ; 0020          # 1.1  EN QUAD..HAIR SPACE\n200B          ; ignored                                # 1.1  ZERO WIDTH SPACE\n200C..200D    ; deviation              ;               # 1.1  ZERO WIDTH NON-JOINER..ZERO WIDTH JOINER\n200E..200F    ; disallowed                             # 1.1  LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK\n2010          ; valid                  ;      ; NV8    # 1.1  HYPHEN\n2011          ; mapped                 ; 2010          # 1.1  NON-BREAKING HYPHEN\n2012..2016    ; valid                  ;      ; NV8    # 1.1  FIGURE DASH..DOUBLE VERTICAL LINE\n2017          ; disallowed_STD3_mapped ; 0020 0333     # 1.1  DOUBLE LOW LINE\n2018..2023    ; valid                  ;      ; NV8    # 1.1  LEFT SINGLE QUOTATION MARK..TRIANGULAR BULLET\n2024..2026    ; disallowed                             # 1.1  ONE DOT LEADER..HORIZONTAL ELLIPSIS\n2027          ; valid                  ;      ; NV8    # 1.1  HYPHENATION POINT\n2028..202E    ; disallowed                             # 1.1  LINE SEPARATOR..RIGHT-TO-LEFT OVERRIDE\n202F          ; disallowed_STD3_mapped ; 0020          # 3.0  NARROW NO-BREAK SPACE\n2030..2032    ; valid                  ;      ; NV8    # 1.1  PER MILLE SIGN..PRIME\n2033          ; mapped                 ; 2032 2032     # 1.1  DOUBLE PRIME\n2034          ; mapped                 ; 2032 2032 2032 #1.1  TRIPLE PRIME\n2035          ; valid                  ;      ; NV8    # 1.1  REVERSED PRIME\n2036          ; mapped                 ; 2035 2035     # 1.1  REVERSED DOUBLE PRIME\n2037          ; mapped                 ; 2035 2035 2035 #1.1  REVERSED TRIPLE PRIME\n2038..203B    ; valid                  ;      ; NV8    # 1.1  CARET..REFERENCE MARK\n203C          ; disallowed_STD3_mapped ; 0021 0021     # 1.1  DOUBLE EXCLAMATION MARK\n203D          ; valid                  ;      ; NV8    # 1.1  INTERROBANG\n203E          ; disallowed_STD3_mapped ; 0020 0305     # 1.1  OVERLINE\n203F..2046    ; valid                  ;      ; NV8    # 1.1  UNDERTIE..RIGHT SQUARE BRACKET WITH QUILL\n2047          ; disallowed_STD3_mapped ; 003F 003F     # 3.2  DOUBLE QUESTION MARK\n2048          ; disallowed_STD3_mapped ; 003F 0021     # 3.0  QUESTION EXCLAMATION MARK\n2049          ; disallowed_STD3_mapped ; 0021 003F     # 3.0  EXCLAMATION QUESTION MARK\n204A..204D    ; valid                  ;      ; NV8    # 3.0  TIRONIAN SIGN ET..BLACK RIGHTWARDS BULLET\n204E..2052    ; valid                  ;      ; NV8    # 3.2  LOW ASTERISK..COMMERCIAL MINUS SIGN\n2053..2054    ; valid                  ;      ; NV8    # 4.0  SWUNG DASH..INVERTED UNDERTIE\n2055..2056    ; valid                  ;      ; NV8    # 4.1  FLOWER PUNCTUATION MARK..THREE DOT PUNCTUATION\n2057          ; mapped                 ; 2032 2032 2032 2032 #3.2 QUADRUPLE PRIME\n2058..205E    ; valid                  ;      ; NV8    # 4.1  FOUR DOT PUNCTUATION..VERTICAL FOUR DOTS\n205F          ; disallowed_STD3_mapped ; 0020          # 3.2  MEDIUM MATHEMATICAL SPACE\n2060          ; ignored                                # 3.2  WORD JOINER\n2061..2063    ; disallowed                             # 3.2  FUNCTION APPLICATION..INVISIBLE SEPARATOR\n2064          ; ignored                                # 5.1  INVISIBLE PLUS\n2065          ; disallowed                             # NA   <reserved-2065>\n2066..2069    ; disallowed                             # 6.3  LEFT-TO-RIGHT ISOLATE..POP DIRECTIONAL ISOLATE\n206A..206F    ; disallowed                             # 1.1  INHIBIT SYMMETRIC SWAPPING..NOMINAL DIGIT SHAPES\n2070          ; mapped                 ; 0030          # 1.1  SUPERSCRIPT ZERO\n2071          ; mapped                 ; 0069          # 3.2  SUPERSCRIPT LATIN SMALL LETTER I\n2072..2073    ; disallowed                             # NA   <reserved-2072>..<reserved-2073>\n2074          ; mapped                 ; 0034          # 1.1  SUPERSCRIPT FOUR\n2075          ; mapped                 ; 0035          # 1.1  SUPERSCRIPT FIVE\n2076          ; mapped                 ; 0036          # 1.1  SUPERSCRIPT SIX\n2077          ; mapped                 ; 0037          # 1.1  SUPERSCRIPT SEVEN\n2078          ; mapped                 ; 0038          # 1.1  SUPERSCRIPT EIGHT\n2079          ; mapped                 ; 0039          # 1.1  SUPERSCRIPT NINE\n207A          ; disallowed_STD3_mapped ; 002B          # 1.1  SUPERSCRIPT PLUS SIGN\n207B          ; mapped                 ; 2212          # 1.1  SUPERSCRIPT MINUS\n207C          ; disallowed_STD3_mapped ; 003D          # 1.1  SUPERSCRIPT EQUALS SIGN\n207D          ; disallowed_STD3_mapped ; 0028          # 1.1  SUPERSCRIPT LEFT PARENTHESIS\n207E          ; disallowed_STD3_mapped ; 0029          # 1.1  SUPERSCRIPT RIGHT PARENTHESIS\n207F          ; mapped                 ; 006E          # 1.1  SUPERSCRIPT LATIN SMALL LETTER N\n2080          ; mapped                 ; 0030          # 1.1  SUBSCRIPT ZERO\n2081          ; mapped                 ; 0031          # 1.1  SUBSCRIPT ONE\n2082          ; mapped                 ; 0032          # 1.1  SUBSCRIPT TWO\n2083          ; mapped                 ; 0033          # 1.1  SUBSCRIPT THREE\n2084          ; mapped                 ; 0034          # 1.1  SUBSCRIPT FOUR\n2085          ; mapped                 ; 0035          # 1.1  SUBSCRIPT FIVE\n2086          ; mapped                 ; 0036          # 1.1  SUBSCRIPT SIX\n2087          ; mapped                 ; 0037          # 1.1  SUBSCRIPT SEVEN\n2088          ; mapped                 ; 0038          # 1.1  SUBSCRIPT EIGHT\n2089          ; mapped                 ; 0039          # 1.1  SUBSCRIPT NINE\n208A          ; disallowed_STD3_mapped ; 002B          # 1.1  SUBSCRIPT PLUS SIGN\n208B          ; mapped                 ; 2212          # 1.1  SUBSCRIPT MINUS\n208C          ; disallowed_STD3_mapped ; 003D          # 1.1  SUBSCRIPT EQUALS SIGN\n208D          ; disallowed_STD3_mapped ; 0028          # 1.1  SUBSCRIPT LEFT PARENTHESIS\n208E          ; disallowed_STD3_mapped ; 0029          # 1.1  SUBSCRIPT RIGHT PARENTHESIS\n208F          ; disallowed                             # NA   <reserved-208F>\n2090          ; mapped                 ; 0061          # 4.1  LATIN SUBSCRIPT SMALL LETTER A\n2091          ; mapped                 ; 0065          # 4.1  LATIN SUBSCRIPT SMALL LETTER E\n2092          ; mapped                 ; 006F          # 4.1  LATIN SUBSCRIPT SMALL LETTER O\n2093          ; mapped                 ; 0078          # 4.1  LATIN SUBSCRIPT SMALL LETTER X\n2094          ; mapped                 ; 0259          # 4.1  LATIN SUBSCRIPT SMALL LETTER SCHWA\n2095          ; mapped                 ; 0068          # 6.0  LATIN SUBSCRIPT SMALL LETTER H\n2096          ; mapped                 ; 006B          # 6.0  LATIN SUBSCRIPT SMALL LETTER K\n2097          ; mapped                 ; 006C          # 6.0  LATIN SUBSCRIPT SMALL LETTER L\n2098          ; mapped                 ; 006D          # 6.0  LATIN SUBSCRIPT SMALL LETTER M\n2099          ; mapped                 ; 006E          # 6.0  LATIN SUBSCRIPT SMALL LETTER N\n209A          ; mapped                 ; 0070          # 6.0  LATIN SUBSCRIPT SMALL LETTER P\n209B          ; mapped                 ; 0073          # 6.0  LATIN SUBSCRIPT SMALL LETTER S\n209C          ; mapped                 ; 0074          # 6.0  LATIN SUBSCRIPT SMALL LETTER T\n209D..209F    ; disallowed                             # NA   <reserved-209D>..<reserved-209F>\n20A0..20A7    ; valid                  ;      ; NV8    # 1.1  EURO-CURRENCY SIGN..PESETA SIGN\n20A8          ; mapped                 ; 0072 0073     # 1.1  RUPEE SIGN\n20A9..20AA    ; valid                  ;      ; NV8    # 1.1  WON SIGN..NEW SHEQEL SIGN\n20AB          ; valid                  ;      ; NV8    # 2.0  DONG SIGN\n20AC          ; valid                  ;      ; NV8    # 2.1  EURO SIGN\n20AD..20AF    ; valid                  ;      ; NV8    # 3.0  KIP SIGN..DRACHMA SIGN\n20B0..20B1    ; valid                  ;      ; NV8    # 3.2  GERMAN PENNY SIGN..PESO SIGN\n20B2..20B5    ; valid                  ;      ; NV8    # 4.1  GUARANI SIGN..CEDI SIGN\n20B6..20B8    ; valid                  ;      ; NV8    # 5.2  LIVRE TOURNOIS SIGN..TENGE SIGN\n20B9          ; valid                  ;      ; NV8    # 6.0  INDIAN RUPEE SIGN\n20BA          ; valid                  ;      ; NV8    # 6.2  TURKISH LIRA SIGN\n20BB..20BD    ; valid                  ;      ; NV8    # 7.0  NORDIC MARK SIGN..RUBLE SIGN\n20BE          ; valid                  ;      ; NV8    # 8.0  LARI SIGN\n20BF          ; valid                  ;      ; NV8    # 10.0 BITCOIN SIGN\n20C0          ; valid                  ;      ; NV8    # 14.0 SOM SIGN\n20C1..20CF    ; disallowed                             # NA   <reserved-20C1>..<reserved-20CF>\n20D0..20E1    ; valid                  ;      ; NV8    # 1.1  COMBINING LEFT HARPOON ABOVE..COMBINING LEFT RIGHT ARROW ABOVE\n20E2..20E3    ; valid                  ;      ; NV8    # 3.0  COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING KEYCAP\n20E4..20EA    ; valid                  ;      ; NV8    # 3.2  COMBINING ENCLOSING UPWARD POINTING TRIANGLE..COMBINING LEFTWARDS ARROW OVERLAY\n20EB          ; valid                  ;      ; NV8    # 4.1  COMBINING LONG DOUBLE SOLIDUS OVERLAY\n20EC..20EF    ; valid                  ;      ; NV8    # 5.0  COMBINING RIGHTWARDS HARPOON WITH BARB DOWNWARDS..COMBINING RIGHT ARROW BELOW\n20F0          ; valid                  ;      ; NV8    # 5.1  COMBINING ASTERISK ABOVE\n20F1..20FF    ; disallowed                             # NA   <reserved-20F1>..<reserved-20FF>\n2100          ; disallowed_STD3_mapped ; 0061 002F 0063 #1.1  ACCOUNT OF\n2101          ; disallowed_STD3_mapped ; 0061 002F 0073 #1.1  ADDRESSED TO THE SUBJECT\n2102          ; mapped                 ; 0063          # 1.1  DOUBLE-STRUCK CAPITAL C\n2103          ; mapped                 ; 00B0 0063     # 1.1  DEGREE CELSIUS\n2104          ; valid                  ;      ; NV8    # 1.1  CENTRE LINE SYMBOL\n2105          ; disallowed_STD3_mapped ; 0063 002F 006F #1.1  CARE OF\n2106          ; disallowed_STD3_mapped ; 0063 002F 0075 #1.1  CADA UNA\n2107          ; mapped                 ; 025B          # 1.1  EULER CONSTANT\n2108          ; valid                  ;      ; NV8    # 1.1  SCRUPLE\n2109          ; mapped                 ; 00B0 0066     # 1.1  DEGREE FAHRENHEIT\n210A          ; mapped                 ; 0067          # 1.1  SCRIPT SMALL G\n210B..210E    ; mapped                 ; 0068          # 1.1  SCRIPT CAPITAL H..PLANCK CONSTANT\n210F          ; mapped                 ; 0127          # 1.1  PLANCK CONSTANT OVER TWO PI\n2110..2111    ; mapped                 ; 0069          # 1.1  SCRIPT CAPITAL I..BLACK-LETTER CAPITAL I\n2112..2113    ; mapped                 ; 006C          # 1.1  SCRIPT CAPITAL L..SCRIPT SMALL L\n2114          ; valid                  ;      ; NV8    # 1.1  L B BAR SYMBOL\n2115          ; mapped                 ; 006E          # 1.1  DOUBLE-STRUCK CAPITAL N\n2116          ; mapped                 ; 006E 006F     # 1.1  NUMERO SIGN\n2117..2118    ; valid                  ;      ; NV8    # 1.1  SOUND RECORDING COPYRIGHT..SCRIPT CAPITAL P\n2119          ; mapped                 ; 0070          # 1.1  DOUBLE-STRUCK CAPITAL P\n211A          ; mapped                 ; 0071          # 1.1  DOUBLE-STRUCK CAPITAL Q\n211B..211D    ; mapped                 ; 0072          # 1.1  SCRIPT CAPITAL R..DOUBLE-STRUCK CAPITAL R\n211E..211F    ; valid                  ;      ; NV8    # 1.1  PRESCRIPTION TAKE..RESPONSE\n2120          ; mapped                 ; 0073 006D     # 1.1  SERVICE MARK\n2121          ; mapped                 ; 0074 0065 006C #1.1  TELEPHONE SIGN\n2122          ; mapped                 ; 0074 006D     # 1.1  TRADE MARK SIGN\n2123          ; valid                  ;      ; NV8    # 1.1  VERSICLE\n2124          ; mapped                 ; 007A          # 1.1  DOUBLE-STRUCK CAPITAL Z\n2125          ; valid                  ;      ; NV8    # 1.1  OUNCE SIGN\n2126          ; mapped                 ; 03C9          # 1.1  OHM SIGN\n2127          ; valid                  ;      ; NV8    # 1.1  INVERTED OHM SIGN\n2128          ; mapped                 ; 007A          # 1.1  BLACK-LETTER CAPITAL Z\n2129          ; valid                  ;      ; NV8    # 1.1  TURNED GREEK SMALL LETTER IOTA\n212A          ; mapped                 ; 006B          # 1.1  KELVIN SIGN\n212B          ; mapped                 ; 00E5          # 1.1  ANGSTROM SIGN\n212C          ; mapped                 ; 0062          # 1.1  SCRIPT CAPITAL B\n212D          ; mapped                 ; 0063          # 1.1  BLACK-LETTER CAPITAL C\n212E          ; valid                  ;      ; NV8    # 1.1  ESTIMATED SYMBOL\n212F..2130    ; mapped                 ; 0065          # 1.1  SCRIPT SMALL E..SCRIPT CAPITAL E\n2131          ; mapped                 ; 0066          # 1.1  SCRIPT CAPITAL F\n2132          ; disallowed                             # 1.1  TURNED CAPITAL F\n2133          ; mapped                 ; 006D          # 1.1  SCRIPT CAPITAL M\n2134          ; mapped                 ; 006F          # 1.1  SCRIPT SMALL O\n2135          ; mapped                 ; 05D0          # 1.1  ALEF SYMBOL\n2136          ; mapped                 ; 05D1          # 1.1  BET SYMBOL\n2137          ; mapped                 ; 05D2          # 1.1  GIMEL SYMBOL\n2138          ; mapped                 ; 05D3          # 1.1  DALET SYMBOL\n2139          ; mapped                 ; 0069          # 3.0  INFORMATION SOURCE\n213A          ; valid                  ;      ; NV8    # 3.0  ROTATED CAPITAL Q\n213B          ; mapped                 ; 0066 0061 0078 #4.0  FACSIMILE SIGN\n213C          ; mapped                 ; 03C0          # 4.1  DOUBLE-STRUCK SMALL PI\n213D..213E    ; mapped                 ; 03B3          # 3.2  DOUBLE-STRUCK SMALL GAMMA..DOUBLE-STRUCK CAPITAL GAMMA\n213F          ; mapped                 ; 03C0          # 3.2  DOUBLE-STRUCK CAPITAL PI\n2140          ; mapped                 ; 2211          # 3.2  DOUBLE-STRUCK N-ARY SUMMATION\n2141..2144    ; valid                  ;      ; NV8    # 3.2  TURNED SANS-SERIF CAPITAL G..TURNED SANS-SERIF CAPITAL Y\n2145..2146    ; mapped                 ; 0064          # 3.2  DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL D\n2147          ; mapped                 ; 0065          # 3.2  DOUBLE-STRUCK ITALIC SMALL E\n2148          ; mapped                 ; 0069          # 3.2  DOUBLE-STRUCK ITALIC SMALL I\n2149          ; mapped                 ; 006A          # 3.2  DOUBLE-STRUCK ITALIC SMALL J\n214A..214B    ; valid                  ;      ; NV8    # 3.2  PROPERTY LINE..TURNED AMPERSAND\n214C          ; valid                  ;      ; NV8    # 4.1  PER SIGN\n214D          ; valid                  ;      ; NV8    # 5.0  AKTIESELSKAB\n214E          ; valid                                  # 5.0  TURNED SMALL F\n214F          ; valid                  ;      ; NV8    # 5.1  SYMBOL FOR SAMARITAN SOURCE\n2150          ; mapped                 ; 0031 2044 0037 #5.2  VULGAR FRACTION ONE SEVENTH\n2151          ; mapped                 ; 0031 2044 0039 #5.2  VULGAR FRACTION ONE NINTH\n2152          ; mapped                 ; 0031 2044 0031 0030 #5.2 VULGAR FRACTION ONE TENTH\n2153          ; mapped                 ; 0031 2044 0033 #1.1  VULGAR FRACTION ONE THIRD\n2154          ; mapped                 ; 0032 2044 0033 #1.1  VULGAR FRACTION TWO THIRDS\n2155          ; mapped                 ; 0031 2044 0035 #1.1  VULGAR FRACTION ONE FIFTH\n2156          ; mapped                 ; 0032 2044 0035 #1.1  VULGAR FRACTION TWO FIFTHS\n2157          ; mapped                 ; 0033 2044 0035 #1.1  VULGAR FRACTION THREE FIFTHS\n2158          ; mapped                 ; 0034 2044 0035 #1.1  VULGAR FRACTION FOUR FIFTHS\n2159          ; mapped                 ; 0031 2044 0036 #1.1  VULGAR FRACTION ONE SIXTH\n215A          ; mapped                 ; 0035 2044 0036 #1.1  VULGAR FRACTION FIVE SIXTHS\n215B          ; mapped                 ; 0031 2044 0038 #1.1  VULGAR FRACTION ONE EIGHTH\n215C          ; mapped                 ; 0033 2044 0038 #1.1  VULGAR FRACTION THREE EIGHTHS\n215D          ; mapped                 ; 0035 2044 0038 #1.1  VULGAR FRACTION FIVE EIGHTHS\n215E          ; mapped                 ; 0037 2044 0038 #1.1  VULGAR FRACTION SEVEN EIGHTHS\n215F          ; mapped                 ; 0031 2044     # 1.1  FRACTION NUMERATOR ONE\n2160          ; mapped                 ; 0069          # 1.1  ROMAN NUMERAL ONE\n2161          ; mapped                 ; 0069 0069     # 1.1  ROMAN NUMERAL TWO\n2162          ; mapped                 ; 0069 0069 0069 #1.1  ROMAN NUMERAL THREE\n2163          ; mapped                 ; 0069 0076     # 1.1  ROMAN NUMERAL FOUR\n2164          ; mapped                 ; 0076          # 1.1  ROMAN NUMERAL FIVE\n2165          ; mapped                 ; 0076 0069     # 1.1  ROMAN NUMERAL SIX\n2166          ; mapped                 ; 0076 0069 0069 #1.1  ROMAN NUMERAL SEVEN\n2167          ; mapped                 ; 0076 0069 0069 0069 #1.1 ROMAN NUMERAL EIGHT\n2168          ; mapped                 ; 0069 0078     # 1.1  ROMAN NUMERAL NINE\n2169          ; mapped                 ; 0078          # 1.1  ROMAN NUMERAL TEN\n216A          ; mapped                 ; 0078 0069     # 1.1  ROMAN NUMERAL ELEVEN\n216B          ; mapped                 ; 0078 0069 0069 #1.1  ROMAN NUMERAL TWELVE\n216C          ; mapped                 ; 006C          # 1.1  ROMAN NUMERAL FIFTY\n216D          ; mapped                 ; 0063          # 1.1  ROMAN NUMERAL ONE HUNDRED\n216E          ; mapped                 ; 0064          # 1.1  ROMAN NUMERAL FIVE HUNDRED\n216F          ; mapped                 ; 006D          # 1.1  ROMAN NUMERAL ONE THOUSAND\n2170          ; mapped                 ; 0069          # 1.1  SMALL ROMAN NUMERAL ONE\n2171          ; mapped                 ; 0069 0069     # 1.1  SMALL ROMAN NUMERAL TWO\n2172          ; mapped                 ; 0069 0069 0069 #1.1  SMALL ROMAN NUMERAL THREE\n2173          ; mapped                 ; 0069 0076     # 1.1  SMALL ROMAN NUMERAL FOUR\n2174          ; mapped                 ; 0076          # 1.1  SMALL ROMAN NUMERAL FIVE\n2175          ; mapped                 ; 0076 0069     # 1.1  SMALL ROMAN NUMERAL SIX\n2176          ; mapped                 ; 0076 0069 0069 #1.1  SMALL ROMAN NUMERAL SEVEN\n2177          ; mapped                 ; 0076 0069 0069 0069 #1.1 SMALL ROMAN NUMERAL EIGHT\n2178          ; mapped                 ; 0069 0078     # 1.1  SMALL ROMAN NUMERAL NINE\n2179          ; mapped                 ; 0078          # 1.1  SMALL ROMAN NUMERAL TEN\n217A          ; mapped                 ; 0078 0069     # 1.1  SMALL ROMAN NUMERAL ELEVEN\n217B          ; mapped                 ; 0078 0069 0069 #1.1  SMALL ROMAN NUMERAL TWELVE\n217C          ; mapped                 ; 006C          # 1.1  SMALL ROMAN NUMERAL FIFTY\n217D          ; mapped                 ; 0063          # 1.1  SMALL ROMAN NUMERAL ONE HUNDRED\n217E          ; mapped                 ; 0064          # 1.1  SMALL ROMAN NUMERAL FIVE HUNDRED\n217F          ; mapped                 ; 006D          # 1.1  SMALL ROMAN NUMERAL ONE THOUSAND\n2180..2182    ; valid                  ;      ; NV8    # 1.1  ROMAN NUMERAL ONE THOUSAND C D..ROMAN NUMERAL TEN THOUSAND\n2183          ; disallowed                             # 3.0  ROMAN NUMERAL REVERSED ONE HUNDRED\n2184          ; valid                                  # 5.0  LATIN SMALL LETTER REVERSED C\n2185..2188    ; valid                  ;      ; NV8    # 5.1  ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND\n2189          ; mapped                 ; 0030 2044 0033 #5.2  VULGAR FRACTION ZERO THIRDS\n218A..218B    ; valid                  ;      ; NV8    # 8.0  TURNED DIGIT TWO..TURNED DIGIT THREE\n218C..218F    ; disallowed                             # NA   <reserved-218C>..<reserved-218F>\n2190..21EA    ; valid                  ;      ; NV8    # 1.1  LEFTWARDS ARROW..UPWARDS WHITE ARROW FROM BAR\n21EB..21F3    ; valid                  ;      ; NV8    # 3.0  UPWARDS WHITE ARROW ON PEDESTAL..UP DOWN WHITE ARROW\n21F4..21FF    ; valid                  ;      ; NV8    # 3.2  RIGHT ARROW WITH SMALL CIRCLE..LEFT RIGHT OPEN-HEADED ARROW\n2200..222B    ; valid                  ;      ; NV8    # 1.1  FOR ALL..INTEGRAL\n222C          ; mapped                 ; 222B 222B     # 1.1  DOUBLE INTEGRAL\n222D          ; mapped                 ; 222B 222B 222B #1.1  TRIPLE INTEGRAL\n222E          ; valid                  ;      ; NV8    # 1.1  CONTOUR INTEGRAL\n222F          ; mapped                 ; 222E 222E     # 1.1  SURFACE INTEGRAL\n2230          ; mapped                 ; 222E 222E 222E #1.1  VOLUME INTEGRAL\n2231..22F1    ; valid                  ;      ; NV8    # 1.1  CLOCKWISE INTEGRAL..DOWN RIGHT DIAGONAL ELLIPSIS\n22F2..22FF    ; valid                  ;      ; NV8    # 3.2  ELEMENT OF WITH LONG HORIZONTAL STROKE..Z NOTATION BAG MEMBERSHIP\n2300          ; valid                  ;      ; NV8    # 1.1  DIAMETER SIGN\n2301          ; valid                  ;      ; NV8    # 3.0  ELECTRIC ARROW\n2302..2328    ; valid                  ;      ; NV8    # 1.1  HOUSE..KEYBOARD\n2329          ; mapped                 ; 3008          # 1.1  LEFT-POINTING ANGLE BRACKET\n232A          ; mapped                 ; 3009          # 1.1  RIGHT-POINTING ANGLE BRACKET\n232B..237A    ; valid                  ;      ; NV8    # 1.1  ERASE TO THE LEFT..APL FUNCTIONAL SYMBOL ALPHA\n237B          ; valid                  ;      ; NV8    # 3.0  NOT CHECK MARK\n237C          ; valid                  ;      ; NV8    # 3.2  RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW\n237D..239A    ; valid                  ;      ; NV8    # 3.0  SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL\n239B..23CE    ; valid                  ;      ; NV8    # 3.2  LEFT PARENTHESIS UPPER HOOK..RETURN SYMBOL\n23CF..23D0    ; valid                  ;      ; NV8    # 4.0  EJECT SYMBOL..VERTICAL LINE EXTENSION\n23D1..23DB    ; valid                  ;      ; NV8    # 4.1  METRICAL BREVE..FUSE\n23DC..23E7    ; valid                  ;      ; NV8    # 5.0  TOP PARENTHESIS..ELECTRICAL INTERSECTION\n23E8          ; valid                  ;      ; NV8    # 5.2  DECIMAL EXPONENT SYMBOL\n23E9..23F3    ; valid                  ;      ; NV8    # 6.0  BLACK RIGHT-POINTING DOUBLE TRIANGLE..HOURGLASS WITH FLOWING SAND\n23F4..23FA    ; valid                  ;      ; NV8    # 7.0  BLACK MEDIUM LEFT-POINTING TRIANGLE..BLACK CIRCLE FOR RECORD\n23FB..23FE    ; valid                  ;      ; NV8    # 9.0  POWER SYMBOL..POWER SLEEP SYMBOL\n23FF          ; valid                  ;      ; NV8    # 10.0 OBSERVER EYE SYMBOL\n2400..2424    ; valid                  ;      ; NV8    # 1.1  SYMBOL FOR NULL..SYMBOL FOR NEWLINE\n2425..2426    ; valid                  ;      ; NV8    # 3.0  SYMBOL FOR DELETE FORM TWO..SYMBOL FOR SUBSTITUTE FORM TWO\n2427..243F    ; disallowed                             # NA   <reserved-2427>..<reserved-243F>\n2440..244A    ; valid                  ;      ; NV8    # 1.1  OCR HOOK..OCR DOUBLE BACKSLASH\n244B..245F    ; disallowed                             # NA   <reserved-244B>..<reserved-245F>\n2460          ; mapped                 ; 0031          # 1.1  CIRCLED DIGIT ONE\n2461          ; mapped                 ; 0032          # 1.1  CIRCLED DIGIT TWO\n2462          ; mapped                 ; 0033          # 1.1  CIRCLED DIGIT THREE\n2463          ; mapped                 ; 0034          # 1.1  CIRCLED DIGIT FOUR\n2464          ; mapped                 ; 0035          # 1.1  CIRCLED DIGIT FIVE\n2465          ; mapped                 ; 0036          # 1.1  CIRCLED DIGIT SIX\n2466          ; mapped                 ; 0037          # 1.1  CIRCLED DIGIT SEVEN\n2467          ; mapped                 ; 0038          # 1.1  CIRCLED DIGIT EIGHT\n2468          ; mapped                 ; 0039          # 1.1  CIRCLED DIGIT NINE\n2469          ; mapped                 ; 0031 0030     # 1.1  CIRCLED NUMBER TEN\n246A          ; mapped                 ; 0031 0031     # 1.1  CIRCLED NUMBER ELEVEN\n246B          ; mapped                 ; 0031 0032     # 1.1  CIRCLED NUMBER TWELVE\n246C          ; mapped                 ; 0031 0033     # 1.1  CIRCLED NUMBER THIRTEEN\n246D          ; mapped                 ; 0031 0034     # 1.1  CIRCLED NUMBER FOURTEEN\n246E          ; mapped                 ; 0031 0035     # 1.1  CIRCLED NUMBER FIFTEEN\n246F          ; mapped                 ; 0031 0036     # 1.1  CIRCLED NUMBER SIXTEEN\n2470          ; mapped                 ; 0031 0037     # 1.1  CIRCLED NUMBER SEVENTEEN\n2471          ; mapped                 ; 0031 0038     # 1.1  CIRCLED NUMBER EIGHTEEN\n2472          ; mapped                 ; 0031 0039     # 1.1  CIRCLED NUMBER NINETEEN\n2473          ; mapped                 ; 0032 0030     # 1.1  CIRCLED NUMBER TWENTY\n2474          ; disallowed_STD3_mapped ; 0028 0031 0029 #1.1  PARENTHESIZED DIGIT ONE\n2475          ; disallowed_STD3_mapped ; 0028 0032 0029 #1.1  PARENTHESIZED DIGIT TWO\n2476          ; disallowed_STD3_mapped ; 0028 0033 0029 #1.1  PARENTHESIZED DIGIT THREE\n2477          ; disallowed_STD3_mapped ; 0028 0034 0029 #1.1  PARENTHESIZED DIGIT FOUR\n2478          ; disallowed_STD3_mapped ; 0028 0035 0029 #1.1  PARENTHESIZED DIGIT FIVE\n2479          ; disallowed_STD3_mapped ; 0028 0036 0029 #1.1  PARENTHESIZED DIGIT SIX\n247A          ; disallowed_STD3_mapped ; 0028 0037 0029 #1.1  PARENTHESIZED DIGIT SEVEN\n247B          ; disallowed_STD3_mapped ; 0028 0038 0029 #1.1  PARENTHESIZED DIGIT EIGHT\n247C          ; disallowed_STD3_mapped ; 0028 0039 0029 #1.1  PARENTHESIZED DIGIT NINE\n247D          ; disallowed_STD3_mapped ; 0028 0031 0030 0029 #1.1 PARENTHESIZED NUMBER TEN\n247E          ; disallowed_STD3_mapped ; 0028 0031 0031 0029 #1.1 PARENTHESIZED NUMBER ELEVEN\n247F          ; disallowed_STD3_mapped ; 0028 0031 0032 0029 #1.1 PARENTHESIZED NUMBER TWELVE\n2480          ; disallowed_STD3_mapped ; 0028 0031 0033 0029 #1.1 PARENTHESIZED NUMBER THIRTEEN\n2481          ; disallowed_STD3_mapped ; 0028 0031 0034 0029 #1.1 PARENTHESIZED NUMBER FOURTEEN\n2482          ; disallowed_STD3_mapped ; 0028 0031 0035 0029 #1.1 PARENTHESIZED NUMBER FIFTEEN\n2483          ; disallowed_STD3_mapped ; 0028 0031 0036 0029 #1.1 PARENTHESIZED NUMBER SIXTEEN\n2484          ; disallowed_STD3_mapped ; 0028 0031 0037 0029 #1.1 PARENTHESIZED NUMBER SEVENTEEN\n2485          ; disallowed_STD3_mapped ; 0028 0031 0038 0029 #1.1 PARENTHESIZED NUMBER EIGHTEEN\n2486          ; disallowed_STD3_mapped ; 0028 0031 0039 0029 #1.1 PARENTHESIZED NUMBER NINETEEN\n2487          ; disallowed_STD3_mapped ; 0028 0032 0030 0029 #1.1 PARENTHESIZED NUMBER TWENTY\n2488..249B    ; disallowed                             # 1.1  DIGIT ONE FULL STOP..NUMBER TWENTY FULL STOP\n249C          ; disallowed_STD3_mapped ; 0028 0061 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER A\n249D          ; disallowed_STD3_mapped ; 0028 0062 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER B\n249E          ; disallowed_STD3_mapped ; 0028 0063 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER C\n249F          ; disallowed_STD3_mapped ; 0028 0064 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER D\n24A0          ; disallowed_STD3_mapped ; 0028 0065 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER E\n24A1          ; disallowed_STD3_mapped ; 0028 0066 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER F\n24A2          ; disallowed_STD3_mapped ; 0028 0067 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER G\n24A3          ; disallowed_STD3_mapped ; 0028 0068 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER H\n24A4          ; disallowed_STD3_mapped ; 0028 0069 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER I\n24A5          ; disallowed_STD3_mapped ; 0028 006A 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER J\n24A6          ; disallowed_STD3_mapped ; 0028 006B 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER K\n24A7          ; disallowed_STD3_mapped ; 0028 006C 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER L\n24A8          ; disallowed_STD3_mapped ; 0028 006D 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER M\n24A9          ; disallowed_STD3_mapped ; 0028 006E 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER N\n24AA          ; disallowed_STD3_mapped ; 0028 006F 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER O\n24AB          ; disallowed_STD3_mapped ; 0028 0070 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER P\n24AC          ; disallowed_STD3_mapped ; 0028 0071 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER Q\n24AD          ; disallowed_STD3_mapped ; 0028 0072 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER R\n24AE          ; disallowed_STD3_mapped ; 0028 0073 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER S\n24AF          ; disallowed_STD3_mapped ; 0028 0074 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER T\n24B0          ; disallowed_STD3_mapped ; 0028 0075 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER U\n24B1          ; disallowed_STD3_mapped ; 0028 0076 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER V\n24B2          ; disallowed_STD3_mapped ; 0028 0077 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER W\n24B3          ; disallowed_STD3_mapped ; 0028 0078 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER X\n24B4          ; disallowed_STD3_mapped ; 0028 0079 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER Y\n24B5          ; disallowed_STD3_mapped ; 0028 007A 0029 #1.1  PARENTHESIZED LATIN SMALL LETTER Z\n24B6          ; mapped                 ; 0061          # 1.1  CIRCLED LATIN CAPITAL LETTER A\n24B7          ; mapped                 ; 0062          # 1.1  CIRCLED LATIN CAPITAL LETTER B\n24B8          ; mapped                 ; 0063          # 1.1  CIRCLED LATIN CAPITAL LETTER C\n24B9          ; mapped                 ; 0064          # 1.1  CIRCLED LATIN CAPITAL LETTER D\n24BA          ; mapped                 ; 0065          # 1.1  CIRCLED LATIN CAPITAL LETTER E\n24BB          ; mapped                 ; 0066          # 1.1  CIRCLED LATIN CAPITAL LETTER F\n24BC          ; mapped                 ; 0067          # 1.1  CIRCLED LATIN CAPITAL LETTER G\n24BD          ; mapped                 ; 0068          # 1.1  CIRCLED LATIN CAPITAL LETTER H\n24BE          ; mapped                 ; 0069          # 1.1  CIRCLED LATIN CAPITAL LETTER I\n24BF          ; mapped                 ; 006A          # 1.1  CIRCLED LATIN CAPITAL LETTER J\n24C0          ; mapped                 ; 006B          # 1.1  CIRCLED LATIN CAPITAL LETTER K\n24C1          ; mapped                 ; 006C          # 1.1  CIRCLED LATIN CAPITAL LETTER L\n24C2          ; mapped                 ; 006D          # 1.1  CIRCLED LATIN CAPITAL LETTER M\n24C3          ; mapped                 ; 006E          # 1.1  CIRCLED LATIN CAPITAL LETTER N\n24C4          ; mapped                 ; 006F          # 1.1  CIRCLED LATIN CAPITAL LETTER O\n24C5          ; mapped                 ; 0070          # 1.1  CIRCLED LATIN CAPITAL LETTER P\n24C6          ; mapped                 ; 0071          # 1.1  CIRCLED LATIN CAPITAL LETTER Q\n24C7          ; mapped                 ; 0072          # 1.1  CIRCLED LATIN CAPITAL LETTER R\n24C8          ; mapped                 ; 0073          # 1.1  CIRCLED LATIN CAPITAL LETTER S\n24C9          ; mapped                 ; 0074          # 1.1  CIRCLED LATIN CAPITAL LETTER T\n24CA          ; mapped                 ; 0075          # 1.1  CIRCLED LATIN CAPITAL LETTER U\n24CB          ; mapped                 ; 0076          # 1.1  CIRCLED LATIN CAPITAL LETTER V\n24CC          ; mapped                 ; 0077          # 1.1  CIRCLED LATIN CAPITAL LETTER W\n24CD          ; mapped                 ; 0078          # 1.1  CIRCLED LATIN CAPITAL LETTER X\n24CE          ; mapped                 ; 0079          # 1.1  CIRCLED LATIN CAPITAL LETTER Y\n24CF          ; mapped                 ; 007A          # 1.1  CIRCLED LATIN CAPITAL LETTER Z\n24D0          ; mapped                 ; 0061          # 1.1  CIRCLED LATIN SMALL LETTER A\n24D1          ; mapped                 ; 0062          # 1.1  CIRCLED LATIN SMALL LETTER B\n24D2          ; mapped                 ; 0063          # 1.1  CIRCLED LATIN SMALL LETTER C\n24D3          ; mapped                 ; 0064          # 1.1  CIRCLED LATIN SMALL LETTER D\n24D4          ; mapped                 ; 0065          # 1.1  CIRCLED LATIN SMALL LETTER E\n24D5          ; mapped                 ; 0066          # 1.1  CIRCLED LATIN SMALL LETTER F\n24D6          ; mapped                 ; 0067          # 1.1  CIRCLED LATIN SMALL LETTER G\n24D7          ; mapped                 ; 0068          # 1.1  CIRCLED LATIN SMALL LETTER H\n24D8          ; mapped                 ; 0069          # 1.1  CIRCLED LATIN SMALL LETTER I\n24D9          ; mapped                 ; 006A          # 1.1  CIRCLED LATIN SMALL LETTER J\n24DA          ; mapped                 ; 006B          # 1.1  CIRCLED LATIN SMALL LETTER K\n24DB          ; mapped                 ; 006C          # 1.1  CIRCLED LATIN SMALL LETTER L\n24DC          ; mapped                 ; 006D          # 1.1  CIRCLED LATIN SMALL LETTER M\n24DD          ; mapped                 ; 006E          # 1.1  CIRCLED LATIN SMALL LETTER N\n24DE          ; mapped                 ; 006F          # 1.1  CIRCLED LATIN SMALL LETTER O\n24DF          ; mapped                 ; 0070          # 1.1  CIRCLED LATIN SMALL LETTER P\n24E0          ; mapped                 ; 0071          # 1.1  CIRCLED LATIN SMALL LETTER Q\n24E1          ; mapped                 ; 0072          # 1.1  CIRCLED LATIN SMALL LETTER R\n24E2          ; mapped                 ; 0073          # 1.1  CIRCLED LATIN SMALL LETTER S\n24E3          ; mapped                 ; 0074          # 1.1  CIRCLED LATIN SMALL LETTER T\n24E4          ; mapped                 ; 0075          # 1.1  CIRCLED LATIN SMALL LETTER U\n24E5          ; mapped                 ; 0076          # 1.1  CIRCLED LATIN SMALL LETTER V\n24E6          ; mapped                 ; 0077          # 1.1  CIRCLED LATIN SMALL LETTER W\n24E7          ; mapped                 ; 0078          # 1.1  CIRCLED LATIN SMALL LETTER X\n24E8          ; mapped                 ; 0079          # 1.1  CIRCLED LATIN SMALL LETTER Y\n24E9          ; mapped                 ; 007A          # 1.1  CIRCLED LATIN SMALL LETTER Z\n24EA          ; mapped                 ; 0030          # 1.1  CIRCLED DIGIT ZERO\n24EB..24FE    ; valid                  ;      ; NV8    # 3.2  NEGATIVE CIRCLED NUMBER ELEVEN..DOUBLE CIRCLED NUMBER TEN\n24FF          ; valid                  ;      ; NV8    # 4.0  NEGATIVE CIRCLED DIGIT ZERO\n2500..2595    ; valid                  ;      ; NV8    # 1.1  BOX DRAWINGS LIGHT HORIZONTAL..RIGHT ONE EIGHTH BLOCK\n2596..259F    ; valid                  ;      ; NV8    # 3.2  QUADRANT LOWER LEFT..QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT\n25A0..25EF    ; valid                  ;      ; NV8    # 1.1  BLACK SQUARE..LARGE CIRCLE\n25F0..25F7    ; valid                  ;      ; NV8    # 3.0  WHITE SQUARE WITH UPPER LEFT QUADRANT..WHITE CIRCLE WITH UPPER RIGHT QUADRANT\n25F8..25FF    ; valid                  ;      ; NV8    # 3.2  UPPER LEFT TRIANGLE..LOWER RIGHT TRIANGLE\n2600..2613    ; valid                  ;      ; NV8    # 1.1  BLACK SUN WITH RAYS..SALTIRE\n2614..2615    ; valid                  ;      ; NV8    # 4.0  UMBRELLA WITH RAIN DROPS..HOT BEVERAGE\n2616..2617    ; valid                  ;      ; NV8    # 3.2  WHITE SHOGI PIECE..BLACK SHOGI PIECE\n2618          ; valid                  ;      ; NV8    # 4.1  SHAMROCK\n2619          ; valid                  ;      ; NV8    # 3.0  REVERSED ROTATED FLORAL HEART BULLET\n261A..266F    ; valid                  ;      ; NV8    # 1.1  BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN\n2670..2671    ; valid                  ;      ; NV8    # 3.0  WEST SYRIAC CROSS..EAST SYRIAC CROSS\n2672..267D    ; valid                  ;      ; NV8    # 3.2  UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL\n267E..267F    ; valid                  ;      ; NV8    # 4.1  PERMANENT PAPER SIGN..WHEELCHAIR SYMBOL\n2680..2689    ; valid                  ;      ; NV8    # 3.2  DIE FACE-1..BLACK CIRCLE WITH TWO WHITE DOTS\n268A..2691    ; valid                  ;      ; NV8    # 4.0  MONOGRAM FOR YANG..BLACK FLAG\n2692..269C    ; valid                  ;      ; NV8    # 4.1  HAMMER AND PICK..FLEUR-DE-LIS\n269D          ; valid                  ;      ; NV8    # 5.1  OUTLINED WHITE STAR\n269E..269F    ; valid                  ;      ; NV8    # 5.2  THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT\n26A0..26A1    ; valid                  ;      ; NV8    # 4.0  WARNING SIGN..HIGH VOLTAGE SIGN\n26A2..26B1    ; valid                  ;      ; NV8    # 4.1  DOUBLED FEMALE SIGN..FUNERAL URN\n26B2          ; valid                  ;      ; NV8    # 5.0  NEUTER\n26B3..26BC    ; valid                  ;      ; NV8    # 5.1  CERES..SESQUIQUADRATE\n26BD..26BF    ; valid                  ;      ; NV8    # 5.2  SOCCER BALL..SQUARED KEY\n26C0..26C3    ; valid                  ;      ; NV8    # 5.1  WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING\n26C4..26CD    ; valid                  ;      ; NV8    # 5.2  SNOWMAN WITHOUT SNOW..DISABLED CAR\n26CE          ; valid                  ;      ; NV8    # 6.0  OPHIUCHUS\n26CF..26E1    ; valid                  ;      ; NV8    # 5.2  PICK..RESTRICTED LEFT ENTRY-2\n26E2          ; valid                  ;      ; NV8    # 6.0  ASTRONOMICAL SYMBOL FOR URANUS\n26E3          ; valid                  ;      ; NV8    # 5.2  HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE\n26E4..26E7    ; valid                  ;      ; NV8    # 6.0  PENTAGRAM..INVERTED PENTAGRAM\n26E8..26FF    ; valid                  ;      ; NV8    # 5.2  BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE\n2700          ; valid                  ;      ; NV8    # 7.0  BLACK SAFETY SCISSORS\n2701..2704    ; valid                  ;      ; NV8    # 1.1  UPPER BLADE SCISSORS..WHITE SCISSORS\n2705          ; valid                  ;      ; NV8    # 6.0  WHITE HEAVY CHECK MARK\n2706..2709    ; valid                  ;      ; NV8    # 1.1  TELEPHONE LOCATION SIGN..ENVELOPE\n270A..270B    ; valid                  ;      ; NV8    # 6.0  RAISED FIST..RAISED HAND\n270C..2727    ; valid                  ;      ; NV8    # 1.1  VICTORY HAND..WHITE FOUR POINTED STAR\n2728          ; valid                  ;      ; NV8    # 6.0  SPARKLES\n2729..274B    ; valid                  ;      ; NV8    # 1.1  STRESS OUTLINED WHITE STAR..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK\n274C          ; valid                  ;      ; NV8    # 6.0  CROSS MARK\n274D          ; valid                  ;      ; NV8    # 1.1  SHADOWED WHITE CIRCLE\n274E          ; valid                  ;      ; NV8    # 6.0  NEGATIVE SQUARED CROSS MARK\n274F..2752    ; valid                  ;      ; NV8    # 1.1  LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE\n2753..2755    ; valid                  ;      ; NV8    # 6.0  BLACK QUESTION MARK ORNAMENT..WHITE EXCLAMATION MARK ORNAMENT\n2756          ; valid                  ;      ; NV8    # 1.1  BLACK DIAMOND MINUS WHITE X\n2757          ; valid                  ;      ; NV8    # 5.2  HEAVY EXCLAMATION MARK SYMBOL\n2758..275E    ; valid                  ;      ; NV8    # 1.1  LIGHT VERTICAL BAR..HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT\n275F..2760    ; valid                  ;      ; NV8    # 6.0  HEAVY LOW SINGLE COMMA QUOTATION MARK ORNAMENT..HEAVY LOW DOUBLE COMMA QUOTATION MARK ORNAMENT\n2761..2767    ; valid                  ;      ; NV8    # 1.1  CURVED STEM PARAGRAPH SIGN ORNAMENT..ROTATED FLORAL HEART BULLET\n2768..2775    ; valid                  ;      ; NV8    # 3.2  MEDIUM LEFT PARENTHESIS ORNAMENT..MEDIUM RIGHT CURLY BRACKET ORNAMENT\n2776..2794    ; valid                  ;      ; NV8    # 1.1  DINGBAT NEGATIVE CIRCLED DIGIT ONE..HEAVY WIDE-HEADED RIGHTWARDS ARROW\n2795..2797    ; valid                  ;      ; NV8    # 6.0  HEAVY PLUS SIGN..HEAVY DIVISION SIGN\n2798..27AF    ; valid                  ;      ; NV8    # 1.1  HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW\n27B0          ; valid                  ;      ; NV8    # 6.0  CURLY LOOP\n27B1..27BE    ; valid                  ;      ; NV8    # 1.1  NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW\n27BF          ; valid                  ;      ; NV8    # 6.0  DOUBLE CURLY LOOP\n27C0..27C6    ; valid                  ;      ; NV8    # 4.1  THREE DIMENSIONAL ANGLE..RIGHT S-SHAPED BAG DELIMITER\n27C7..27CA    ; valid                  ;      ; NV8    # 5.0  OR WITH DOT INSIDE..VERTICAL BAR WITH HORIZONTAL STROKE\n27CB          ; valid                  ;      ; NV8    # 6.1  MATHEMATICAL RISING DIAGONAL\n27CC          ; valid                  ;      ; NV8    # 5.1  LONG DIVISION\n27CD          ; valid                  ;      ; NV8    # 6.1  MATHEMATICAL FALLING DIAGONAL\n27CE..27CF    ; valid                  ;      ; NV8    # 6.0  SQUARED LOGICAL AND..SQUARED LOGICAL OR\n27D0..27EB    ; valid                  ;      ; NV8    # 3.2  WHITE DIAMOND WITH CENTRED DOT..MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET\n27EC..27EF    ; valid                  ;      ; NV8    # 5.1  MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET..MATHEMATICAL RIGHT FLATTENED PARENTHESIS\n27F0..27FF    ; valid                  ;      ; NV8    # 3.2  UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW\n2800..28FF    ; valid                  ;      ; NV8    # 3.0  BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678\n2900..2A0B    ; valid                  ;      ; NV8    # 3.2  RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..SUMMATION WITH INTEGRAL\n2A0C          ; mapped                 ; 222B 222B 222B 222B #3.2 QUADRUPLE INTEGRAL OPERATOR\n2A0D..2A73    ; valid                  ;      ; NV8    # 3.2  FINITE PART INTEGRAL..EQUALS SIGN ABOVE TILDE OPERATOR\n2A74          ; disallowed_STD3_mapped ; 003A 003A 003D #3.2  DOUBLE COLON EQUAL\n2A75          ; disallowed_STD3_mapped ; 003D 003D     # 3.2  TWO CONSECUTIVE EQUALS SIGNS\n2A76          ; disallowed_STD3_mapped ; 003D 003D 003D #3.2  THREE CONSECUTIVE EQUALS SIGNS\n2A77..2ADB    ; valid                  ;      ; NV8    # 3.2  EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW..TRANSVERSAL INTERSECTION\n2ADC          ; mapped                 ; 2ADD 0338     # 3.2  FORKING\n2ADD..2AFF    ; valid                  ;      ; NV8    # 3.2  NONFORKING..N-ARY WHITE VERTICAL BAR\n2B00..2B0D    ; valid                  ;      ; NV8    # 4.0  NORTH EAST WHITE ARROW..UP DOWN BLACK ARROW\n2B0E..2B13    ; valid                  ;      ; NV8    # 4.1  RIGHTWARDS ARROW WITH TIP DOWNWARDS..SQUARE WITH BOTTOM HALF BLACK\n2B14..2B1A    ; valid                  ;      ; NV8    # 5.0  SQUARE WITH UPPER RIGHT DIAGONAL HALF BLACK..DOTTED SQUARE\n2B1B..2B1F    ; valid                  ;      ; NV8    # 5.1  BLACK LARGE SQUARE..BLACK PENTAGON\n2B20..2B23    ; valid                  ;      ; NV8    # 5.0  WHITE PENTAGON..HORIZONTAL BLACK HEXAGON\n2B24..2B4C    ; valid                  ;      ; NV8    # 5.1  BLACK LARGE CIRCLE..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR\n2B4D..2B4F    ; valid                  ;      ; NV8    # 7.0  DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..SHORT BACKSLANTED SOUTH ARROW\n2B50..2B54    ; valid                  ;      ; NV8    # 5.1  WHITE MEDIUM STAR..WHITE RIGHT-POINTING PENTAGON\n2B55..2B59    ; valid                  ;      ; NV8    # 5.2  HEAVY LARGE CIRCLE..HEAVY CIRCLED SALTIRE\n2B5A..2B73    ; valid                  ;      ; NV8    # 7.0  SLANTED NORTH ARROW WITH HOOKED HEAD..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR\n2B74..2B75    ; disallowed                             # NA   <reserved-2B74>..<reserved-2B75>\n2B76..2B95    ; valid                  ;      ; NV8    # 7.0  NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW\n2B96          ; disallowed                             # NA   <reserved-2B96>\n2B97          ; valid                  ;      ; NV8    # 13.0 SYMBOL FOR TYPE A ELECTRONICS\n2B98..2BB9    ; valid                  ;      ; NV8    # 7.0  THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..UP ARROWHEAD IN A RECTANGLE BOX\n2BBA..2BBC    ; valid                  ;      ; NV8    # 11.0 OVERLAPPING WHITE SQUARES..OVERLAPPING BLACK SQUARES\n2BBD..2BC8    ; valid                  ;      ; NV8    # 7.0  BALLOT BOX WITH LIGHT X..BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED\n2BC9          ; valid                  ;      ; NV8    # 12.0 NEPTUNE FORM TWO\n2BCA..2BD1    ; valid                  ;      ; NV8    # 7.0  TOP HALF BLACK CIRCLE..UNCERTAINTY SIGN\n2BD2          ; valid                  ;      ; NV8    # 10.0 GROUP MARK\n2BD3..2BEB    ; valid                  ;      ; NV8    # 11.0 PLUTO FORM TWO..STAR WITH RIGHT HALF BLACK\n2BEC..2BEF    ; valid                  ;      ; NV8    # 8.0  LEFTWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS..DOWNWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS\n2BF0..2BFE    ; valid                  ;      ; NV8    # 11.0 ERIS FORM ONE..REVERSED RIGHT ANGLE\n2BFF          ; valid                  ;      ; NV8    # 12.0 HELLSCHREIBER PAUSE SYMBOL\n2C00          ; mapped                 ; 2C30          # 4.1  GLAGOLITIC CAPITAL LETTER AZU\n2C01          ; mapped                 ; 2C31          # 4.1  GLAGOLITIC CAPITAL LETTER BUKY\n2C02          ; mapped                 ; 2C32          # 4.1  GLAGOLITIC CAPITAL LETTER VEDE\n2C03          ; mapped                 ; 2C33          # 4.1  GLAGOLITIC CAPITAL LETTER GLAGOLI\n2C04          ; mapped                 ; 2C34          # 4.1  GLAGOLITIC CAPITAL LETTER DOBRO\n2C05          ; mapped                 ; 2C35          # 4.1  GLAGOLITIC CAPITAL LETTER YESTU\n2C06          ; mapped                 ; 2C36          # 4.1  GLAGOLITIC CAPITAL LETTER ZHIVETE\n2C07          ; mapped                 ; 2C37          # 4.1  GLAGOLITIC CAPITAL LETTER DZELO\n2C08          ; mapped                 ; 2C38          # 4.1  GLAGOLITIC CAPITAL LETTER ZEMLJA\n2C09          ; mapped                 ; 2C39          # 4.1  GLAGOLITIC CAPITAL LETTER IZHE\n2C0A          ; mapped                 ; 2C3A          # 4.1  GLAGOLITIC CAPITAL LETTER INITIAL IZHE\n2C0B          ; mapped                 ; 2C3B          # 4.1  GLAGOLITIC CAPITAL LETTER I\n2C0C          ; mapped                 ; 2C3C          # 4.1  GLAGOLITIC CAPITAL LETTER DJERVI\n2C0D          ; mapped                 ; 2C3D          # 4.1  GLAGOLITIC CAPITAL LETTER KAKO\n2C0E          ; mapped                 ; 2C3E          # 4.1  GLAGOLITIC CAPITAL LETTER LJUDIJE\n2C0F          ; mapped                 ; 2C3F          # 4.1  GLAGOLITIC CAPITAL LETTER MYSLITE\n2C10          ; mapped                 ; 2C40          # 4.1  GLAGOLITIC CAPITAL LETTER NASHI\n2C11          ; mapped                 ; 2C41          # 4.1  GLAGOLITIC CAPITAL LETTER ONU\n2C12          ; mapped                 ; 2C42          # 4.1  GLAGOLITIC CAPITAL LETTER POKOJI\n2C13          ; mapped                 ; 2C43          # 4.1  GLAGOLITIC CAPITAL LETTER RITSI\n2C14          ; mapped                 ; 2C44          # 4.1  GLAGOLITIC CAPITAL LETTER SLOVO\n2C15          ; mapped                 ; 2C45          # 4.1  GLAGOLITIC CAPITAL LETTER TVRIDO\n2C16          ; mapped                 ; 2C46          # 4.1  GLAGOLITIC CAPITAL LETTER UKU\n2C17          ; mapped                 ; 2C47          # 4.1  GLAGOLITIC CAPITAL LETTER FRITU\n2C18          ; mapped                 ; 2C48          # 4.1  GLAGOLITIC CAPITAL LETTER HERU\n2C19          ; mapped                 ; 2C49          # 4.1  GLAGOLITIC CAPITAL LETTER OTU\n2C1A          ; mapped                 ; 2C4A          # 4.1  GLAGOLITIC CAPITAL LETTER PE\n2C1B          ; mapped                 ; 2C4B          # 4.1  GLAGOLITIC CAPITAL LETTER SHTA\n2C1C          ; mapped                 ; 2C4C          # 4.1  GLAGOLITIC CAPITAL LETTER TSI\n2C1D          ; mapped                 ; 2C4D          # 4.1  GLAGOLITIC CAPITAL LETTER CHRIVI\n2C1E          ; mapped                 ; 2C4E          # 4.1  GLAGOLITIC CAPITAL LETTER SHA\n2C1F          ; mapped                 ; 2C4F          # 4.1  GLAGOLITIC CAPITAL LETTER YERU\n2C20          ; mapped                 ; 2C50          # 4.1  GLAGOLITIC CAPITAL LETTER YERI\n2C21          ; mapped                 ; 2C51          # 4.1  GLAGOLITIC CAPITAL LETTER YATI\n2C22          ; mapped                 ; 2C52          # 4.1  GLAGOLITIC CAPITAL LETTER SPIDERY HA\n2C23          ; mapped                 ; 2C53          # 4.1  GLAGOLITIC CAPITAL LETTER YU\n2C24          ; mapped                 ; 2C54          # 4.1  GLAGOLITIC CAPITAL LETTER SMALL YUS\n2C25          ; mapped                 ; 2C55          # 4.1  GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL\n2C26          ; mapped                 ; 2C56          # 4.1  GLAGOLITIC CAPITAL LETTER YO\n2C27          ; mapped                 ; 2C57          # 4.1  GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS\n2C28          ; mapped                 ; 2C58          # 4.1  GLAGOLITIC CAPITAL LETTER BIG YUS\n2C29          ; mapped                 ; 2C59          # 4.1  GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS\n2C2A          ; mapped                 ; 2C5A          # 4.1  GLAGOLITIC CAPITAL LETTER FITA\n2C2B          ; mapped                 ; 2C5B          # 4.1  GLAGOLITIC CAPITAL LETTER IZHITSA\n2C2C          ; mapped                 ; 2C5C          # 4.1  GLAGOLITIC CAPITAL LETTER SHTAPIC\n2C2D          ; mapped                 ; 2C5D          # 4.1  GLAGOLITIC CAPITAL LETTER TROKUTASTI A\n2C2E          ; mapped                 ; 2C5E          # 4.1  GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE\n2C2F          ; mapped                 ; 2C5F          # 14.0 GLAGOLITIC CAPITAL LETTER CAUDATE CHRIVI\n2C30..2C5E    ; valid                                  # 4.1  GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE\n2C5F          ; valid                                  # 14.0 GLAGOLITIC SMALL LETTER CAUDATE CHRIVI\n2C60          ; mapped                 ; 2C61          # 5.0  LATIN CAPITAL LETTER L WITH DOUBLE BAR\n2C61          ; valid                                  # 5.0  LATIN SMALL LETTER L WITH DOUBLE BAR\n2C62          ; mapped                 ; 026B          # 5.0  LATIN CAPITAL LETTER L WITH MIDDLE TILDE\n2C63          ; mapped                 ; 1D7D          # 5.0  LATIN CAPITAL LETTER P WITH STROKE\n2C64          ; mapped                 ; 027D          # 5.0  LATIN CAPITAL LETTER R WITH TAIL\n2C65..2C66    ; valid                                  # 5.0  LATIN SMALL LETTER A WITH STROKE..LATIN SMALL LETTER T WITH DIAGONAL STROKE\n2C67          ; mapped                 ; 2C68          # 5.0  LATIN CAPITAL LETTER H WITH DESCENDER\n2C68          ; valid                                  # 5.0  LATIN SMALL LETTER H WITH DESCENDER\n2C69          ; mapped                 ; 2C6A          # 5.0  LATIN CAPITAL LETTER K WITH DESCENDER\n2C6A          ; valid                                  # 5.0  LATIN SMALL LETTER K WITH DESCENDER\n2C6B          ; mapped                 ; 2C6C          # 5.0  LATIN CAPITAL LETTER Z WITH DESCENDER\n2C6C          ; valid                                  # 5.0  LATIN SMALL LETTER Z WITH DESCENDER\n2C6D          ; mapped                 ; 0251          # 5.1  LATIN CAPITAL LETTER ALPHA\n2C6E          ; mapped                 ; 0271          # 5.1  LATIN CAPITAL LETTER M WITH HOOK\n2C6F          ; mapped                 ; 0250          # 5.1  LATIN CAPITAL LETTER TURNED A\n2C70          ; mapped                 ; 0252          # 5.2  LATIN CAPITAL LETTER TURNED ALPHA\n2C71          ; valid                                  # 5.1  LATIN SMALL LETTER V WITH RIGHT HOOK\n2C72          ; mapped                 ; 2C73          # 5.1  LATIN CAPITAL LETTER W WITH HOOK\n2C73          ; valid                                  # 5.1  LATIN SMALL LETTER W WITH HOOK\n2C74          ; valid                                  # 5.0  LATIN SMALL LETTER V WITH CURL\n2C75          ; mapped                 ; 2C76          # 5.0  LATIN CAPITAL LETTER HALF H\n2C76..2C77    ; valid                                  # 5.0  LATIN SMALL LETTER HALF H..LATIN SMALL LETTER TAILLESS PHI\n2C78..2C7B    ; valid                                  # 5.1  LATIN SMALL LETTER E WITH NOTCH..LATIN LETTER SMALL CAPITAL TURNED E\n2C7C          ; mapped                 ; 006A          # 5.1  LATIN SUBSCRIPT SMALL LETTER J\n2C7D          ; mapped                 ; 0076          # 5.1  MODIFIER LETTER CAPITAL V\n2C7E          ; mapped                 ; 023F          # 5.2  LATIN CAPITAL LETTER S WITH SWASH TAIL\n2C7F          ; mapped                 ; 0240          # 5.2  LATIN CAPITAL LETTER Z WITH SWASH TAIL\n2C80          ; mapped                 ; 2C81          # 4.1  COPTIC CAPITAL LETTER ALFA\n2C81          ; valid                                  # 4.1  COPTIC SMALL LETTER ALFA\n2C82          ; mapped                 ; 2C83          # 4.1  COPTIC CAPITAL LETTER VIDA\n2C83          ; valid                                  # 4.1  COPTIC SMALL LETTER VIDA\n2C84          ; mapped                 ; 2C85          # 4.1  COPTIC CAPITAL LETTER GAMMA\n2C85          ; valid                                  # 4.1  COPTIC SMALL LETTER GAMMA\n2C86          ; mapped                 ; 2C87          # 4.1  COPTIC CAPITAL LETTER DALDA\n2C87          ; valid                                  # 4.1  COPTIC SMALL LETTER DALDA\n2C88          ; mapped                 ; 2C89          # 4.1  COPTIC CAPITAL LETTER EIE\n2C89          ; valid                                  # 4.1  COPTIC SMALL LETTER EIE\n2C8A          ; mapped                 ; 2C8B          # 4.1  COPTIC CAPITAL LETTER SOU\n2C8B          ; valid                                  # 4.1  COPTIC SMALL LETTER SOU\n2C8C          ; mapped                 ; 2C8D          # 4.1  COPTIC CAPITAL LETTER ZATA\n2C8D          ; valid                                  # 4.1  COPTIC SMALL LETTER ZATA\n2C8E          ; mapped                 ; 2C8F          # 4.1  COPTIC CAPITAL LETTER HATE\n2C8F          ; valid                                  # 4.1  COPTIC SMALL LETTER HATE\n2C90          ; mapped                 ; 2C91          # 4.1  COPTIC CAPITAL LETTER THETHE\n2C91          ; valid                                  # 4.1  COPTIC SMALL LETTER THETHE\n2C92          ; mapped                 ; 2C93          # 4.1  COPTIC CAPITAL LETTER IAUDA\n2C93          ; valid                                  # 4.1  COPTIC SMALL LETTER IAUDA\n2C94          ; mapped                 ; 2C95          # 4.1  COPTIC CAPITAL LETTER KAPA\n2C95          ; valid                                  # 4.1  COPTIC SMALL LETTER KAPA\n2C96          ; mapped                 ; 2C97          # 4.1  COPTIC CAPITAL LETTER LAULA\n2C97          ; valid                                  # 4.1  COPTIC SMALL LETTER LAULA\n2C98          ; mapped                 ; 2C99          # 4.1  COPTIC CAPITAL LETTER MI\n2C99          ; valid                                  # 4.1  COPTIC SMALL LETTER MI\n2C9A          ; mapped                 ; 2C9B          # 4.1  COPTIC CAPITAL LETTER NI\n2C9B          ; valid                                  # 4.1  COPTIC SMALL LETTER NI\n2C9C          ; mapped                 ; 2C9D          # 4.1  COPTIC CAPITAL LETTER KSI\n2C9D          ; valid                                  # 4.1  COPTIC SMALL LETTER KSI\n2C9E          ; mapped                 ; 2C9F          # 4.1  COPTIC CAPITAL LETTER O\n2C9F          ; valid                                  # 4.1  COPTIC SMALL LETTER O\n2CA0          ; mapped                 ; 2CA1          # 4.1  COPTIC CAPITAL LETTER PI\n2CA1          ; valid                                  # 4.1  COPTIC SMALL LETTER PI\n2CA2          ; mapped                 ; 2CA3          # 4.1  COPTIC CAPITAL LETTER RO\n2CA3          ; valid                                  # 4.1  COPTIC SMALL LETTER RO\n2CA4          ; mapped                 ; 2CA5          # 4.1  COPTIC CAPITAL LETTER SIMA\n2CA5          ; valid                                  # 4.1  COPTIC SMALL LETTER SIMA\n2CA6          ; mapped                 ; 2CA7          # 4.1  COPTIC CAPITAL LETTER TAU\n2CA7          ; valid                                  # 4.1  COPTIC SMALL LETTER TAU\n2CA8          ; mapped                 ; 2CA9          # 4.1  COPTIC CAPITAL LETTER UA\n2CA9          ; valid                                  # 4.1  COPTIC SMALL LETTER UA\n2CAA          ; mapped                 ; 2CAB          # 4.1  COPTIC CAPITAL LETTER FI\n2CAB          ; valid                                  # 4.1  COPTIC SMALL LETTER FI\n2CAC          ; mapped                 ; 2CAD          # 4.1  COPTIC CAPITAL LETTER KHI\n2CAD          ; valid                                  # 4.1  COPTIC SMALL LETTER KHI\n2CAE          ; mapped                 ; 2CAF          # 4.1  COPTIC CAPITAL LETTER PSI\n2CAF          ; valid                                  # 4.1  COPTIC SMALL LETTER PSI\n2CB0          ; mapped                 ; 2CB1          # 4.1  COPTIC CAPITAL LETTER OOU\n2CB1          ; valid                                  # 4.1  COPTIC SMALL LETTER OOU\n2CB2          ; mapped                 ; 2CB3          # 4.1  COPTIC CAPITAL LETTER DIALECT-P ALEF\n2CB3          ; valid                                  # 4.1  COPTIC SMALL LETTER DIALECT-P ALEF\n2CB4          ; mapped                 ; 2CB5          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC AIN\n2CB5          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC AIN\n2CB6          ; mapped                 ; 2CB7          # 4.1  COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE\n2CB7          ; valid                                  # 4.1  COPTIC SMALL LETTER CRYPTOGRAMMIC EIE\n2CB8          ; mapped                 ; 2CB9          # 4.1  COPTIC CAPITAL LETTER DIALECT-P KAPA\n2CB9          ; valid                                  # 4.1  COPTIC SMALL LETTER DIALECT-P KAPA\n2CBA          ; mapped                 ; 2CBB          # 4.1  COPTIC CAPITAL LETTER DIALECT-P NI\n2CBB          ; valid                                  # 4.1  COPTIC SMALL LETTER DIALECT-P NI\n2CBC          ; mapped                 ; 2CBD          # 4.1  COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI\n2CBD          ; valid                                  # 4.1  COPTIC SMALL LETTER CRYPTOGRAMMIC NI\n2CBE          ; mapped                 ; 2CBF          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC OOU\n2CBF          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC OOU\n2CC0          ; mapped                 ; 2CC1          # 4.1  COPTIC CAPITAL LETTER SAMPI\n2CC1          ; valid                                  # 4.1  COPTIC SMALL LETTER SAMPI\n2CC2          ; mapped                 ; 2CC3          # 4.1  COPTIC CAPITAL LETTER CROSSED SHEI\n2CC3          ; valid                                  # 4.1  COPTIC SMALL LETTER CROSSED SHEI\n2CC4          ; mapped                 ; 2CC5          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC SHEI\n2CC5          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC SHEI\n2CC6          ; mapped                 ; 2CC7          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC ESH\n2CC7          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC ESH\n2CC8          ; mapped                 ; 2CC9          # 4.1  COPTIC CAPITAL LETTER AKHMIMIC KHEI\n2CC9          ; valid                                  # 4.1  COPTIC SMALL LETTER AKHMIMIC KHEI\n2CCA          ; mapped                 ; 2CCB          # 4.1  COPTIC CAPITAL LETTER DIALECT-P HORI\n2CCB          ; valid                                  # 4.1  COPTIC SMALL LETTER DIALECT-P HORI\n2CCC          ; mapped                 ; 2CCD          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC HORI\n2CCD          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC HORI\n2CCE          ; mapped                 ; 2CCF          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC HA\n2CCF          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC HA\n2CD0          ; mapped                 ; 2CD1          # 4.1  COPTIC CAPITAL LETTER L-SHAPED HA\n2CD1          ; valid                                  # 4.1  COPTIC SMALL LETTER L-SHAPED HA\n2CD2          ; mapped                 ; 2CD3          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC HEI\n2CD3          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC HEI\n2CD4          ; mapped                 ; 2CD5          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC HAT\n2CD5          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC HAT\n2CD6          ; mapped                 ; 2CD7          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC GANGIA\n2CD7          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC GANGIA\n2CD8          ; mapped                 ; 2CD9          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC DJA\n2CD9          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC DJA\n2CDA          ; mapped                 ; 2CDB          # 4.1  COPTIC CAPITAL LETTER OLD COPTIC SHIMA\n2CDB          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD COPTIC SHIMA\n2CDC          ; mapped                 ; 2CDD          # 4.1  COPTIC CAPITAL LETTER OLD NUBIAN SHIMA\n2CDD          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD NUBIAN SHIMA\n2CDE          ; mapped                 ; 2CDF          # 4.1  COPTIC CAPITAL LETTER OLD NUBIAN NGI\n2CDF          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD NUBIAN NGI\n2CE0          ; mapped                 ; 2CE1          # 4.1  COPTIC CAPITAL LETTER OLD NUBIAN NYI\n2CE1          ; valid                                  # 4.1  COPTIC SMALL LETTER OLD NUBIAN NYI\n2CE2          ; mapped                 ; 2CE3          # 4.1  COPTIC CAPITAL LETTER OLD NUBIAN WAU\n2CE3..2CE4    ; valid                                  # 4.1  COPTIC SMALL LETTER OLD NUBIAN WAU..COPTIC SYMBOL KAI\n2CE5..2CEA    ; valid                  ;      ; NV8    # 4.1  COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA\n2CEB          ; mapped                 ; 2CEC          # 5.2  COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI\n2CEC          ; valid                                  # 5.2  COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI\n2CED          ; mapped                 ; 2CEE          # 5.2  COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA\n2CEE..2CF1    ; valid                                  # 5.2  COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA..COPTIC COMBINING SPIRITUS LENIS\n2CF2          ; mapped                 ; 2CF3          # 6.1  COPTIC CAPITAL LETTER BOHAIRIC KHEI\n2CF3          ; valid                                  # 6.1  COPTIC SMALL LETTER BOHAIRIC KHEI\n2CF4..2CF8    ; disallowed                             # NA   <reserved-2CF4>..<reserved-2CF8>\n2CF9..2CFF    ; valid                  ;      ; NV8    # 4.1  COPTIC OLD NUBIAN FULL STOP..COPTIC MORPHOLOGICAL DIVIDER\n2D00..2D25    ; valid                                  # 4.1  GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE\n2D26          ; disallowed                             # NA   <reserved-2D26>\n2D27          ; valid                                  # 6.1  GEORGIAN SMALL LETTER YN\n2D28..2D2C    ; disallowed                             # NA   <reserved-2D28>..<reserved-2D2C>\n2D2D          ; valid                                  # 6.1  GEORGIAN SMALL LETTER AEN\n2D2E..2D2F    ; disallowed                             # NA   <reserved-2D2E>..<reserved-2D2F>\n2D30..2D65    ; valid                                  # 4.1  TIFINAGH LETTER YA..TIFINAGH LETTER YAZZ\n2D66..2D67    ; valid                                  # 6.1  TIFINAGH LETTER YE..TIFINAGH LETTER YO\n2D68..2D6E    ; disallowed                             # NA   <reserved-2D68>..<reserved-2D6E>\n2D6F          ; mapped                 ; 2D61          # 4.1  TIFINAGH MODIFIER LETTER LABIALIZATION MARK\n2D70          ; valid                  ;      ; NV8    # 6.0  TIFINAGH SEPARATOR MARK\n2D71..2D7E    ; disallowed                             # NA   <reserved-2D71>..<reserved-2D7E>\n2D7F          ; valid                                  # 6.0  TIFINAGH CONSONANT JOINER\n2D80..2D96    ; valid                                  # 4.1  ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE\n2D97..2D9F    ; disallowed                             # NA   <reserved-2D97>..<reserved-2D9F>\n2DA0..2DA6    ; valid                                  # 4.1  ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO\n2DA7          ; disallowed                             # NA   <reserved-2DA7>\n2DA8..2DAE    ; valid                                  # 4.1  ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO\n2DAF          ; disallowed                             # NA   <reserved-2DAF>\n2DB0..2DB6    ; valid                                  # 4.1  ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO\n2DB7          ; disallowed                             # NA   <reserved-2DB7>\n2DB8..2DBE    ; valid                                  # 4.1  ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO\n2DBF          ; disallowed                             # NA   <reserved-2DBF>\n2DC0..2DC6    ; valid                                  # 4.1  ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO\n2DC7          ; disallowed                             # NA   <reserved-2DC7>\n2DC8..2DCE    ; valid                                  # 4.1  ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO\n2DCF          ; disallowed                             # NA   <reserved-2DCF>\n2DD0..2DD6    ; valid                                  # 4.1  ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO\n2DD7          ; disallowed                             # NA   <reserved-2DD7>\n2DD8..2DDE    ; valid                                  # 4.1  ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO\n2DDF          ; disallowed                             # NA   <reserved-2DDF>\n2DE0..2DFF    ; valid                                  # 5.1  COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS\n2E00..2E17    ; valid                  ;      ; NV8    # 4.1  RIGHT ANGLE SUBSTITUTION MARKER..DOUBLE OBLIQUE HYPHEN\n2E18..2E1B    ; valid                  ;      ; NV8    # 5.1  INVERTED INTERROBANG..TILDE WITH RING ABOVE\n2E1C..2E1D    ; valid                  ;      ; NV8    # 4.1  LEFT LOW PARAPHRASE BRACKET..RIGHT LOW PARAPHRASE BRACKET\n2E1E..2E2E    ; valid                  ;      ; NV8    # 5.1  TILDE WITH DOT ABOVE..REVERSED QUESTION MARK\n2E2F          ; valid                                  # 5.1  VERTICAL TILDE\n2E30          ; valid                  ;      ; NV8    # 5.1  RING POINT\n2E31          ; valid                  ;      ; NV8    # 5.2  WORD SEPARATOR MIDDLE DOT\n2E32..2E3B    ; valid                  ;      ; NV8    # 6.1  TURNED COMMA..THREE-EM DASH\n2E3C..2E42    ; valid                  ;      ; NV8    # 7.0  STENOGRAPHIC FULL STOP..DOUBLE LOW-REVERSED-9 QUOTATION MARK\n2E43..2E44    ; valid                  ;      ; NV8    # 9.0  DASH WITH LEFT UPTURN..DOUBLE SUSPENSION MARK\n2E45..2E49    ; valid                  ;      ; NV8    # 10.0 INVERTED LOW KAVYKA..DOUBLE STACKED COMMA\n2E4A..2E4E    ; valid                  ;      ; NV8    # 11.0 DOTTED SOLIDUS..PUNCTUS ELEVATUS MARK\n2E4F          ; valid                  ;      ; NV8    # 12.0 CORNISH VERSE DIVIDER\n2E50..2E52    ; valid                  ;      ; NV8    # 13.0 CROSS PATTY WITH RIGHT CROSSBAR..TIRONIAN SIGN CAPITAL ET\n2E53..2E5D    ; valid                  ;      ; NV8    # 14.0 MEDIEVAL EXCLAMATION MARK..OBLIQUE HYPHEN\n2E5E..2E7F    ; disallowed                             # NA   <reserved-2E5E>..<reserved-2E7F>\n2E80..2E99    ; valid                  ;      ; NV8    # 3.0  CJK RADICAL REPEAT..CJK RADICAL RAP\n2E9A          ; disallowed                             # NA   <reserved-2E9A>\n2E9B..2E9E    ; valid                  ;      ; NV8    # 3.0  CJK RADICAL CHOKE..CJK RADICAL DEATH\n2E9F          ; mapped                 ; 6BCD          # 3.0  CJK RADICAL MOTHER\n2EA0..2EF2    ; valid                  ;      ; NV8    # 3.0  CJK RADICAL CIVILIAN..CJK RADICAL J-SIMPLIFIED TURTLE\n2EF3          ; mapped                 ; 9F9F          # 3.0  CJK RADICAL C-SIMPLIFIED TURTLE\n2EF4..2EFF    ; disallowed                             # NA   <reserved-2EF4>..<reserved-2EFF>\n2F00          ; mapped                 ; 4E00          # 3.0  KANGXI RADICAL ONE\n2F01          ; mapped                 ; 4E28          # 3.0  KANGXI RADICAL LINE\n2F02          ; mapped                 ; 4E36          # 3.0  KANGXI RADICAL DOT\n2F03          ; mapped                 ; 4E3F          # 3.0  KANGXI RADICAL SLASH\n2F04          ; mapped                 ; 4E59          # 3.0  KANGXI RADICAL SECOND\n2F05          ; mapped                 ; 4E85          # 3.0  KANGXI RADICAL HOOK\n2F06          ; mapped                 ; 4E8C          # 3.0  KANGXI RADICAL TWO\n2F07          ; mapped                 ; 4EA0          # 3.0  KANGXI RADICAL LID\n2F08          ; mapped                 ; 4EBA          # 3.0  KANGXI RADICAL MAN\n2F09          ; mapped                 ; 513F          # 3.0  KANGXI RADICAL LEGS\n2F0A          ; mapped                 ; 5165          # 3.0  KANGXI RADICAL ENTER\n2F0B          ; mapped                 ; 516B          # 3.0  KANGXI RADICAL EIGHT\n2F0C          ; mapped                 ; 5182          # 3.0  KANGXI RADICAL DOWN BOX\n2F0D          ; mapped                 ; 5196          # 3.0  KANGXI RADICAL COVER\n2F0E          ; mapped                 ; 51AB          # 3.0  KANGXI RADICAL ICE\n2F0F          ; mapped                 ; 51E0          # 3.0  KANGXI RADICAL TABLE\n2F10          ; mapped                 ; 51F5          # 3.0  KANGXI RADICAL OPEN BOX\n2F11          ; mapped                 ; 5200          # 3.0  KANGXI RADICAL KNIFE\n2F12          ; mapped                 ; 529B          # 3.0  KANGXI RADICAL POWER\n2F13          ; mapped                 ; 52F9          # 3.0  KANGXI RADICAL WRAP\n2F14          ; mapped                 ; 5315          # 3.0  KANGXI RADICAL SPOON\n2F15          ; mapped                 ; 531A          # 3.0  KANGXI RADICAL RIGHT OPEN BOX\n2F16          ; mapped                 ; 5338          # 3.0  KANGXI RADICAL HIDING ENCLOSURE\n2F17          ; mapped                 ; 5341          # 3.0  KANGXI RADICAL TEN\n2F18          ; mapped                 ; 535C          # 3.0  KANGXI RADICAL DIVINATION\n2F19          ; mapped                 ; 5369          # 3.0  KANGXI RADICAL SEAL\n2F1A          ; mapped                 ; 5382          # 3.0  KANGXI RADICAL CLIFF\n2F1B          ; mapped                 ; 53B6          # 3.0  KANGXI RADICAL PRIVATE\n2F1C          ; mapped                 ; 53C8          # 3.0  KANGXI RADICAL AGAIN\n2F1D          ; mapped                 ; 53E3          # 3.0  KANGXI RADICAL MOUTH\n2F1E          ; mapped                 ; 56D7          # 3.0  KANGXI RADICAL ENCLOSURE\n2F1F          ; mapped                 ; 571F          # 3.0  KANGXI RADICAL EARTH\n2F20          ; mapped                 ; 58EB          # 3.0  KANGXI RADICAL SCHOLAR\n2F21          ; mapped                 ; 5902          # 3.0  KANGXI RADICAL GO\n2F22          ; mapped                 ; 590A          # 3.0  KANGXI RADICAL GO SLOWLY\n2F23          ; mapped                 ; 5915          # 3.0  KANGXI RADICAL EVENING\n2F24          ; mapped                 ; 5927          # 3.0  KANGXI RADICAL BIG\n2F25          ; mapped                 ; 5973          # 3.0  KANGXI RADICAL WOMAN\n2F26          ; mapped                 ; 5B50          # 3.0  KANGXI RADICAL CHILD\n2F27          ; mapped                 ; 5B80          # 3.0  KANGXI RADICAL ROOF\n2F28          ; mapped                 ; 5BF8          # 3.0  KANGXI RADICAL INCH\n2F29          ; mapped                 ; 5C0F          # 3.0  KANGXI RADICAL SMALL\n2F2A          ; mapped                 ; 5C22          # 3.0  KANGXI RADICAL LAME\n2F2B          ; mapped                 ; 5C38          # 3.0  KANGXI RADICAL CORPSE\n2F2C          ; mapped                 ; 5C6E          # 3.0  KANGXI RADICAL SPROUT\n2F2D          ; mapped                 ; 5C71          # 3.0  KANGXI RADICAL MOUNTAIN\n2F2E          ; mapped                 ; 5DDB          # 3.0  KANGXI RADICAL RIVER\n2F2F          ; mapped                 ; 5DE5          # 3.0  KANGXI RADICAL WORK\n2F30          ; mapped                 ; 5DF1          # 3.0  KANGXI RADICAL ONESELF\n2F31          ; mapped                 ; 5DFE          # 3.0  KANGXI RADICAL TURBAN\n2F32          ; mapped                 ; 5E72          # 3.0  KANGXI RADICAL DRY\n2F33          ; mapped                 ; 5E7A          # 3.0  KANGXI RADICAL SHORT THREAD\n2F34          ; mapped                 ; 5E7F          # 3.0  KANGXI RADICAL DOTTED CLIFF\n2F35          ; mapped                 ; 5EF4          # 3.0  KANGXI RADICAL LONG STRIDE\n2F36          ; mapped                 ; 5EFE          # 3.0  KANGXI RADICAL TWO HANDS\n2F37          ; mapped                 ; 5F0B          # 3.0  KANGXI RADICAL SHOOT\n2F38          ; mapped                 ; 5F13          # 3.0  KANGXI RADICAL BOW\n2F39          ; mapped                 ; 5F50          # 3.0  KANGXI RADICAL SNOUT\n2F3A          ; mapped                 ; 5F61          # 3.0  KANGXI RADICAL BRISTLE\n2F3B          ; mapped                 ; 5F73          # 3.0  KANGXI RADICAL STEP\n2F3C          ; mapped                 ; 5FC3          # 3.0  KANGXI RADICAL HEART\n2F3D          ; mapped                 ; 6208          # 3.0  KANGXI RADICAL HALBERD\n2F3E          ; mapped                 ; 6236          # 3.0  KANGXI RADICAL DOOR\n2F3F          ; mapped                 ; 624B          # 3.0  KANGXI RADICAL HAND\n2F40          ; mapped                 ; 652F          # 3.0  KANGXI RADICAL BRANCH\n2F41          ; mapped                 ; 6534          # 3.0  KANGXI RADICAL RAP\n2F42          ; mapped                 ; 6587          # 3.0  KANGXI RADICAL SCRIPT\n2F43          ; mapped                 ; 6597          # 3.0  KANGXI RADICAL DIPPER\n2F44          ; mapped                 ; 65A4          # 3.0  KANGXI RADICAL AXE\n2F45          ; mapped                 ; 65B9          # 3.0  KANGXI RADICAL SQUARE\n2F46          ; mapped                 ; 65E0          # 3.0  KANGXI RADICAL NOT\n2F47          ; mapped                 ; 65E5          # 3.0  KANGXI RADICAL SUN\n2F48          ; mapped                 ; 66F0          # 3.0  KANGXI RADICAL SAY\n2F49          ; mapped                 ; 6708          # 3.0  KANGXI RADICAL MOON\n2F4A          ; mapped                 ; 6728          # 3.0  KANGXI RADICAL TREE\n2F4B          ; mapped                 ; 6B20          # 3.0  KANGXI RADICAL LACK\n2F4C          ; mapped                 ; 6B62          # 3.0  KANGXI RADICAL STOP\n2F4D          ; mapped                 ; 6B79          # 3.0  KANGXI RADICAL DEATH\n2F4E          ; mapped                 ; 6BB3          # 3.0  KANGXI RADICAL WEAPON\n2F4F          ; mapped                 ; 6BCB          # 3.0  KANGXI RADICAL DO NOT\n2F50          ; mapped                 ; 6BD4          # 3.0  KANGXI RADICAL COMPARE\n2F51          ; mapped                 ; 6BDB          # 3.0  KANGXI RADICAL FUR\n2F52          ; mapped                 ; 6C0F          # 3.0  KANGXI RADICAL CLAN\n2F53          ; mapped                 ; 6C14          # 3.0  KANGXI RADICAL STEAM\n2F54          ; mapped                 ; 6C34          # 3.0  KANGXI RADICAL WATER\n2F55          ; mapped                 ; 706B          # 3.0  KANGXI RADICAL FIRE\n2F56          ; mapped                 ; 722A          # 3.0  KANGXI RADICAL CLAW\n2F57          ; mapped                 ; 7236          # 3.0  KANGXI RADICAL FATHER\n2F58          ; mapped                 ; 723B          # 3.0  KANGXI RADICAL DOUBLE X\n2F59          ; mapped                 ; 723F          # 3.0  KANGXI RADICAL HALF TREE TRUNK\n2F5A          ; mapped                 ; 7247          # 3.0  KANGXI RADICAL SLICE\n2F5B          ; mapped                 ; 7259          # 3.0  KANGXI RADICAL FANG\n2F5C          ; mapped                 ; 725B          # 3.0  KANGXI RADICAL COW\n2F5D          ; mapped                 ; 72AC          # 3.0  KANGXI RADICAL DOG\n2F5E          ; mapped                 ; 7384          # 3.0  KANGXI RADICAL PROFOUND\n2F5F          ; mapped                 ; 7389          # 3.0  KANGXI RADICAL JADE\n2F60          ; mapped                 ; 74DC          # 3.0  KANGXI RADICAL MELON\n2F61          ; mapped                 ; 74E6          # 3.0  KANGXI RADICAL TILE\n2F62          ; mapped                 ; 7518          # 3.0  KANGXI RADICAL SWEET\n2F63          ; mapped                 ; 751F          # 3.0  KANGXI RADICAL LIFE\n2F64          ; mapped                 ; 7528          # 3.0  KANGXI RADICAL USE\n2F65          ; mapped                 ; 7530          # 3.0  KANGXI RADICAL FIELD\n2F66          ; mapped                 ; 758B          # 3.0  KANGXI RADICAL BOLT OF CLOTH\n2F67          ; mapped                 ; 7592          # 3.0  KANGXI RADICAL SICKNESS\n2F68          ; mapped                 ; 7676          # 3.0  KANGXI RADICAL DOTTED TENT\n2F69          ; mapped                 ; 767D          # 3.0  KANGXI RADICAL WHITE\n2F6A          ; mapped                 ; 76AE          # 3.0  KANGXI RADICAL SKIN\n2F6B          ; mapped                 ; 76BF          # 3.0  KANGXI RADICAL DISH\n2F6C          ; mapped                 ; 76EE          # 3.0  KANGXI RADICAL EYE\n2F6D          ; mapped                 ; 77DB          # 3.0  KANGXI RADICAL SPEAR\n2F6E          ; mapped                 ; 77E2          # 3.0  KANGXI RADICAL ARROW\n2F6F          ; mapped                 ; 77F3          # 3.0  KANGXI RADICAL STONE\n2F70          ; mapped                 ; 793A          # 3.0  KANGXI RADICAL SPIRIT\n2F71          ; mapped                 ; 79B8          # 3.0  KANGXI RADICAL TRACK\n2F72          ; mapped                 ; 79BE          # 3.0  KANGXI RADICAL GRAIN\n2F73          ; mapped                 ; 7A74          # 3.0  KANGXI RADICAL CAVE\n2F74          ; mapped                 ; 7ACB          # 3.0  KANGXI RADICAL STAND\n2F75          ; mapped                 ; 7AF9          # 3.0  KANGXI RADICAL BAMBOO\n2F76          ; mapped                 ; 7C73          # 3.0  KANGXI RADICAL RICE\n2F77          ; mapped                 ; 7CF8          # 3.0  KANGXI RADICAL SILK\n2F78          ; mapped                 ; 7F36          # 3.0  KANGXI RADICAL JAR\n2F79          ; mapped                 ; 7F51          # 3.0  KANGXI RADICAL NET\n2F7A          ; mapped                 ; 7F8A          # 3.0  KANGXI RADICAL SHEEP\n2F7B          ; mapped                 ; 7FBD          # 3.0  KANGXI RADICAL FEATHER\n2F7C          ; mapped                 ; 8001          # 3.0  KANGXI RADICAL OLD\n2F7D          ; mapped                 ; 800C          # 3.0  KANGXI RADICAL AND\n2F7E          ; mapped                 ; 8012          # 3.0  KANGXI RADICAL PLOW\n2F7F          ; mapped                 ; 8033          # 3.0  KANGXI RADICAL EAR\n2F80          ; mapped                 ; 807F          # 3.0  KANGXI RADICAL BRUSH\n2F81          ; mapped                 ; 8089          # 3.0  KANGXI RADICAL MEAT\n2F82          ; mapped                 ; 81E3          # 3.0  KANGXI RADICAL MINISTER\n2F83          ; mapped                 ; 81EA          # 3.0  KANGXI RADICAL SELF\n2F84          ; mapped                 ; 81F3          # 3.0  KANGXI RADICAL ARRIVE\n2F85          ; mapped                 ; 81FC          # 3.0  KANGXI RADICAL MORTAR\n2F86          ; mapped                 ; 820C          # 3.0  KANGXI RADICAL TONGUE\n2F87          ; mapped                 ; 821B          # 3.0  KANGXI RADICAL OPPOSE\n2F88          ; mapped                 ; 821F          # 3.0  KANGXI RADICAL BOAT\n2F89          ; mapped                 ; 826E          # 3.0  KANGXI RADICAL STOPPING\n2F8A          ; mapped                 ; 8272          # 3.0  KANGXI RADICAL COLOR\n2F8B          ; mapped                 ; 8278          # 3.0  KANGXI RADICAL GRASS\n2F8C          ; mapped                 ; 864D          # 3.0  KANGXI RADICAL TIGER\n2F8D          ; mapped                 ; 866B          # 3.0  KANGXI RADICAL INSECT\n2F8E          ; mapped                 ; 8840          # 3.0  KANGXI RADICAL BLOOD\n2F8F          ; mapped                 ; 884C          # 3.0  KANGXI RADICAL WALK ENCLOSURE\n2F90          ; mapped                 ; 8863          # 3.0  KANGXI RADICAL CLOTHES\n2F91          ; mapped                 ; 897E          # 3.0  KANGXI RADICAL WEST\n2F92          ; mapped                 ; 898B          # 3.0  KANGXI RADICAL SEE\n2F93          ; mapped                 ; 89D2          # 3.0  KANGXI RADICAL HORN\n2F94          ; mapped                 ; 8A00          # 3.0  KANGXI RADICAL SPEECH\n2F95          ; mapped                 ; 8C37          # 3.0  KANGXI RADICAL VALLEY\n2F96          ; mapped                 ; 8C46          # 3.0  KANGXI RADICAL BEAN\n2F97          ; mapped                 ; 8C55          # 3.0  KANGXI RADICAL PIG\n2F98          ; mapped                 ; 8C78          # 3.0  KANGXI RADICAL BADGER\n2F99          ; mapped                 ; 8C9D          # 3.0  KANGXI RADICAL SHELL\n2F9A          ; mapped                 ; 8D64          # 3.0  KANGXI RADICAL RED\n2F9B          ; mapped                 ; 8D70          # 3.0  KANGXI RADICAL RUN\n2F9C          ; mapped                 ; 8DB3          # 3.0  KANGXI RADICAL FOOT\n2F9D          ; mapped                 ; 8EAB          # 3.0  KANGXI RADICAL BODY\n2F9E          ; mapped                 ; 8ECA          # 3.0  KANGXI RADICAL CART\n2F9F          ; mapped                 ; 8F9B          # 3.0  KANGXI RADICAL BITTER\n2FA0          ; mapped                 ; 8FB0          # 3.0  KANGXI RADICAL MORNING\n2FA1          ; mapped                 ; 8FB5          # 3.0  KANGXI RADICAL WALK\n2FA2          ; mapped                 ; 9091          # 3.0  KANGXI RADICAL CITY\n2FA3          ; mapped                 ; 9149          # 3.0  KANGXI RADICAL WINE\n2FA4          ; mapped                 ; 91C6          # 3.0  KANGXI RADICAL DISTINGUISH\n2FA5          ; mapped                 ; 91CC          # 3.0  KANGXI RADICAL VILLAGE\n2FA6          ; mapped                 ; 91D1          # 3.0  KANGXI RADICAL GOLD\n2FA7          ; mapped                 ; 9577          # 3.0  KANGXI RADICAL LONG\n2FA8          ; mapped                 ; 9580          # 3.0  KANGXI RADICAL GATE\n2FA9          ; mapped                 ; 961C          # 3.0  KANGXI RADICAL MOUND\n2FAA          ; mapped                 ; 96B6          # 3.0  KANGXI RADICAL SLAVE\n2FAB          ; mapped                 ; 96B9          # 3.0  KANGXI RADICAL SHORT TAILED BIRD\n2FAC          ; mapped                 ; 96E8          # 3.0  KANGXI RADICAL RAIN\n2FAD          ; mapped                 ; 9751          # 3.0  KANGXI RADICAL BLUE\n2FAE          ; mapped                 ; 975E          # 3.0  KANGXI RADICAL WRONG\n2FAF          ; mapped                 ; 9762          # 3.0  KANGXI RADICAL FACE\n2FB0          ; mapped                 ; 9769          # 3.0  KANGXI RADICAL LEATHER\n2FB1          ; mapped                 ; 97CB          # 3.0  KANGXI RADICAL TANNED LEATHER\n2FB2          ; mapped                 ; 97ED          # 3.0  KANGXI RADICAL LEEK\n2FB3          ; mapped                 ; 97F3          # 3.0  KANGXI RADICAL SOUND\n2FB4          ; mapped                 ; 9801          # 3.0  KANGXI RADICAL LEAF\n2FB5          ; mapped                 ; 98A8          # 3.0  KANGXI RADICAL WIND\n2FB6          ; mapped                 ; 98DB          # 3.0  KANGXI RADICAL FLY\n2FB7          ; mapped                 ; 98DF          # 3.0  KANGXI RADICAL EAT\n2FB8          ; mapped                 ; 9996          # 3.0  KANGXI RADICAL HEAD\n2FB9          ; mapped                 ; 9999          # 3.0  KANGXI RADICAL FRAGRANT\n2FBA          ; mapped                 ; 99AC          # 3.0  KANGXI RADICAL HORSE\n2FBB          ; mapped                 ; 9AA8          # 3.0  KANGXI RADICAL BONE\n2FBC          ; mapped                 ; 9AD8          # 3.0  KANGXI RADICAL TALL\n2FBD          ; mapped                 ; 9ADF          # 3.0  KANGXI RADICAL HAIR\n2FBE          ; mapped                 ; 9B25          # 3.0  KANGXI RADICAL FIGHT\n2FBF          ; mapped                 ; 9B2F          # 3.0  KANGXI RADICAL SACRIFICIAL WINE\n2FC0          ; mapped                 ; 9B32          # 3.0  KANGXI RADICAL CAULDRON\n2FC1          ; mapped                 ; 9B3C          # 3.0  KANGXI RADICAL GHOST\n2FC2          ; mapped                 ; 9B5A          # 3.0  KANGXI RADICAL FISH\n2FC3          ; mapped                 ; 9CE5          # 3.0  KANGXI RADICAL BIRD\n2FC4          ; mapped                 ; 9E75          # 3.0  KANGXI RADICAL SALT\n2FC5          ; mapped                 ; 9E7F          # 3.0  KANGXI RADICAL DEER\n2FC6          ; mapped                 ; 9EA5          # 3.0  KANGXI RADICAL WHEAT\n2FC7          ; mapped                 ; 9EBB          # 3.0  KANGXI RADICAL HEMP\n2FC8          ; mapped                 ; 9EC3          # 3.0  KANGXI RADICAL YELLOW\n2FC9          ; mapped                 ; 9ECD          # 3.0  KANGXI RADICAL MILLET\n2FCA          ; mapped                 ; 9ED1          # 3.0  KANGXI RADICAL BLACK\n2FCB          ; mapped                 ; 9EF9          # 3.0  KANGXI RADICAL EMBROIDERY\n2FCC          ; mapped                 ; 9EFD          # 3.0  KANGXI RADICAL FROG\n2FCD          ; mapped                 ; 9F0E          # 3.0  KANGXI RADICAL TRIPOD\n2FCE          ; mapped                 ; 9F13          # 3.0  KANGXI RADICAL DRUM\n2FCF          ; mapped                 ; 9F20          # 3.0  KANGXI RADICAL RAT\n2FD0          ; mapped                 ; 9F3B          # 3.0  KANGXI RADICAL NOSE\n2FD1          ; mapped                 ; 9F4A          # 3.0  KANGXI RADICAL EVEN\n2FD2          ; mapped                 ; 9F52          # 3.0  KANGXI RADICAL TOOTH\n2FD3          ; mapped                 ; 9F8D          # 3.0  KANGXI RADICAL DRAGON\n2FD4          ; mapped                 ; 9F9C          # 3.0  KANGXI RADICAL TURTLE\n2FD5          ; mapped                 ; 9FA0          # 3.0  KANGXI RADICAL FLUTE\n2FD6..2FEF    ; disallowed                             # NA   <reserved-2FD6>..<reserved-2FEF>\n2FF0..2FFB    ; disallowed                             # 3.0  IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID\n2FFC..2FFF    ; disallowed                             # 15.1 IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER ROTATION\n3000          ; disallowed_STD3_mapped ; 0020          # 1.1  IDEOGRAPHIC SPACE\n3001          ; valid                  ;      ; NV8    # 1.1  IDEOGRAPHIC COMMA\n3002          ; mapped                 ; 002E          # 1.1  IDEOGRAPHIC FULL STOP\n3003..3004    ; valid                  ;      ; NV8    # 1.1  DITTO MARK..JAPANESE INDUSTRIAL STANDARD SYMBOL\n3005..3007    ; valid                                  # 1.1  IDEOGRAPHIC ITERATION MARK..IDEOGRAPHIC NUMBER ZERO\n3008..3029    ; valid                  ;      ; NV8    # 1.1  LEFT ANGLE BRACKET..HANGZHOU NUMERAL NINE\n302A..302D    ; valid                                  # 1.1  IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK\n302E..3035    ; valid                  ;      ; NV8    # 1.1  HANGUL SINGLE DOT TONE MARK..VERTICAL KANA REPEAT MARK LOWER HALF\n3036          ; mapped                 ; 3012          # 1.1  CIRCLED POSTAL MARK\n3037          ; valid                  ;      ; NV8    # 1.1  IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL\n3038          ; mapped                 ; 5341          # 3.0  HANGZHOU NUMERAL TEN\n3039          ; mapped                 ; 5344          # 3.0  HANGZHOU NUMERAL TWENTY\n303A          ; mapped                 ; 5345          # 3.0  HANGZHOU NUMERAL THIRTY\n303B          ; valid                  ;      ; NV8    # 3.2  VERTICAL IDEOGRAPHIC ITERATION MARK\n303C          ; valid                                  # 3.2  MASU MARK\n303D          ; valid                  ;      ; NV8    # 3.2  PART ALTERNATION MARK\n303E          ; valid                  ;      ; NV8    # 3.0  IDEOGRAPHIC VARIATION INDICATOR\n303F          ; valid                  ;      ; NV8    # 1.1  IDEOGRAPHIC HALF FILL SPACE\n3040          ; disallowed                             # NA   <reserved-3040>\n3041..3094    ; valid                                  # 1.1  HIRAGANA LETTER SMALL A..HIRAGANA LETTER VU\n3095..3096    ; valid                                  # 3.2  HIRAGANA LETTER SMALL KA..HIRAGANA LETTER SMALL KE\n3097..3098    ; disallowed                             # NA   <reserved-3097>..<reserved-3098>\n3099..309A    ; valid                                  # 1.1  COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK\n309B          ; disallowed_STD3_mapped ; 0020 3099     # 1.1  KATAKANA-HIRAGANA VOICED SOUND MARK\n309C          ; disallowed_STD3_mapped ; 0020 309A     # 1.1  KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK\n309D..309E    ; valid                                  # 1.1  HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK\n309F          ; mapped                 ; 3088 308A     # 3.2  HIRAGANA DIGRAPH YORI\n30A0          ; valid                  ;      ; NV8    # 3.2  KATAKANA-HIRAGANA DOUBLE HYPHEN\n30A1..30FE    ; valid                                  # 1.1  KATAKANA LETTER SMALL A..KATAKANA VOICED ITERATION MARK\n30FF          ; mapped                 ; 30B3 30C8     # 3.2  KATAKANA DIGRAPH KOTO\n3100..3104    ; disallowed                             # NA   <reserved-3100>..<reserved-3104>\n3105..312C    ; valid                                  # 1.1  BOPOMOFO LETTER B..BOPOMOFO LETTER GN\n312D          ; valid                                  # 5.1  BOPOMOFO LETTER IH\n312E          ; valid                                  # 10.0 BOPOMOFO LETTER O WITH DOT ABOVE\n312F          ; valid                                  # 11.0 BOPOMOFO LETTER NN\n3130          ; disallowed                             # NA   <reserved-3130>\n3131          ; mapped                 ; 1100          # 1.1  HANGUL LETTER KIYEOK\n3132          ; mapped                 ; 1101          # 1.1  HANGUL LETTER SSANGKIYEOK\n3133          ; mapped                 ; 11AA          # 1.1  HANGUL LETTER KIYEOK-SIOS\n3134          ; mapped                 ; 1102          # 1.1  HANGUL LETTER NIEUN\n3135          ; mapped                 ; 11AC          # 1.1  HANGUL LETTER NIEUN-CIEUC\n3136          ; mapped                 ; 11AD          # 1.1  HANGUL LETTER NIEUN-HIEUH\n3137          ; mapped                 ; 1103          # 1.1  HANGUL LETTER TIKEUT\n3138          ; mapped                 ; 1104          # 1.1  HANGUL LETTER SSANGTIKEUT\n3139          ; mapped                 ; 1105          # 1.1  HANGUL LETTER RIEUL\n313A          ; mapped                 ; 11B0          # 1.1  HANGUL LETTER RIEUL-KIYEOK\n313B          ; mapped                 ; 11B1          # 1.1  HANGUL LETTER RIEUL-MIEUM\n313C          ; mapped                 ; 11B2          # 1.1  HANGUL LETTER RIEUL-PIEUP\n313D          ; mapped                 ; 11B3          # 1.1  HANGUL LETTER RIEUL-SIOS\n313E          ; mapped                 ; 11B4          # 1.1  HANGUL LETTER RIEUL-THIEUTH\n313F          ; mapped                 ; 11B5          # 1.1  HANGUL LETTER RIEUL-PHIEUPH\n3140          ; mapped                 ; 111A          # 1.1  HANGUL LETTER RIEUL-HIEUH\n3141          ; mapped                 ; 1106          # 1.1  HANGUL LETTER MIEUM\n3142          ; mapped                 ; 1107          # 1.1  HANGUL LETTER PIEUP\n3143          ; mapped                 ; 1108          # 1.1  HANGUL LETTER SSANGPIEUP\n3144          ; mapped                 ; 1121          # 1.1  HANGUL LETTER PIEUP-SIOS\n3145          ; mapped                 ; 1109          # 1.1  HANGUL LETTER SIOS\n3146          ; mapped                 ; 110A          # 1.1  HANGUL LETTER SSANGSIOS\n3147          ; mapped                 ; 110B          # 1.1  HANGUL LETTER IEUNG\n3148          ; mapped                 ; 110C          # 1.1  HANGUL LETTER CIEUC\n3149          ; mapped                 ; 110D          # 1.1  HANGUL LETTER SSANGCIEUC\n314A          ; mapped                 ; 110E          # 1.1  HANGUL LETTER CHIEUCH\n314B          ; mapped                 ; 110F          # 1.1  HANGUL LETTER KHIEUKH\n314C          ; mapped                 ; 1110          # 1.1  HANGUL LETTER THIEUTH\n314D          ; mapped                 ; 1111          # 1.1  HANGUL LETTER PHIEUPH\n314E          ; mapped                 ; 1112          # 1.1  HANGUL LETTER HIEUH\n314F          ; mapped                 ; 1161          # 1.1  HANGUL LETTER A\n3150          ; mapped                 ; 1162          # 1.1  HANGUL LETTER AE\n3151          ; mapped                 ; 1163          # 1.1  HANGUL LETTER YA\n3152          ; mapped                 ; 1164          # 1.1  HANGUL LETTER YAE\n3153          ; mapped                 ; 1165          # 1.1  HANGUL LETTER EO\n3154          ; mapped                 ; 1166          # 1.1  HANGUL LETTER E\n3155          ; mapped                 ; 1167          # 1.1  HANGUL LETTER YEO\n3156          ; mapped                 ; 1168          # 1.1  HANGUL LETTER YE\n3157          ; mapped                 ; 1169          # 1.1  HANGUL LETTER O\n3158          ; mapped                 ; 116A          # 1.1  HANGUL LETTER WA\n3159          ; mapped                 ; 116B          # 1.1  HANGUL LETTER WAE\n315A          ; mapped                 ; 116C          # 1.1  HANGUL LETTER OE\n315B          ; mapped                 ; 116D          # 1.1  HANGUL LETTER YO\n315C          ; mapped                 ; 116E          # 1.1  HANGUL LETTER U\n315D          ; mapped                 ; 116F          # 1.1  HANGUL LETTER WEO\n315E          ; mapped                 ; 1170          # 1.1  HANGUL LETTER WE\n315F          ; mapped                 ; 1171          # 1.1  HANGUL LETTER WI\n3160          ; mapped                 ; 1172          # 1.1  HANGUL LETTER YU\n3161          ; mapped                 ; 1173          # 1.1  HANGUL LETTER EU\n3162          ; mapped                 ; 1174          # 1.1  HANGUL LETTER YI\n3163          ; mapped                 ; 1175          # 1.1  HANGUL LETTER I\n3164          ; disallowed                             # 1.1  HANGUL FILLER\n3165          ; mapped                 ; 1114          # 1.1  HANGUL LETTER SSANGNIEUN\n3166          ; mapped                 ; 1115          # 1.1  HANGUL LETTER NIEUN-TIKEUT\n3167          ; mapped                 ; 11C7          # 1.1  HANGUL LETTER NIEUN-SIOS\n3168          ; mapped                 ; 11C8          # 1.1  HANGUL LETTER NIEUN-PANSIOS\n3169          ; mapped                 ; 11CC          # 1.1  HANGUL LETTER RIEUL-KIYEOK-SIOS\n316A          ; mapped                 ; 11CE          # 1.1  HANGUL LETTER RIEUL-TIKEUT\n316B          ; mapped                 ; 11D3          # 1.1  HANGUL LETTER RIEUL-PIEUP-SIOS\n316C          ; mapped                 ; 11D7          # 1.1  HANGUL LETTER RIEUL-PANSIOS\n316D          ; mapped                 ; 11D9          # 1.1  HANGUL LETTER RIEUL-YEORINHIEUH\n316E          ; mapped                 ; 111C          # 1.1  HANGUL LETTER MIEUM-PIEUP\n316F          ; mapped                 ; 11DD          # 1.1  HANGUL LETTER MIEUM-SIOS\n3170          ; mapped                 ; 11DF          # 1.1  HANGUL LETTER MIEUM-PANSIOS\n3171          ; mapped                 ; 111D          # 1.1  HANGUL LETTER KAPYEOUNMIEUM\n3172          ; mapped                 ; 111E          # 1.1  HANGUL LETTER PIEUP-KIYEOK\n3173          ; mapped                 ; 1120          # 1.1  HANGUL LETTER PIEUP-TIKEUT\n3174          ; mapped                 ; 1122          # 1.1  HANGUL LETTER PIEUP-SIOS-KIYEOK\n3175          ; mapped                 ; 1123          # 1.1  HANGUL LETTER PIEUP-SIOS-TIKEUT\n3176          ; mapped                 ; 1127          # 1.1  HANGUL LETTER PIEUP-CIEUC\n3177          ; mapped                 ; 1129          # 1.1  HANGUL LETTER PIEUP-THIEUTH\n3178          ; mapped                 ; 112B          # 1.1  HANGUL LETTER KAPYEOUNPIEUP\n3179          ; mapped                 ; 112C          # 1.1  HANGUL LETTER KAPYEOUNSSANGPIEUP\n317A          ; mapped                 ; 112D          # 1.1  HANGUL LETTER SIOS-KIYEOK\n317B          ; mapped                 ; 112E          # 1.1  HANGUL LETTER SIOS-NIEUN\n317C          ; mapped                 ; 112F          # 1.1  HANGUL LETTER SIOS-TIKEUT\n317D          ; mapped                 ; 1132          # 1.1  HANGUL LETTER SIOS-PIEUP\n317E          ; mapped                 ; 1136          # 1.1  HANGUL LETTER SIOS-CIEUC\n317F          ; mapped                 ; 1140          # 1.1  HANGUL LETTER PANSIOS\n3180          ; mapped                 ; 1147          # 1.1  HANGUL LETTER SSANGIEUNG\n3181          ; mapped                 ; 114C          # 1.1  HANGUL LETTER YESIEUNG\n3182          ; mapped                 ; 11F1          # 1.1  HANGUL LETTER YESIEUNG-SIOS\n3183          ; mapped                 ; 11F2          # 1.1  HANGUL LETTER YESIEUNG-PANSIOS\n3184          ; mapped                 ; 1157          # 1.1  HANGUL LETTER KAPYEOUNPHIEUPH\n3185          ; mapped                 ; 1158          # 1.1  HANGUL LETTER SSANGHIEUH\n3186          ; mapped                 ; 1159          # 1.1  HANGUL LETTER YEORINHIEUH\n3187          ; mapped                 ; 1184          # 1.1  HANGUL LETTER YO-YA\n3188          ; mapped                 ; 1185          # 1.1  HANGUL LETTER YO-YAE\n3189          ; mapped                 ; 1188          # 1.1  HANGUL LETTER YO-I\n318A          ; mapped                 ; 1191          # 1.1  HANGUL LETTER YU-YEO\n318B          ; mapped                 ; 1192          # 1.1  HANGUL LETTER YU-YE\n318C          ; mapped                 ; 1194          # 1.1  HANGUL LETTER YU-I\n318D          ; mapped                 ; 119E          # 1.1  HANGUL LETTER ARAEA\n318E          ; mapped                 ; 11A1          # 1.1  HANGUL LETTER ARAEAE\n318F          ; disallowed                             # NA   <reserved-318F>\n3190..3191    ; valid                  ;      ; NV8    # 1.1  IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK\n3192          ; mapped                 ; 4E00          # 1.1  IDEOGRAPHIC ANNOTATION ONE MARK\n3193          ; mapped                 ; 4E8C          # 1.1  IDEOGRAPHIC ANNOTATION TWO MARK\n3194          ; mapped                 ; 4E09          # 1.1  IDEOGRAPHIC ANNOTATION THREE MARK\n3195          ; mapped                 ; 56DB          # 1.1  IDEOGRAPHIC ANNOTATION FOUR MARK\n3196          ; mapped                 ; 4E0A          # 1.1  IDEOGRAPHIC ANNOTATION TOP MARK\n3197          ; mapped                 ; 4E2D          # 1.1  IDEOGRAPHIC ANNOTATION MIDDLE MARK\n3198          ; mapped                 ; 4E0B          # 1.1  IDEOGRAPHIC ANNOTATION BOTTOM MARK\n3199          ; mapped                 ; 7532          # 1.1  IDEOGRAPHIC ANNOTATION FIRST MARK\n319A          ; mapped                 ; 4E59          # 1.1  IDEOGRAPHIC ANNOTATION SECOND MARK\n319B          ; mapped                 ; 4E19          # 1.1  IDEOGRAPHIC ANNOTATION THIRD MARK\n319C          ; mapped                 ; 4E01          # 1.1  IDEOGRAPHIC ANNOTATION FOURTH MARK\n319D          ; mapped                 ; 5929          # 1.1  IDEOGRAPHIC ANNOTATION HEAVEN MARK\n319E          ; mapped                 ; 5730          # 1.1  IDEOGRAPHIC ANNOTATION EARTH MARK\n319F          ; mapped                 ; 4EBA          # 1.1  IDEOGRAPHIC ANNOTATION MAN MARK\n31A0..31B7    ; valid                                  # 3.0  BOPOMOFO LETTER BU..BOPOMOFO FINAL LETTER H\n31B8..31BA    ; valid                                  # 6.0  BOPOMOFO LETTER GH..BOPOMOFO LETTER ZY\n31BB..31BF    ; valid                                  # 13.0 BOPOMOFO FINAL LETTER G..BOPOMOFO LETTER AH\n31C0..31CF    ; valid                  ;      ; NV8    # 4.1  CJK STROKE T..CJK STROKE N\n31D0..31E3    ; valid                  ;      ; NV8    # 5.1  CJK STROKE H..CJK STROKE Q\n31E4..31EE    ; disallowed                             # NA   <reserved-31E4>..<reserved-31EE>\n31EF          ; disallowed                             # 15.1 IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION\n31F0..31FF    ; valid                                  # 3.2  KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO\n3200          ; disallowed_STD3_mapped ; 0028 1100 0029 #1.1  PARENTHESIZED HANGUL KIYEOK\n3201          ; disallowed_STD3_mapped ; 0028 1102 0029 #1.1  PARENTHESIZED HANGUL NIEUN\n3202          ; disallowed_STD3_mapped ; 0028 1103 0029 #1.1  PARENTHESIZED HANGUL TIKEUT\n3203          ; disallowed_STD3_mapped ; 0028 1105 0029 #1.1  PARENTHESIZED HANGUL RIEUL\n3204          ; disallowed_STD3_mapped ; 0028 1106 0029 #1.1  PARENTHESIZED HANGUL MIEUM\n3205          ; disallowed_STD3_mapped ; 0028 1107 0029 #1.1  PARENTHESIZED HANGUL PIEUP\n3206          ; disallowed_STD3_mapped ; 0028 1109 0029 #1.1  PARENTHESIZED HANGUL SIOS\n3207          ; disallowed_STD3_mapped ; 0028 110B 0029 #1.1  PARENTHESIZED HANGUL IEUNG\n3208          ; disallowed_STD3_mapped ; 0028 110C 0029 #1.1  PARENTHESIZED HANGUL CIEUC\n3209          ; disallowed_STD3_mapped ; 0028 110E 0029 #1.1  PARENTHESIZED HANGUL CHIEUCH\n320A          ; disallowed_STD3_mapped ; 0028 110F 0029 #1.1  PARENTHESIZED HANGUL KHIEUKH\n320B          ; disallowed_STD3_mapped ; 0028 1110 0029 #1.1  PARENTHESIZED HANGUL THIEUTH\n320C          ; disallowed_STD3_mapped ; 0028 1111 0029 #1.1  PARENTHESIZED HANGUL PHIEUPH\n320D          ; disallowed_STD3_mapped ; 0028 1112 0029 #1.1  PARENTHESIZED HANGUL HIEUH\n320E          ; disallowed_STD3_mapped ; 0028 AC00 0029 #1.1  PARENTHESIZED HANGUL KIYEOK A\n320F          ; disallowed_STD3_mapped ; 0028 B098 0029 #1.1  PARENTHESIZED HANGUL NIEUN A\n3210          ; disallowed_STD3_mapped ; 0028 B2E4 0029 #1.1  PARENTHESIZED HANGUL TIKEUT A\n3211          ; disallowed_STD3_mapped ; 0028 B77C 0029 #1.1  PARENTHESIZED HANGUL RIEUL A\n3212          ; disallowed_STD3_mapped ; 0028 B9C8 0029 #1.1  PARENTHESIZED HANGUL MIEUM A\n3213          ; disallowed_STD3_mapped ; 0028 BC14 0029 #1.1  PARENTHESIZED HANGUL PIEUP A\n3214          ; disallowed_STD3_mapped ; 0028 C0AC 0029 #1.1  PARENTHESIZED HANGUL SIOS A\n3215          ; disallowed_STD3_mapped ; 0028 C544 0029 #1.1  PARENTHESIZED HANGUL IEUNG A\n3216          ; disallowed_STD3_mapped ; 0028 C790 0029 #1.1  PARENTHESIZED HANGUL CIEUC A\n3217          ; disallowed_STD3_mapped ; 0028 CC28 0029 #1.1  PARENTHESIZED HANGUL CHIEUCH A\n3218          ; disallowed_STD3_mapped ; 0028 CE74 0029 #1.1  PARENTHESIZED HANGUL KHIEUKH A\n3219          ; disallowed_STD3_mapped ; 0028 D0C0 0029 #1.1  PARENTHESIZED HANGUL THIEUTH A\n321A          ; disallowed_STD3_mapped ; 0028 D30C 0029 #1.1  PARENTHESIZED HANGUL PHIEUPH A\n321B          ; disallowed_STD3_mapped ; 0028 D558 0029 #1.1  PARENTHESIZED HANGUL HIEUH A\n321C          ; disallowed_STD3_mapped ; 0028 C8FC 0029 #1.1  PARENTHESIZED HANGUL CIEUC U\n321D          ; disallowed_STD3_mapped ; 0028 C624 C804 0029 #4.0 PARENTHESIZED KOREAN CHARACTER OJEON\n321E          ; disallowed_STD3_mapped ; 0028 C624 D6C4 0029 #4.0 PARENTHESIZED KOREAN CHARACTER O HU\n321F          ; disallowed                             # NA   <reserved-321F>\n3220          ; disallowed_STD3_mapped ; 0028 4E00 0029 #1.1  PARENTHESIZED IDEOGRAPH ONE\n3221          ; disallowed_STD3_mapped ; 0028 4E8C 0029 #1.1  PARENTHESIZED IDEOGRAPH TWO\n3222          ; disallowed_STD3_mapped ; 0028 4E09 0029 #1.1  PARENTHESIZED IDEOGRAPH THREE\n3223          ; disallowed_STD3_mapped ; 0028 56DB 0029 #1.1  PARENTHESIZED IDEOGRAPH FOUR\n3224          ; disallowed_STD3_mapped ; 0028 4E94 0029 #1.1  PARENTHESIZED IDEOGRAPH FIVE\n3225          ; disallowed_STD3_mapped ; 0028 516D 0029 #1.1  PARENTHESIZED IDEOGRAPH SIX\n3226          ; disallowed_STD3_mapped ; 0028 4E03 0029 #1.1  PARENTHESIZED IDEOGRAPH SEVEN\n3227          ; disallowed_STD3_mapped ; 0028 516B 0029 #1.1  PARENTHESIZED IDEOGRAPH EIGHT\n3228          ; disallowed_STD3_mapped ; 0028 4E5D 0029 #1.1  PARENTHESIZED IDEOGRAPH NINE\n3229          ; disallowed_STD3_mapped ; 0028 5341 0029 #1.1  PARENTHESIZED IDEOGRAPH TEN\n322A          ; disallowed_STD3_mapped ; 0028 6708 0029 #1.1  PARENTHESIZED IDEOGRAPH MOON\n322B          ; disallowed_STD3_mapped ; 0028 706B 0029 #1.1  PARENTHESIZED IDEOGRAPH FIRE\n322C          ; disallowed_STD3_mapped ; 0028 6C34 0029 #1.1  PARENTHESIZED IDEOGRAPH WATER\n322D          ; disallowed_STD3_mapped ; 0028 6728 0029 #1.1  PARENTHESIZED IDEOGRAPH WOOD\n322E          ; disallowed_STD3_mapped ; 0028 91D1 0029 #1.1  PARENTHESIZED IDEOGRAPH METAL\n322F          ; disallowed_STD3_mapped ; 0028 571F 0029 #1.1  PARENTHESIZED IDEOGRAPH EARTH\n3230          ; disallowed_STD3_mapped ; 0028 65E5 0029 #1.1  PARENTHESIZED IDEOGRAPH SUN\n3231          ; disallowed_STD3_mapped ; 0028 682A 0029 #1.1  PARENTHESIZED IDEOGRAPH STOCK\n3232          ; disallowed_STD3_mapped ; 0028 6709 0029 #1.1  PARENTHESIZED IDEOGRAPH HAVE\n3233          ; disallowed_STD3_mapped ; 0028 793E 0029 #1.1  PARENTHESIZED IDEOGRAPH SOCIETY\n3234          ; disallowed_STD3_mapped ; 0028 540D 0029 #1.1  PARENTHESIZED IDEOGRAPH NAME\n3235          ; disallowed_STD3_mapped ; 0028 7279 0029 #1.1  PARENTHESIZED IDEOGRAPH SPECIAL\n3236          ; disallowed_STD3_mapped ; 0028 8CA1 0029 #1.1  PARENTHESIZED IDEOGRAPH FINANCIAL\n3237          ; disallowed_STD3_mapped ; 0028 795D 0029 #1.1  PARENTHESIZED IDEOGRAPH CONGRATULATION\n3238          ; disallowed_STD3_mapped ; 0028 52B4 0029 #1.1  PARENTHESIZED IDEOGRAPH LABOR\n3239          ; disallowed_STD3_mapped ; 0028 4EE3 0029 #1.1  PARENTHESIZED IDEOGRAPH REPRESENT\n323A          ; disallowed_STD3_mapped ; 0028 547C 0029 #1.1  PARENTHESIZED IDEOGRAPH CALL\n323B          ; disallowed_STD3_mapped ; 0028 5B66 0029 #1.1  PARENTHESIZED IDEOGRAPH STUDY\n323C          ; disallowed_STD3_mapped ; 0028 76E3 0029 #1.1  PARENTHESIZED IDEOGRAPH SUPERVISE\n323D          ; disallowed_STD3_mapped ; 0028 4F01 0029 #1.1  PARENTHESIZED IDEOGRAPH ENTERPRISE\n323E          ; disallowed_STD3_mapped ; 0028 8CC7 0029 #1.1  PARENTHESIZED IDEOGRAPH RESOURCE\n323F          ; disallowed_STD3_mapped ; 0028 5354 0029 #1.1  PARENTHESIZED IDEOGRAPH ALLIANCE\n3240          ; disallowed_STD3_mapped ; 0028 796D 0029 #1.1  PARENTHESIZED IDEOGRAPH FESTIVAL\n3241          ; disallowed_STD3_mapped ; 0028 4F11 0029 #1.1  PARENTHESIZED IDEOGRAPH REST\n3242          ; disallowed_STD3_mapped ; 0028 81EA 0029 #1.1  PARENTHESIZED IDEOGRAPH SELF\n3243          ; disallowed_STD3_mapped ; 0028 81F3 0029 #1.1  PARENTHESIZED IDEOGRAPH REACH\n3244          ; mapped                 ; 554F          # 5.2  CIRCLED IDEOGRAPH QUESTION\n3245          ; mapped                 ; 5E7C          # 5.2  CIRCLED IDEOGRAPH KINDERGARTEN\n3246          ; mapped                 ; 6587          # 5.2  CIRCLED IDEOGRAPH SCHOOL\n3247          ; mapped                 ; 7B8F          # 5.2  CIRCLED IDEOGRAPH KOTO\n3248..324F    ; valid                  ;      ; NV8    # 5.2  CIRCLED NUMBER TEN ON BLACK SQUARE..CIRCLED NUMBER EIGHTY ON BLACK SQUARE\n3250          ; mapped                 ; 0070 0074 0065 #4.0  PARTNERSHIP SIGN\n3251          ; mapped                 ; 0032 0031     # 3.2  CIRCLED NUMBER TWENTY ONE\n3252          ; mapped                 ; 0032 0032     # 3.2  CIRCLED NUMBER TWENTY TWO\n3253          ; mapped                 ; 0032 0033     # 3.2  CIRCLED NUMBER TWENTY THREE\n3254          ; mapped                 ; 0032 0034     # 3.2  CIRCLED NUMBER TWENTY FOUR\n3255          ; mapped                 ; 0032 0035     # 3.2  CIRCLED NUMBER TWENTY FIVE\n3256          ; mapped                 ; 0032 0036     # 3.2  CIRCLED NUMBER TWENTY SIX\n3257          ; mapped                 ; 0032 0037     # 3.2  CIRCLED NUMBER TWENTY SEVEN\n3258          ; mapped                 ; 0032 0038     # 3.2  CIRCLED NUMBER TWENTY EIGHT\n3259          ; mapped                 ; 0032 0039     # 3.2  CIRCLED NUMBER TWENTY NINE\n325A          ; mapped                 ; 0033 0030     # 3.2  CIRCLED NUMBER THIRTY\n325B          ; mapped                 ; 0033 0031     # 3.2  CIRCLED NUMBER THIRTY ONE\n325C          ; mapped                 ; 0033 0032     # 3.2  CIRCLED NUMBER THIRTY TWO\n325D          ; mapped                 ; 0033 0033     # 3.2  CIRCLED NUMBER THIRTY THREE\n325E          ; mapped                 ; 0033 0034     # 3.2  CIRCLED NUMBER THIRTY FOUR\n325F          ; mapped                 ; 0033 0035     # 3.2  CIRCLED NUMBER THIRTY FIVE\n3260          ; mapped                 ; 1100          # 1.1  CIRCLED HANGUL KIYEOK\n3261          ; mapped                 ; 1102          # 1.1  CIRCLED HANGUL NIEUN\n3262          ; mapped                 ; 1103          # 1.1  CIRCLED HANGUL TIKEUT\n3263          ; mapped                 ; 1105          # 1.1  CIRCLED HANGUL RIEUL\n3264          ; mapped                 ; 1106          # 1.1  CIRCLED HANGUL MIEUM\n3265          ; mapped                 ; 1107          # 1.1  CIRCLED HANGUL PIEUP\n3266          ; mapped                 ; 1109          # 1.1  CIRCLED HANGUL SIOS\n3267          ; mapped                 ; 110B          # 1.1  CIRCLED HANGUL IEUNG\n3268          ; mapped                 ; 110C          # 1.1  CIRCLED HANGUL CIEUC\n3269          ; mapped                 ; 110E          # 1.1  CIRCLED HANGUL CHIEUCH\n326A          ; mapped                 ; 110F          # 1.1  CIRCLED HANGUL KHIEUKH\n326B          ; mapped                 ; 1110          # 1.1  CIRCLED HANGUL THIEUTH\n326C          ; mapped                 ; 1111          # 1.1  CIRCLED HANGUL PHIEUPH\n326D          ; mapped                 ; 1112          # 1.1  CIRCLED HANGUL HIEUH\n326E          ; mapped                 ; AC00          # 1.1  CIRCLED HANGUL KIYEOK A\n326F          ; mapped                 ; B098          # 1.1  CIRCLED HANGUL NIEUN A\n3270          ; mapped                 ; B2E4          # 1.1  CIRCLED HANGUL TIKEUT A\n3271          ; mapped                 ; B77C          # 1.1  CIRCLED HANGUL RIEUL A\n3272          ; mapped                 ; B9C8          # 1.1  CIRCLED HANGUL MIEUM A\n3273          ; mapped                 ; BC14          # 1.1  CIRCLED HANGUL PIEUP A\n3274          ; mapped                 ; C0AC          # 1.1  CIRCLED HANGUL SIOS A\n3275          ; mapped                 ; C544          # 1.1  CIRCLED HANGUL IEUNG A\n3276          ; mapped                 ; C790          # 1.1  CIRCLED HANGUL CIEUC A\n3277          ; mapped                 ; CC28          # 1.1  CIRCLED HANGUL CHIEUCH A\n3278          ; mapped                 ; CE74          # 1.1  CIRCLED HANGUL KHIEUKH A\n3279          ; mapped                 ; D0C0          # 1.1  CIRCLED HANGUL THIEUTH A\n327A          ; mapped                 ; D30C          # 1.1  CIRCLED HANGUL PHIEUPH A\n327B          ; mapped                 ; D558          # 1.1  CIRCLED HANGUL HIEUH A\n327C          ; mapped                 ; CC38 ACE0     # 4.0  CIRCLED KOREAN CHARACTER CHAMKO\n327D          ; mapped                 ; C8FC C758     # 4.0  CIRCLED KOREAN CHARACTER JUEUI\n327E          ; mapped                 ; C6B0          # 4.1  CIRCLED HANGUL IEUNG U\n327F          ; valid                  ;      ; NV8    # 1.1  KOREAN STANDARD SYMBOL\n3280          ; mapped                 ; 4E00          # 1.1  CIRCLED IDEOGRAPH ONE\n3281          ; mapped                 ; 4E8C          # 1.1  CIRCLED IDEOGRAPH TWO\n3282          ; mapped                 ; 4E09          # 1.1  CIRCLED IDEOGRAPH THREE\n3283          ; mapped                 ; 56DB          # 1.1  CIRCLED IDEOGRAPH FOUR\n3284          ; mapped                 ; 4E94          # 1.1  CIRCLED IDEOGRAPH FIVE\n3285          ; mapped                 ; 516D          # 1.1  CIRCLED IDEOGRAPH SIX\n3286          ; mapped                 ; 4E03          # 1.1  CIRCLED IDEOGRAPH SEVEN\n3287          ; mapped                 ; 516B          # 1.1  CIRCLED IDEOGRAPH EIGHT\n3288          ; mapped                 ; 4E5D          # 1.1  CIRCLED IDEOGRAPH NINE\n3289          ; mapped                 ; 5341          # 1.1  CIRCLED IDEOGRAPH TEN\n328A          ; mapped                 ; 6708          # 1.1  CIRCLED IDEOGRAPH MOON\n328B          ; mapped                 ; 706B          # 1.1  CIRCLED IDEOGRAPH FIRE\n328C          ; mapped                 ; 6C34          # 1.1  CIRCLED IDEOGRAPH WATER\n328D          ; mapped                 ; 6728          # 1.1  CIRCLED IDEOGRAPH WOOD\n328E          ; mapped                 ; 91D1          # 1.1  CIRCLED IDEOGRAPH METAL\n328F          ; mapped                 ; 571F          # 1.1  CIRCLED IDEOGRAPH EARTH\n3290          ; mapped                 ; 65E5          # 1.1  CIRCLED IDEOGRAPH SUN\n3291          ; mapped                 ; 682A          # 1.1  CIRCLED IDEOGRAPH STOCK\n3292          ; mapped                 ; 6709          # 1.1  CIRCLED IDEOGRAPH HAVE\n3293          ; mapped                 ; 793E          # 1.1  CIRCLED IDEOGRAPH SOCIETY\n3294          ; mapped                 ; 540D          # 1.1  CIRCLED IDEOGRAPH NAME\n3295          ; mapped                 ; 7279          # 1.1  CIRCLED IDEOGRAPH SPECIAL\n3296          ; mapped                 ; 8CA1          # 1.1  CIRCLED IDEOGRAPH FINANCIAL\n3297          ; mapped                 ; 795D          # 1.1  CIRCLED IDEOGRAPH CONGRATULATION\n3298          ; mapped                 ; 52B4          # 1.1  CIRCLED IDEOGRAPH LABOR\n3299          ; mapped                 ; 79D8          # 1.1  CIRCLED IDEOGRAPH SECRET\n329A          ; mapped                 ; 7537          # 1.1  CIRCLED IDEOGRAPH MALE\n329B          ; mapped                 ; 5973          # 1.1  CIRCLED IDEOGRAPH FEMALE\n329C          ; mapped                 ; 9069          # 1.1  CIRCLED IDEOGRAPH SUITABLE\n329D          ; mapped                 ; 512A          # 1.1  CIRCLED IDEOGRAPH EXCELLENT\n329E          ; mapped                 ; 5370          # 1.1  CIRCLED IDEOGRAPH PRINT\n329F          ; mapped                 ; 6CE8          # 1.1  CIRCLED IDEOGRAPH ATTENTION\n32A0          ; mapped                 ; 9805          # 1.1  CIRCLED IDEOGRAPH ITEM\n32A1          ; mapped                 ; 4F11          # 1.1  CIRCLED IDEOGRAPH REST\n32A2          ; mapped                 ; 5199          # 1.1  CIRCLED IDEOGRAPH COPY\n32A3          ; mapped                 ; 6B63          # 1.1  CIRCLED IDEOGRAPH CORRECT\n32A4          ; mapped                 ; 4E0A          # 1.1  CIRCLED IDEOGRAPH HIGH\n32A5          ; mapped                 ; 4E2D          # 1.1  CIRCLED IDEOGRAPH CENTRE\n32A6          ; mapped                 ; 4E0B          # 1.1  CIRCLED IDEOGRAPH LOW\n32A7          ; mapped                 ; 5DE6          # 1.1  CIRCLED IDEOGRAPH LEFT\n32A8          ; mapped                 ; 53F3          # 1.1  CIRCLED IDEOGRAPH RIGHT\n32A9          ; mapped                 ; 533B          # 1.1  CIRCLED IDEOGRAPH MEDICINE\n32AA          ; mapped                 ; 5B97          # 1.1  CIRCLED IDEOGRAPH RELIGION\n32AB          ; mapped                 ; 5B66          # 1.1  CIRCLED IDEOGRAPH STUDY\n32AC          ; mapped                 ; 76E3          # 1.1  CIRCLED IDEOGRAPH SUPERVISE\n32AD          ; mapped                 ; 4F01          # 1.1  CIRCLED IDEOGRAPH ENTERPRISE\n32AE          ; mapped                 ; 8CC7          # 1.1  CIRCLED IDEOGRAPH RESOURCE\n32AF          ; mapped                 ; 5354          # 1.1  CIRCLED IDEOGRAPH ALLIANCE\n32B0          ; mapped                 ; 591C          # 1.1  CIRCLED IDEOGRAPH NIGHT\n32B1          ; mapped                 ; 0033 0036     # 3.2  CIRCLED NUMBER THIRTY SIX\n32B2          ; mapped                 ; 0033 0037     # 3.2  CIRCLED NUMBER THIRTY SEVEN\n32B3          ; mapped                 ; 0033 0038     # 3.2  CIRCLED NUMBER THIRTY EIGHT\n32B4          ; mapped                 ; 0033 0039     # 3.2  CIRCLED NUMBER THIRTY NINE\n32B5          ; mapped                 ; 0034 0030     # 3.2  CIRCLED NUMBER FORTY\n32B6          ; mapped                 ; 0034 0031     # 3.2  CIRCLED NUMBER FORTY ONE\n32B7          ; mapped                 ; 0034 0032     # 3.2  CIRCLED NUMBER FORTY TWO\n32B8          ; mapped                 ; 0034 0033     # 3.2  CIRCLED NUMBER FORTY THREE\n32B9          ; mapped                 ; 0034 0034     # 3.2  CIRCLED NUMBER FORTY FOUR\n32BA          ; mapped                 ; 0034 0035     # 3.2  CIRCLED NUMBER FORTY FIVE\n32BB          ; mapped                 ; 0034 0036     # 3.2  CIRCLED NUMBER FORTY SIX\n32BC          ; mapped                 ; 0034 0037     # 3.2  CIRCLED NUMBER FORTY SEVEN\n32BD          ; mapped                 ; 0034 0038     # 3.2  CIRCLED NUMBER FORTY EIGHT\n32BE          ; mapped                 ; 0034 0039     # 3.2  CIRCLED NUMBER FORTY NINE\n32BF          ; mapped                 ; 0035 0030     # 3.2  CIRCLED NUMBER FIFTY\n32C0          ; mapped                 ; 0031 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY\n32C1          ; mapped                 ; 0032 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY\n32C2          ; mapped                 ; 0033 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH\n32C3          ; mapped                 ; 0034 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL\n32C4          ; mapped                 ; 0035 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY\n32C5          ; mapped                 ; 0036 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE\n32C6          ; mapped                 ; 0037 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY\n32C7          ; mapped                 ; 0038 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST\n32C8          ; mapped                 ; 0039 6708     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER\n32C9          ; mapped                 ; 0031 0030 6708 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER\n32CA          ; mapped                 ; 0031 0031 6708 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER\n32CB          ; mapped                 ; 0031 0032 6708 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER\n32CC          ; mapped                 ; 0068 0067     # 4.0  SQUARE HG\n32CD          ; mapped                 ; 0065 0072 0067 #4.0  SQUARE ERG\n32CE          ; mapped                 ; 0065 0076     # 4.0  SQUARE EV\n32CF          ; mapped                 ; 006C 0074 0064 #4.0  LIMITED LIABILITY SIGN\n32D0          ; mapped                 ; 30A2          # 1.1  CIRCLED KATAKANA A\n32D1          ; mapped                 ; 30A4          # 1.1  CIRCLED KATAKANA I\n32D2          ; mapped                 ; 30A6          # 1.1  CIRCLED KATAKANA U\n32D3          ; mapped                 ; 30A8          # 1.1  CIRCLED KATAKANA E\n32D4          ; mapped                 ; 30AA          # 1.1  CIRCLED KATAKANA O\n32D5          ; mapped                 ; 30AB          # 1.1  CIRCLED KATAKANA KA\n32D6          ; mapped                 ; 30AD          # 1.1  CIRCLED KATAKANA KI\n32D7          ; mapped                 ; 30AF          # 1.1  CIRCLED KATAKANA KU\n32D8          ; mapped                 ; 30B1          # 1.1  CIRCLED KATAKANA KE\n32D9          ; mapped                 ; 30B3          # 1.1  CIRCLED KATAKANA KO\n32DA          ; mapped                 ; 30B5          # 1.1  CIRCLED KATAKANA SA\n32DB          ; mapped                 ; 30B7          # 1.1  CIRCLED KATAKANA SI\n32DC          ; mapped                 ; 30B9          # 1.1  CIRCLED KATAKANA SU\n32DD          ; mapped                 ; 30BB          # 1.1  CIRCLED KATAKANA SE\n32DE          ; mapped                 ; 30BD          # 1.1  CIRCLED KATAKANA SO\n32DF          ; mapped                 ; 30BF          # 1.1  CIRCLED KATAKANA TA\n32E0          ; mapped                 ; 30C1          # 1.1  CIRCLED KATAKANA TI\n32E1          ; mapped                 ; 30C4          # 1.1  CIRCLED KATAKANA TU\n32E2          ; mapped                 ; 30C6          # 1.1  CIRCLED KATAKANA TE\n32E3          ; mapped                 ; 30C8          # 1.1  CIRCLED KATAKANA TO\n32E4          ; mapped                 ; 30CA          # 1.1  CIRCLED KATAKANA NA\n32E5          ; mapped                 ; 30CB          # 1.1  CIRCLED KATAKANA NI\n32E6          ; mapped                 ; 30CC          # 1.1  CIRCLED KATAKANA NU\n32E7          ; mapped                 ; 30CD          # 1.1  CIRCLED KATAKANA NE\n32E8          ; mapped                 ; 30CE          # 1.1  CIRCLED KATAKANA NO\n32E9          ; mapped                 ; 30CF          # 1.1  CIRCLED KATAKANA HA\n32EA          ; mapped                 ; 30D2          # 1.1  CIRCLED KATAKANA HI\n32EB          ; mapped                 ; 30D5          # 1.1  CIRCLED KATAKANA HU\n32EC          ; mapped                 ; 30D8          # 1.1  CIRCLED KATAKANA HE\n32ED          ; mapped                 ; 30DB          # 1.1  CIRCLED KATAKANA HO\n32EE          ; mapped                 ; 30DE          # 1.1  CIRCLED KATAKANA MA\n32EF          ; mapped                 ; 30DF          # 1.1  CIRCLED KATAKANA MI\n32F0          ; mapped                 ; 30E0          # 1.1  CIRCLED KATAKANA MU\n32F1          ; mapped                 ; 30E1          # 1.1  CIRCLED KATAKANA ME\n32F2          ; mapped                 ; 30E2          # 1.1  CIRCLED KATAKANA MO\n32F3          ; mapped                 ; 30E4          # 1.1  CIRCLED KATAKANA YA\n32F4          ; mapped                 ; 30E6          # 1.1  CIRCLED KATAKANA YU\n32F5          ; mapped                 ; 30E8          # 1.1  CIRCLED KATAKANA YO\n32F6          ; mapped                 ; 30E9          # 1.1  CIRCLED KATAKANA RA\n32F7          ; mapped                 ; 30EA          # 1.1  CIRCLED KATAKANA RI\n32F8          ; mapped                 ; 30EB          # 1.1  CIRCLED KATAKANA RU\n32F9          ; mapped                 ; 30EC          # 1.1  CIRCLED KATAKANA RE\n32FA          ; mapped                 ; 30ED          # 1.1  CIRCLED KATAKANA RO\n32FB          ; mapped                 ; 30EF          # 1.1  CIRCLED KATAKANA WA\n32FC          ; mapped                 ; 30F0          # 1.1  CIRCLED KATAKANA WI\n32FD          ; mapped                 ; 30F1          # 1.1  CIRCLED KATAKANA WE\n32FE          ; mapped                 ; 30F2          # 1.1  CIRCLED KATAKANA WO\n32FF          ; mapped                 ; 4EE4 548C     # 12.1 SQUARE ERA NAME REIWA\n3300          ; mapped                 ; 30A2 30D1 30FC 30C8 #1.1 SQUARE APAATO\n3301          ; mapped                 ; 30A2 30EB 30D5 30A1 #1.1 SQUARE ARUHUA\n3302          ; mapped                 ; 30A2 30F3 30DA 30A2 #1.1 SQUARE ANPEA\n3303          ; mapped                 ; 30A2 30FC 30EB #1.1  SQUARE AARU\n3304          ; mapped                 ; 30A4 30CB 30F3 30B0 #1.1 SQUARE ININGU\n3305          ; mapped                 ; 30A4 30F3 30C1 #1.1  SQUARE INTI\n3306          ; mapped                 ; 30A6 30A9 30F3 #1.1  SQUARE UON\n3307          ; mapped                 ; 30A8 30B9 30AF 30FC 30C9 #1.1 SQUARE ESUKUUDO\n3308          ; mapped                 ; 30A8 30FC 30AB 30FC #1.1 SQUARE EEKAA\n3309          ; mapped                 ; 30AA 30F3 30B9 #1.1  SQUARE ONSU\n330A          ; mapped                 ; 30AA 30FC 30E0 #1.1  SQUARE OOMU\n330B          ; mapped                 ; 30AB 30A4 30EA #1.1  SQUARE KAIRI\n330C          ; mapped                 ; 30AB 30E9 30C3 30C8 #1.1 SQUARE KARATTO\n330D          ; mapped                 ; 30AB 30ED 30EA 30FC #1.1 SQUARE KARORII\n330E          ; mapped                 ; 30AC 30ED 30F3 #1.1  SQUARE GARON\n330F          ; mapped                 ; 30AC 30F3 30DE #1.1  SQUARE GANMA\n3310          ; mapped                 ; 30AE 30AC     # 1.1  SQUARE GIGA\n3311          ; mapped                 ; 30AE 30CB 30FC #1.1  SQUARE GINII\n3312          ; mapped                 ; 30AD 30E5 30EA 30FC #1.1 SQUARE KYURII\n3313          ; mapped                 ; 30AE 30EB 30C0 30FC #1.1 SQUARE GIRUDAA\n3314          ; mapped                 ; 30AD 30ED     # 1.1  SQUARE KIRO\n3315          ; mapped                 ; 30AD 30ED 30B0 30E9 30E0 #1.1 SQUARE KIROGURAMU\n3316          ; mapped                 ; 30AD 30ED 30E1 30FC 30C8 30EB #1.1 SQUARE KIROMEETORU\n3317          ; mapped                 ; 30AD 30ED 30EF 30C3 30C8 #1.1 SQUARE KIROWATTO\n3318          ; mapped                 ; 30B0 30E9 30E0 #1.1  SQUARE GURAMU\n3319          ; mapped                 ; 30B0 30E9 30E0 30C8 30F3 #1.1 SQUARE GURAMUTON\n331A          ; mapped                 ; 30AF 30EB 30BC 30A4 30ED #1.1 SQUARE KURUZEIRO\n331B          ; mapped                 ; 30AF 30ED 30FC 30CD #1.1 SQUARE KUROONE\n331C          ; mapped                 ; 30B1 30FC 30B9 #1.1  SQUARE KEESU\n331D          ; mapped                 ; 30B3 30EB 30CA #1.1  SQUARE KORUNA\n331E          ; mapped                 ; 30B3 30FC 30DD #1.1  SQUARE KOOPO\n331F          ; mapped                 ; 30B5 30A4 30AF 30EB #1.1 SQUARE SAIKURU\n3320          ; mapped                 ; 30B5 30F3 30C1 30FC 30E0 #1.1 SQUARE SANTIIMU\n3321          ; mapped                 ; 30B7 30EA 30F3 30B0 #1.1 SQUARE SIRINGU\n3322          ; mapped                 ; 30BB 30F3 30C1 #1.1  SQUARE SENTI\n3323          ; mapped                 ; 30BB 30F3 30C8 #1.1  SQUARE SENTO\n3324          ; mapped                 ; 30C0 30FC 30B9 #1.1  SQUARE DAASU\n3325          ; mapped                 ; 30C7 30B7     # 1.1  SQUARE DESI\n3326          ; mapped                 ; 30C9 30EB     # 1.1  SQUARE DORU\n3327          ; mapped                 ; 30C8 30F3     # 1.1  SQUARE TON\n3328          ; mapped                 ; 30CA 30CE     # 1.1  SQUARE NANO\n3329          ; mapped                 ; 30CE 30C3 30C8 #1.1  SQUARE NOTTO\n332A          ; mapped                 ; 30CF 30A4 30C4 #1.1  SQUARE HAITU\n332B          ; mapped                 ; 30D1 30FC 30BB 30F3 30C8 #1.1 SQUARE PAASENTO\n332C          ; mapped                 ; 30D1 30FC 30C4 #1.1  SQUARE PAATU\n332D          ; mapped                 ; 30D0 30FC 30EC 30EB #1.1 SQUARE BAARERU\n332E          ; mapped                 ; 30D4 30A2 30B9 30C8 30EB #1.1 SQUARE PIASUTORU\n332F          ; mapped                 ; 30D4 30AF 30EB #1.1  SQUARE PIKURU\n3330          ; mapped                 ; 30D4 30B3     # 1.1  SQUARE PIKO\n3331          ; mapped                 ; 30D3 30EB     # 1.1  SQUARE BIRU\n3332          ; mapped                 ; 30D5 30A1 30E9 30C3 30C9 #1.1 SQUARE HUARADDO\n3333          ; mapped                 ; 30D5 30A3 30FC 30C8 #1.1 SQUARE HUIITO\n3334          ; mapped                 ; 30D6 30C3 30B7 30A7 30EB #1.1 SQUARE BUSSYERU\n3335          ; mapped                 ; 30D5 30E9 30F3 #1.1  SQUARE HURAN\n3336          ; mapped                 ; 30D8 30AF 30BF 30FC 30EB #1.1 SQUARE HEKUTAARU\n3337          ; mapped                 ; 30DA 30BD     # 1.1  SQUARE PESO\n3338          ; mapped                 ; 30DA 30CB 30D2 #1.1  SQUARE PENIHI\n3339          ; mapped                 ; 30D8 30EB 30C4 #1.1  SQUARE HERUTU\n333A          ; mapped                 ; 30DA 30F3 30B9 #1.1  SQUARE PENSU\n333B          ; mapped                 ; 30DA 30FC 30B8 #1.1  SQUARE PEEZI\n333C          ; mapped                 ; 30D9 30FC 30BF #1.1  SQUARE BEETA\n333D          ; mapped                 ; 30DD 30A4 30F3 30C8 #1.1 SQUARE POINTO\n333E          ; mapped                 ; 30DC 30EB 30C8 #1.1  SQUARE BORUTO\n333F          ; mapped                 ; 30DB 30F3     # 1.1  SQUARE HON\n3340          ; mapped                 ; 30DD 30F3 30C9 #1.1  SQUARE PONDO\n3341          ; mapped                 ; 30DB 30FC 30EB #1.1  SQUARE HOORU\n3342          ; mapped                 ; 30DB 30FC 30F3 #1.1  SQUARE HOON\n3343          ; mapped                 ; 30DE 30A4 30AF 30ED #1.1 SQUARE MAIKURO\n3344          ; mapped                 ; 30DE 30A4 30EB #1.1  SQUARE MAIRU\n3345          ; mapped                 ; 30DE 30C3 30CF #1.1  SQUARE MAHHA\n3346          ; mapped                 ; 30DE 30EB 30AF #1.1  SQUARE MARUKU\n3347          ; mapped                 ; 30DE 30F3 30B7 30E7 30F3 #1.1 SQUARE MANSYON\n3348          ; mapped                 ; 30DF 30AF 30ED 30F3 #1.1 SQUARE MIKURON\n3349          ; mapped                 ; 30DF 30EA     # 1.1  SQUARE MIRI\n334A          ; mapped                 ; 30DF 30EA 30D0 30FC 30EB #1.1 SQUARE MIRIBAARU\n334B          ; mapped                 ; 30E1 30AC     # 1.1  SQUARE MEGA\n334C          ; mapped                 ; 30E1 30AC 30C8 30F3 #1.1 SQUARE MEGATON\n334D          ; mapped                 ; 30E1 30FC 30C8 30EB #1.1 SQUARE MEETORU\n334E          ; mapped                 ; 30E4 30FC 30C9 #1.1  SQUARE YAADO\n334F          ; mapped                 ; 30E4 30FC 30EB #1.1  SQUARE YAARU\n3350          ; mapped                 ; 30E6 30A2 30F3 #1.1  SQUARE YUAN\n3351          ; mapped                 ; 30EA 30C3 30C8 30EB #1.1 SQUARE RITTORU\n3352          ; mapped                 ; 30EA 30E9     # 1.1  SQUARE RIRA\n3353          ; mapped                 ; 30EB 30D4 30FC #1.1  SQUARE RUPII\n3354          ; mapped                 ; 30EB 30FC 30D6 30EB #1.1 SQUARE RUUBURU\n3355          ; mapped                 ; 30EC 30E0     # 1.1  SQUARE REMU\n3356          ; mapped                 ; 30EC 30F3 30C8 30B2 30F3 #1.1 SQUARE RENTOGEN\n3357          ; mapped                 ; 30EF 30C3 30C8 #1.1  SQUARE WATTO\n3358          ; mapped                 ; 0030 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO\n3359          ; mapped                 ; 0031 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE\n335A          ; mapped                 ; 0032 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO\n335B          ; mapped                 ; 0033 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE\n335C          ; mapped                 ; 0034 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR\n335D          ; mapped                 ; 0035 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE\n335E          ; mapped                 ; 0036 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX\n335F          ; mapped                 ; 0037 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN\n3360          ; mapped                 ; 0038 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT\n3361          ; mapped                 ; 0039 70B9     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE\n3362          ; mapped                 ; 0031 0030 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN\n3363          ; mapped                 ; 0031 0031 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN\n3364          ; mapped                 ; 0031 0032 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE\n3365          ; mapped                 ; 0031 0033 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN\n3366          ; mapped                 ; 0031 0034 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN\n3367          ; mapped                 ; 0031 0035 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN\n3368          ; mapped                 ; 0031 0036 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN\n3369          ; mapped                 ; 0031 0037 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN\n336A          ; mapped                 ; 0031 0038 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN\n336B          ; mapped                 ; 0031 0039 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN\n336C          ; mapped                 ; 0032 0030 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY\n336D          ; mapped                 ; 0032 0031 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE\n336E          ; mapped                 ; 0032 0032 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO\n336F          ; mapped                 ; 0032 0033 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE\n3370          ; mapped                 ; 0032 0034 70B9 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR\n3371          ; mapped                 ; 0068 0070 0061 #1.1  SQUARE HPA\n3372          ; mapped                 ; 0064 0061     # 1.1  SQUARE DA\n3373          ; mapped                 ; 0061 0075     # 1.1  SQUARE AU\n3374          ; mapped                 ; 0062 0061 0072 #1.1  SQUARE BAR\n3375          ; mapped                 ; 006F 0076     # 1.1  SQUARE OV\n3376          ; mapped                 ; 0070 0063     # 1.1  SQUARE PC\n3377          ; mapped                 ; 0064 006D     # 4.0  SQUARE DM\n3378          ; mapped                 ; 0064 006D 0032 #4.0  SQUARE DM SQUARED\n3379          ; mapped                 ; 0064 006D 0033 #4.0  SQUARE DM CUBED\n337A          ; mapped                 ; 0069 0075     # 4.0  SQUARE IU\n337B          ; mapped                 ; 5E73 6210     # 1.1  SQUARE ERA NAME HEISEI\n337C          ; mapped                 ; 662D 548C     # 1.1  SQUARE ERA NAME SYOUWA\n337D          ; mapped                 ; 5927 6B63     # 1.1  SQUARE ERA NAME TAISYOU\n337E          ; mapped                 ; 660E 6CBB     # 1.1  SQUARE ERA NAME MEIZI\n337F          ; mapped                 ; 682A 5F0F 4F1A 793E #1.1 SQUARE CORPORATION\n3380          ; mapped                 ; 0070 0061     # 1.1  SQUARE PA AMPS\n3381          ; mapped                 ; 006E 0061     # 1.1  SQUARE NA\n3382          ; mapped                 ; 03BC 0061     # 1.1  SQUARE MU A\n3383          ; mapped                 ; 006D 0061     # 1.1  SQUARE MA\n3384          ; mapped                 ; 006B 0061     # 1.1  SQUARE KA\n3385          ; mapped                 ; 006B 0062     # 1.1  SQUARE KB\n3386          ; mapped                 ; 006D 0062     # 1.1  SQUARE MB\n3387          ; mapped                 ; 0067 0062     # 1.1  SQUARE GB\n3388          ; mapped                 ; 0063 0061 006C #1.1  SQUARE CAL\n3389          ; mapped                 ; 006B 0063 0061 006C #1.1 SQUARE KCAL\n338A          ; mapped                 ; 0070 0066     # 1.1  SQUARE PF\n338B          ; mapped                 ; 006E 0066     # 1.1  SQUARE NF\n338C          ; mapped                 ; 03BC 0066     # 1.1  SQUARE MU F\n338D          ; mapped                 ; 03BC 0067     # 1.1  SQUARE MU G\n338E          ; mapped                 ; 006D 0067     # 1.1  SQUARE MG\n338F          ; mapped                 ; 006B 0067     # 1.1  SQUARE KG\n3390          ; mapped                 ; 0068 007A     # 1.1  SQUARE HZ\n3391          ; mapped                 ; 006B 0068 007A #1.1  SQUARE KHZ\n3392          ; mapped                 ; 006D 0068 007A #1.1  SQUARE MHZ\n3393          ; mapped                 ; 0067 0068 007A #1.1  SQUARE GHZ\n3394          ; mapped                 ; 0074 0068 007A #1.1  SQUARE THZ\n3395          ; mapped                 ; 03BC 006C     # 1.1  SQUARE MU L\n3396          ; mapped                 ; 006D 006C     # 1.1  SQUARE ML\n3397          ; mapped                 ; 0064 006C     # 1.1  SQUARE DL\n3398          ; mapped                 ; 006B 006C     # 1.1  SQUARE KL\n3399          ; mapped                 ; 0066 006D     # 1.1  SQUARE FM\n339A          ; mapped                 ; 006E 006D     # 1.1  SQUARE NM\n339B          ; mapped                 ; 03BC 006D     # 1.1  SQUARE MU M\n339C          ; mapped                 ; 006D 006D     # 1.1  SQUARE MM\n339D          ; mapped                 ; 0063 006D     # 1.1  SQUARE CM\n339E          ; mapped                 ; 006B 006D     # 1.1  SQUARE KM\n339F          ; mapped                 ; 006D 006D 0032 #1.1  SQUARE MM SQUARED\n33A0          ; mapped                 ; 0063 006D 0032 #1.1  SQUARE CM SQUARED\n33A1          ; mapped                 ; 006D 0032     # 1.1  SQUARE M SQUARED\n33A2          ; mapped                 ; 006B 006D 0032 #1.1  SQUARE KM SQUARED\n33A3          ; mapped                 ; 006D 006D 0033 #1.1  SQUARE MM CUBED\n33A4          ; mapped                 ; 0063 006D 0033 #1.1  SQUARE CM CUBED\n33A5          ; mapped                 ; 006D 0033     # 1.1  SQUARE M CUBED\n33A6          ; mapped                 ; 006B 006D 0033 #1.1  SQUARE KM CUBED\n33A7          ; mapped                 ; 006D 2215 0073 #1.1  SQUARE M OVER S\n33A8          ; mapped                 ; 006D 2215 0073 0032 #1.1 SQUARE M OVER S SQUARED\n33A9          ; mapped                 ; 0070 0061     # 1.1  SQUARE PA\n33AA          ; mapped                 ; 006B 0070 0061 #1.1  SQUARE KPA\n33AB          ; mapped                 ; 006D 0070 0061 #1.1  SQUARE MPA\n33AC          ; mapped                 ; 0067 0070 0061 #1.1  SQUARE GPA\n33AD          ; mapped                 ; 0072 0061 0064 #1.1  SQUARE RAD\n33AE          ; mapped                 ; 0072 0061 0064 2215 0073 #1.1 SQUARE RAD OVER S\n33AF          ; mapped                 ; 0072 0061 0064 2215 0073 0032 #1.1 SQUARE RAD OVER S SQUARED\n33B0          ; mapped                 ; 0070 0073     # 1.1  SQUARE PS\n33B1          ; mapped                 ; 006E 0073     # 1.1  SQUARE NS\n33B2          ; mapped                 ; 03BC 0073     # 1.1  SQUARE MU S\n33B3          ; mapped                 ; 006D 0073     # 1.1  SQUARE MS\n33B4          ; mapped                 ; 0070 0076     # 1.1  SQUARE PV\n33B5          ; mapped                 ; 006E 0076     # 1.1  SQUARE NV\n33B6          ; mapped                 ; 03BC 0076     # 1.1  SQUARE MU V\n33B7          ; mapped                 ; 006D 0076     # 1.1  SQUARE MV\n33B8          ; mapped                 ; 006B 0076     # 1.1  SQUARE KV\n33B9          ; mapped                 ; 006D 0076     # 1.1  SQUARE MV MEGA\n33BA          ; mapped                 ; 0070 0077     # 1.1  SQUARE PW\n33BB          ; mapped                 ; 006E 0077     # 1.1  SQUARE NW\n33BC          ; mapped                 ; 03BC 0077     # 1.1  SQUARE MU W\n33BD          ; mapped                 ; 006D 0077     # 1.1  SQUARE MW\n33BE          ; mapped                 ; 006B 0077     # 1.1  SQUARE KW\n33BF          ; mapped                 ; 006D 0077     # 1.1  SQUARE MW MEGA\n33C0          ; mapped                 ; 006B 03C9     # 1.1  SQUARE K OHM\n33C1          ; mapped                 ; 006D 03C9     # 1.1  SQUARE M OHM\n33C2          ; disallowed                             # 1.1  SQUARE AM\n33C3          ; mapped                 ; 0062 0071     # 1.1  SQUARE BQ\n33C4          ; mapped                 ; 0063 0063     # 1.1  SQUARE CC\n33C5          ; mapped                 ; 0063 0064     # 1.1  SQUARE CD\n33C6          ; mapped                 ; 0063 2215 006B 0067 #1.1 SQUARE C OVER KG\n33C7          ; disallowed                             # 1.1  SQUARE CO\n33C8          ; mapped                 ; 0064 0062     # 1.1  SQUARE DB\n33C9          ; mapped                 ; 0067 0079     # 1.1  SQUARE GY\n33CA          ; mapped                 ; 0068 0061     # 1.1  SQUARE HA\n33CB          ; mapped                 ; 0068 0070     # 1.1  SQUARE HP\n33CC          ; mapped                 ; 0069 006E     # 1.1  SQUARE IN\n33CD          ; mapped                 ; 006B 006B     # 1.1  SQUARE KK\n33CE          ; mapped                 ; 006B 006D     # 1.1  SQUARE KM CAPITAL\n33CF          ; mapped                 ; 006B 0074     # 1.1  SQUARE KT\n33D0          ; mapped                 ; 006C 006D     # 1.1  SQUARE LM\n33D1          ; mapped                 ; 006C 006E     # 1.1  SQUARE LN\n33D2          ; mapped                 ; 006C 006F 0067 #1.1  SQUARE LOG\n33D3          ; mapped                 ; 006C 0078     # 1.1  SQUARE LX\n33D4          ; mapped                 ; 006D 0062     # 1.1  SQUARE MB SMALL\n33D5          ; mapped                 ; 006D 0069 006C #1.1  SQUARE MIL\n33D6          ; mapped                 ; 006D 006F 006C #1.1  SQUARE MOL\n33D7          ; mapped                 ; 0070 0068     # 1.1  SQUARE PH\n33D8          ; disallowed                             # 1.1  SQUARE PM\n33D9          ; mapped                 ; 0070 0070 006D #1.1  SQUARE PPM\n33DA          ; mapped                 ; 0070 0072     # 1.1  SQUARE PR\n33DB          ; mapped                 ; 0073 0072     # 1.1  SQUARE SR\n33DC          ; mapped                 ; 0073 0076     # 1.1  SQUARE SV\n33DD          ; mapped                 ; 0077 0062     # 1.1  SQUARE WB\n33DE          ; mapped                 ; 0076 2215 006D #4.0  SQUARE V OVER M\n33DF          ; mapped                 ; 0061 2215 006D #4.0  SQUARE A OVER M\n33E0          ; mapped                 ; 0031 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE\n33E1          ; mapped                 ; 0032 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO\n33E2          ; mapped                 ; 0033 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE\n33E3          ; mapped                 ; 0034 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR\n33E4          ; mapped                 ; 0035 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE\n33E5          ; mapped                 ; 0036 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX\n33E6          ; mapped                 ; 0037 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN\n33E7          ; mapped                 ; 0038 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT\n33E8          ; mapped                 ; 0039 65E5     # 1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE\n33E9          ; mapped                 ; 0031 0030 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN\n33EA          ; mapped                 ; 0031 0031 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN\n33EB          ; mapped                 ; 0031 0032 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE\n33EC          ; mapped                 ; 0031 0033 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN\n33ED          ; mapped                 ; 0031 0034 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN\n33EE          ; mapped                 ; 0031 0035 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN\n33EF          ; mapped                 ; 0031 0036 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN\n33F0          ; mapped                 ; 0031 0037 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN\n33F1          ; mapped                 ; 0031 0038 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN\n33F2          ; mapped                 ; 0031 0039 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN\n33F3          ; mapped                 ; 0032 0030 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY\n33F4          ; mapped                 ; 0032 0031 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE\n33F5          ; mapped                 ; 0032 0032 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO\n33F6          ; mapped                 ; 0032 0033 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE\n33F7          ; mapped                 ; 0032 0034 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR\n33F8          ; mapped                 ; 0032 0035 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE\n33F9          ; mapped                 ; 0032 0036 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX\n33FA          ; mapped                 ; 0032 0037 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN\n33FB          ; mapped                 ; 0032 0038 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT\n33FC          ; mapped                 ; 0032 0039 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE\n33FD          ; mapped                 ; 0033 0030 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY\n33FE          ; mapped                 ; 0033 0031 65E5 #1.1  IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE\n33FF          ; mapped                 ; 0067 0061 006C #4.0  SQUARE GAL\n3400..4DB5    ; valid                                  # 3.0  CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DB5\n4DB6..4DBF    ; valid                                  # 13.0 CJK UNIFIED IDEOGRAPH-4DB6..CJK UNIFIED IDEOGRAPH-4DBF\n4DC0..4DFF    ; valid                  ;      ; NV8    # 4.0  HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION\n4E00..9FA5    ; valid                                  # 1.1  CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FA5\n9FA6..9FBB    ; valid                                  # 4.1  CJK UNIFIED IDEOGRAPH-9FA6..CJK UNIFIED IDEOGRAPH-9FBB\n9FBC..9FC3    ; valid                                  # 5.1  CJK UNIFIED IDEOGRAPH-9FBC..CJK UNIFIED IDEOGRAPH-9FC3\n9FC4..9FCB    ; valid                                  # 5.2  CJK UNIFIED IDEOGRAPH-9FC4..CJK UNIFIED IDEOGRAPH-9FCB\n9FCC          ; valid                                  # 6.1  CJK UNIFIED IDEOGRAPH-9FCC\n9FCD..9FD5    ; valid                                  # 8.0  CJK UNIFIED IDEOGRAPH-9FCD..CJK UNIFIED IDEOGRAPH-9FD5\n9FD6..9FEA    ; valid                                  # 10.0 CJK UNIFIED IDEOGRAPH-9FD6..CJK UNIFIED IDEOGRAPH-9FEA\n9FEB..9FEF    ; valid                                  # 11.0 CJK UNIFIED IDEOGRAPH-9FEB..CJK UNIFIED IDEOGRAPH-9FEF\n9FF0..9FFC    ; valid                                  # 13.0 CJK UNIFIED IDEOGRAPH-9FF0..CJK UNIFIED IDEOGRAPH-9FFC\n9FFD..9FFF    ; valid                                  # 14.0 CJK UNIFIED IDEOGRAPH-9FFD..CJK UNIFIED IDEOGRAPH-9FFF\nA000..A48C    ; valid                                  # 3.0  YI SYLLABLE IT..YI SYLLABLE YYR\nA48D..A48F    ; disallowed                             # NA   <reserved-A48D>..<reserved-A48F>\nA490..A4A1    ; valid                  ;      ; NV8    # 3.0  YI RADICAL QOT..YI RADICAL GA\nA4A2..A4A3    ; valid                  ;      ; NV8    # 3.2  YI RADICAL ZUP..YI RADICAL CYT\nA4A4..A4B3    ; valid                  ;      ; NV8    # 3.0  YI RADICAL DDUR..YI RADICAL JO\nA4B4          ; valid                  ;      ; NV8    # 3.2  YI RADICAL NZUP\nA4B5..A4C0    ; valid                  ;      ; NV8    # 3.0  YI RADICAL JJY..YI RADICAL SHAT\nA4C1          ; valid                  ;      ; NV8    # 3.2  YI RADICAL ZUR\nA4C2..A4C4    ; valid                  ;      ; NV8    # 3.0  YI RADICAL SHOP..YI RADICAL ZZIET\nA4C5          ; valid                  ;      ; NV8    # 3.2  YI RADICAL NBIE\nA4C6          ; valid                  ;      ; NV8    # 3.0  YI RADICAL KE\nA4C7..A4CF    ; disallowed                             # NA   <reserved-A4C7>..<reserved-A4CF>\nA4D0..A4FD    ; valid                                  # 5.2  LISU LETTER BA..LISU LETTER TONE MYA JEU\nA4FE..A4FF    ; valid                  ;      ; NV8    # 5.2  LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP\nA500..A60C    ; valid                                  # 5.1  VAI SYLLABLE EE..VAI SYLLABLE LENGTHENER\nA60D..A60F    ; valid                  ;      ; NV8    # 5.1  VAI COMMA..VAI QUESTION MARK\nA610..A62B    ; valid                                  # 5.1  VAI SYLLABLE NDOLE FA..VAI SYLLABLE NDOLE DO\nA62C..A63F    ; disallowed                             # NA   <reserved-A62C>..<reserved-A63F>\nA640          ; mapped                 ; A641          # 5.1  CYRILLIC CAPITAL LETTER ZEMLYA\nA641          ; valid                                  # 5.1  CYRILLIC SMALL LETTER ZEMLYA\nA642          ; mapped                 ; A643          # 5.1  CYRILLIC CAPITAL LETTER DZELO\nA643          ; valid                                  # 5.1  CYRILLIC SMALL LETTER DZELO\nA644          ; mapped                 ; A645          # 5.1  CYRILLIC CAPITAL LETTER REVERSED DZE\nA645          ; valid                                  # 5.1  CYRILLIC SMALL LETTER REVERSED DZE\nA646          ; mapped                 ; A647          # 5.1  CYRILLIC CAPITAL LETTER IOTA\nA647          ; valid                                  # 5.1  CYRILLIC SMALL LETTER IOTA\nA648          ; mapped                 ; A649          # 5.1  CYRILLIC CAPITAL LETTER DJERV\nA649          ; valid                                  # 5.1  CYRILLIC SMALL LETTER DJERV\nA64A          ; mapped                 ; A64B          # 5.1  CYRILLIC CAPITAL LETTER MONOGRAPH UK\nA64B          ; valid                                  # 5.1  CYRILLIC SMALL LETTER MONOGRAPH UK\nA64C          ; mapped                 ; A64D          # 5.1  CYRILLIC CAPITAL LETTER BROAD OMEGA\nA64D          ; valid                                  # 5.1  CYRILLIC SMALL LETTER BROAD OMEGA\nA64E          ; mapped                 ; A64F          # 5.1  CYRILLIC CAPITAL LETTER NEUTRAL YER\nA64F          ; valid                                  # 5.1  CYRILLIC SMALL LETTER NEUTRAL YER\nA650          ; mapped                 ; A651          # 5.1  CYRILLIC CAPITAL LETTER YERU WITH BACK YER\nA651          ; valid                                  # 5.1  CYRILLIC SMALL LETTER YERU WITH BACK YER\nA652          ; mapped                 ; A653          # 5.1  CYRILLIC CAPITAL LETTER IOTIFIED YAT\nA653          ; valid                                  # 5.1  CYRILLIC SMALL LETTER IOTIFIED YAT\nA654          ; mapped                 ; A655          # 5.1  CYRILLIC CAPITAL LETTER REVERSED YU\nA655          ; valid                                  # 5.1  CYRILLIC SMALL LETTER REVERSED YU\nA656          ; mapped                 ; A657          # 5.1  CYRILLIC CAPITAL LETTER IOTIFIED A\nA657          ; valid                                  # 5.1  CYRILLIC SMALL LETTER IOTIFIED A\nA658          ; mapped                 ; A659          # 5.1  CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS\nA659          ; valid                                  # 5.1  CYRILLIC SMALL LETTER CLOSED LITTLE YUS\nA65A          ; mapped                 ; A65B          # 5.1  CYRILLIC CAPITAL LETTER BLENDED YUS\nA65B          ; valid                                  # 5.1  CYRILLIC SMALL LETTER BLENDED YUS\nA65C          ; mapped                 ; A65D          # 5.1  CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS\nA65D          ; valid                                  # 5.1  CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS\nA65E          ; mapped                 ; A65F          # 5.1  CYRILLIC CAPITAL LETTER YN\nA65F          ; valid                                  # 5.1  CYRILLIC SMALL LETTER YN\nA660          ; mapped                 ; A661          # 6.0  CYRILLIC CAPITAL LETTER REVERSED TSE\nA661          ; valid                                  # 6.0  CYRILLIC SMALL LETTER REVERSED TSE\nA662          ; mapped                 ; A663          # 5.1  CYRILLIC CAPITAL LETTER SOFT DE\nA663          ; valid                                  # 5.1  CYRILLIC SMALL LETTER SOFT DE\nA664          ; mapped                 ; A665          # 5.1  CYRILLIC CAPITAL LETTER SOFT EL\nA665          ; valid                                  # 5.1  CYRILLIC SMALL LETTER SOFT EL\nA666          ; mapped                 ; A667          # 5.1  CYRILLIC CAPITAL LETTER SOFT EM\nA667          ; valid                                  # 5.1  CYRILLIC SMALL LETTER SOFT EM\nA668          ; mapped                 ; A669          # 5.1  CYRILLIC CAPITAL LETTER MONOCULAR O\nA669          ; valid                                  # 5.1  CYRILLIC SMALL LETTER MONOCULAR O\nA66A          ; mapped                 ; A66B          # 5.1  CYRILLIC CAPITAL LETTER BINOCULAR O\nA66B          ; valid                                  # 5.1  CYRILLIC SMALL LETTER BINOCULAR O\nA66C          ; mapped                 ; A66D          # 5.1  CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O\nA66D..A66F    ; valid                                  # 5.1  CYRILLIC SMALL LETTER DOUBLE MONOCULAR O..COMBINING CYRILLIC VZMET\nA670..A673    ; valid                  ;      ; NV8    # 5.1  COMBINING CYRILLIC TEN MILLIONS SIGN..SLAVONIC ASTERISK\nA674..A67B    ; valid                                  # 6.1  COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC LETTER OMEGA\nA67C..A67D    ; valid                                  # 5.1  COMBINING CYRILLIC KAVYKA..COMBINING CYRILLIC PAYEROK\nA67E          ; valid                  ;      ; NV8    # 5.1  CYRILLIC KAVYKA\nA67F          ; valid                                  # 5.1  CYRILLIC PAYEROK\nA680          ; mapped                 ; A681          # 5.1  CYRILLIC CAPITAL LETTER DWE\nA681          ; valid                                  # 5.1  CYRILLIC SMALL LETTER DWE\nA682          ; mapped                 ; A683          # 5.1  CYRILLIC CAPITAL LETTER DZWE\nA683          ; valid                                  # 5.1  CYRILLIC SMALL LETTER DZWE\nA684          ; mapped                 ; A685          # 5.1  CYRILLIC CAPITAL LETTER ZHWE\nA685          ; valid                                  # 5.1  CYRILLIC SMALL LETTER ZHWE\nA686          ; mapped                 ; A687          # 5.1  CYRILLIC CAPITAL LETTER CCHE\nA687          ; valid                                  # 5.1  CYRILLIC SMALL LETTER CCHE\nA688          ; mapped                 ; A689          # 5.1  CYRILLIC CAPITAL LETTER DZZE\nA689          ; valid                                  # 5.1  CYRILLIC SMALL LETTER DZZE\nA68A          ; mapped                 ; A68B          # 5.1  CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK\nA68B          ; valid                                  # 5.1  CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK\nA68C          ; mapped                 ; A68D          # 5.1  CYRILLIC CAPITAL LETTER TWE\nA68D          ; valid                                  # 5.1  CYRILLIC SMALL LETTER TWE\nA68E          ; mapped                 ; A68F          # 5.1  CYRILLIC CAPITAL LETTER TSWE\nA68F          ; valid                                  # 5.1  CYRILLIC SMALL LETTER TSWE\nA690          ; mapped                 ; A691          # 5.1  CYRILLIC CAPITAL LETTER TSSE\nA691          ; valid                                  # 5.1  CYRILLIC SMALL LETTER TSSE\nA692          ; mapped                 ; A693          # 5.1  CYRILLIC CAPITAL LETTER TCHE\nA693          ; valid                                  # 5.1  CYRILLIC SMALL LETTER TCHE\nA694          ; mapped                 ; A695          # 5.1  CYRILLIC CAPITAL LETTER HWE\nA695          ; valid                                  # 5.1  CYRILLIC SMALL LETTER HWE\nA696          ; mapped                 ; A697          # 5.1  CYRILLIC CAPITAL LETTER SHWE\nA697          ; valid                                  # 5.1  CYRILLIC SMALL LETTER SHWE\nA698          ; mapped                 ; A699          # 7.0  CYRILLIC CAPITAL LETTER DOUBLE O\nA699          ; valid                                  # 7.0  CYRILLIC SMALL LETTER DOUBLE O\nA69A          ; mapped                 ; A69B          # 7.0  CYRILLIC CAPITAL LETTER CROSSED O\nA69B          ; valid                                  # 7.0  CYRILLIC SMALL LETTER CROSSED O\nA69C          ; mapped                 ; 044A          # 7.0  MODIFIER LETTER CYRILLIC HARD SIGN\nA69D          ; mapped                 ; 044C          # 7.0  MODIFIER LETTER CYRILLIC SOFT SIGN\nA69E          ; valid                                  # 8.0  COMBINING CYRILLIC LETTER EF\nA69F          ; valid                                  # 6.1  COMBINING CYRILLIC LETTER IOTIFIED E\nA6A0..A6E5    ; valid                                  # 5.2  BAMUM LETTER A..BAMUM LETTER KI\nA6E6..A6EF    ; valid                  ;      ; NV8    # 5.2  BAMUM LETTER MO..BAMUM LETTER KOGHOM\nA6F0..A6F1    ; valid                                  # 5.2  BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS\nA6F2..A6F7    ; valid                  ;      ; NV8    # 5.2  BAMUM NJAEMLI..BAMUM QUESTION MARK\nA6F8..A6FF    ; disallowed                             # NA   <reserved-A6F8>..<reserved-A6FF>\nA700..A716    ; valid                  ;      ; NV8    # 4.1  MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR\nA717..A71A    ; valid                                  # 5.0  MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOWER RIGHT CORNER ANGLE\nA71B..A71F    ; valid                                  # 5.1  MODIFIER LETTER RAISED UP ARROW..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK\nA720..A721    ; valid                  ;      ; NV8    # 5.0  MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE\nA722          ; mapped                 ; A723          # 5.1  LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF\nA723          ; valid                                  # 5.1  LATIN SMALL LETTER EGYPTOLOGICAL ALEF\nA724          ; mapped                 ; A725          # 5.1  LATIN CAPITAL LETTER EGYPTOLOGICAL AIN\nA725          ; valid                                  # 5.1  LATIN SMALL LETTER EGYPTOLOGICAL AIN\nA726          ; mapped                 ; A727          # 5.1  LATIN CAPITAL LETTER HENG\nA727          ; valid                                  # 5.1  LATIN SMALL LETTER HENG\nA728          ; mapped                 ; A729          # 5.1  LATIN CAPITAL LETTER TZ\nA729          ; valid                                  # 5.1  LATIN SMALL LETTER TZ\nA72A          ; mapped                 ; A72B          # 5.1  LATIN CAPITAL LETTER TRESILLO\nA72B          ; valid                                  # 5.1  LATIN SMALL LETTER TRESILLO\nA72C          ; mapped                 ; A72D          # 5.1  LATIN CAPITAL LETTER CUATRILLO\nA72D          ; valid                                  # 5.1  LATIN SMALL LETTER CUATRILLO\nA72E          ; mapped                 ; A72F          # 5.1  LATIN CAPITAL LETTER CUATRILLO WITH COMMA\nA72F..A731    ; valid                                  # 5.1  LATIN SMALL LETTER CUATRILLO WITH COMMA..LATIN LETTER SMALL CAPITAL S\nA732          ; mapped                 ; A733          # 5.1  LATIN CAPITAL LETTER AA\nA733          ; valid                                  # 5.1  LATIN SMALL LETTER AA\nA734          ; mapped                 ; A735          # 5.1  LATIN CAPITAL LETTER AO\nA735          ; valid                                  # 5.1  LATIN SMALL LETTER AO\nA736          ; mapped                 ; A737          # 5.1  LATIN CAPITAL LETTER AU\nA737          ; valid                                  # 5.1  LATIN SMALL LETTER AU\nA738          ; mapped                 ; A739          # 5.1  LATIN CAPITAL LETTER AV\nA739          ; valid                                  # 5.1  LATIN SMALL LETTER AV\nA73A          ; mapped                 ; A73B          # 5.1  LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR\nA73B          ; valid                                  # 5.1  LATIN SMALL LETTER AV WITH HORIZONTAL BAR\nA73C          ; mapped                 ; A73D          # 5.1  LATIN CAPITAL LETTER AY\nA73D          ; valid                                  # 5.1  LATIN SMALL LETTER AY\nA73E          ; mapped                 ; A73F          # 5.1  LATIN CAPITAL LETTER REVERSED C WITH DOT\nA73F          ; valid                                  # 5.1  LATIN SMALL LETTER REVERSED C WITH DOT\nA740          ; mapped                 ; A741          # 5.1  LATIN CAPITAL LETTER K WITH STROKE\nA741          ; valid                                  # 5.1  LATIN SMALL LETTER K WITH STROKE\nA742          ; mapped                 ; A743          # 5.1  LATIN CAPITAL LETTER K WITH DIAGONAL STROKE\nA743          ; valid                                  # 5.1  LATIN SMALL LETTER K WITH DIAGONAL STROKE\nA744          ; mapped                 ; A745          # 5.1  LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE\nA745          ; valid                                  # 5.1  LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE\nA746          ; mapped                 ; A747          # 5.1  LATIN CAPITAL LETTER BROKEN L\nA747          ; valid                                  # 5.1  LATIN SMALL LETTER BROKEN L\nA748          ; mapped                 ; A749          # 5.1  LATIN CAPITAL LETTER L WITH HIGH STROKE\nA749          ; valid                                  # 5.1  LATIN SMALL LETTER L WITH HIGH STROKE\nA74A          ; mapped                 ; A74B          # 5.1  LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY\nA74B          ; valid                                  # 5.1  LATIN SMALL LETTER O WITH LONG STROKE OVERLAY\nA74C          ; mapped                 ; A74D          # 5.1  LATIN CAPITAL LETTER O WITH LOOP\nA74D          ; valid                                  # 5.1  LATIN SMALL LETTER O WITH LOOP\nA74E          ; mapped                 ; A74F          # 5.1  LATIN CAPITAL LETTER OO\nA74F          ; valid                                  # 5.1  LATIN SMALL LETTER OO\nA750          ; mapped                 ; A751          # 5.1  LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER\nA751          ; valid                                  # 5.1  LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER\nA752          ; mapped                 ; A753          # 5.1  LATIN CAPITAL LETTER P WITH FLOURISH\nA753          ; valid                                  # 5.1  LATIN SMALL LETTER P WITH FLOURISH\nA754          ; mapped                 ; A755          # 5.1  LATIN CAPITAL LETTER P WITH SQUIRREL TAIL\nA755          ; valid                                  # 5.1  LATIN SMALL LETTER P WITH SQUIRREL TAIL\nA756          ; mapped                 ; A757          # 5.1  LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER\nA757          ; valid                                  # 5.1  LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER\nA758          ; mapped                 ; A759          # 5.1  LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE\nA759          ; valid                                  # 5.1  LATIN SMALL LETTER Q WITH DIAGONAL STROKE\nA75A          ; mapped                 ; A75B          # 5.1  LATIN CAPITAL LETTER R ROTUNDA\nA75B          ; valid                                  # 5.1  LATIN SMALL LETTER R ROTUNDA\nA75C          ; mapped                 ; A75D          # 5.1  LATIN CAPITAL LETTER RUM ROTUNDA\nA75D          ; valid                                  # 5.1  LATIN SMALL LETTER RUM ROTUNDA\nA75E          ; mapped                 ; A75F          # 5.1  LATIN CAPITAL LETTER V WITH DIAGONAL STROKE\nA75F          ; valid                                  # 5.1  LATIN SMALL LETTER V WITH DIAGONAL STROKE\nA760          ; mapped                 ; A761          # 5.1  LATIN CAPITAL LETTER VY\nA761          ; valid                                  # 5.1  LATIN SMALL LETTER VY\nA762          ; mapped                 ; A763          # 5.1  LATIN CAPITAL LETTER VISIGOTHIC Z\nA763          ; valid                                  # 5.1  LATIN SMALL LETTER VISIGOTHIC Z\nA764          ; mapped                 ; A765          # 5.1  LATIN CAPITAL LETTER THORN WITH STROKE\nA765          ; valid                                  # 5.1  LATIN SMALL LETTER THORN WITH STROKE\nA766          ; mapped                 ; A767          # 5.1  LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER\nA767          ; valid                                  # 5.1  LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER\nA768          ; mapped                 ; A769          # 5.1  LATIN CAPITAL LETTER VEND\nA769          ; valid                                  # 5.1  LATIN SMALL LETTER VEND\nA76A          ; mapped                 ; A76B          # 5.1  LATIN CAPITAL LETTER ET\nA76B          ; valid                                  # 5.1  LATIN SMALL LETTER ET\nA76C          ; mapped                 ; A76D          # 5.1  LATIN CAPITAL LETTER IS\nA76D          ; valid                                  # 5.1  LATIN SMALL LETTER IS\nA76E          ; mapped                 ; A76F          # 5.1  LATIN CAPITAL LETTER CON\nA76F          ; valid                                  # 5.1  LATIN SMALL LETTER CON\nA770          ; mapped                 ; A76F          # 5.1  MODIFIER LETTER US\nA771..A778    ; valid                                  # 5.1  LATIN SMALL LETTER DUM..LATIN SMALL LETTER UM\nA779          ; mapped                 ; A77A          # 5.1  LATIN CAPITAL LETTER INSULAR D\nA77A          ; valid                                  # 5.1  LATIN SMALL LETTER INSULAR D\nA77B          ; mapped                 ; A77C          # 5.1  LATIN CAPITAL LETTER INSULAR F\nA77C          ; valid                                  # 5.1  LATIN SMALL LETTER INSULAR F\nA77D          ; mapped                 ; 1D79          # 5.1  LATIN CAPITAL LETTER INSULAR G\nA77E          ; mapped                 ; A77F          # 5.1  LATIN CAPITAL LETTER TURNED INSULAR G\nA77F          ; valid                                  # 5.1  LATIN SMALL LETTER TURNED INSULAR G\nA780          ; mapped                 ; A781          # 5.1  LATIN CAPITAL LETTER TURNED L\nA781          ; valid                                  # 5.1  LATIN SMALL LETTER TURNED L\nA782          ; mapped                 ; A783          # 5.1  LATIN CAPITAL LETTER INSULAR R\nA783          ; valid                                  # 5.1  LATIN SMALL LETTER INSULAR R\nA784          ; mapped                 ; A785          # 5.1  LATIN CAPITAL LETTER INSULAR S\nA785          ; valid                                  # 5.1  LATIN SMALL LETTER INSULAR S\nA786          ; mapped                 ; A787          # 5.1  LATIN CAPITAL LETTER INSULAR T\nA787..A788    ; valid                                  # 5.1  LATIN SMALL LETTER INSULAR T..MODIFIER LETTER LOW CIRCUMFLEX ACCENT\nA789..A78A    ; valid                  ;      ; NV8    # 5.1  MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN\nA78B          ; mapped                 ; A78C          # 5.1  LATIN CAPITAL LETTER SALTILLO\nA78C          ; valid                                  # 5.1  LATIN SMALL LETTER SALTILLO\nA78D          ; mapped                 ; 0265          # 6.0  LATIN CAPITAL LETTER TURNED H\nA78E          ; valid                                  # 6.0  LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT\nA78F          ; valid                                  # 8.0  LATIN LETTER SINOLOGICAL DOT\nA790          ; mapped                 ; A791          # 6.0  LATIN CAPITAL LETTER N WITH DESCENDER\nA791          ; valid                                  # 6.0  LATIN SMALL LETTER N WITH DESCENDER\nA792          ; mapped                 ; A793          # 6.1  LATIN CAPITAL LETTER C WITH BAR\nA793          ; valid                                  # 6.1  LATIN SMALL LETTER C WITH BAR\nA794..A795    ; valid                                  # 7.0  LATIN SMALL LETTER C WITH PALATAL HOOK..LATIN SMALL LETTER H WITH PALATAL HOOK\nA796          ; mapped                 ; A797          # 7.0  LATIN CAPITAL LETTER B WITH FLOURISH\nA797          ; valid                                  # 7.0  LATIN SMALL LETTER B WITH FLOURISH\nA798          ; mapped                 ; A799          # 7.0  LATIN CAPITAL LETTER F WITH STROKE\nA799          ; valid                                  # 7.0  LATIN SMALL LETTER F WITH STROKE\nA79A          ; mapped                 ; A79B          # 7.0  LATIN CAPITAL LETTER VOLAPUK AE\nA79B          ; valid                                  # 7.0  LATIN SMALL LETTER VOLAPUK AE\nA79C          ; mapped                 ; A79D          # 7.0  LATIN CAPITAL LETTER VOLAPUK OE\nA79D          ; valid                                  # 7.0  LATIN SMALL LETTER VOLAPUK OE\nA79E          ; mapped                 ; A79F          # 7.0  LATIN CAPITAL LETTER VOLAPUK UE\nA79F          ; valid                                  # 7.0  LATIN SMALL LETTER VOLAPUK UE\nA7A0          ; mapped                 ; A7A1          # 6.0  LATIN CAPITAL LETTER G WITH OBLIQUE STROKE\nA7A1          ; valid                                  # 6.0  LATIN SMALL LETTER G WITH OBLIQUE STROKE\nA7A2          ; mapped                 ; A7A3          # 6.0  LATIN CAPITAL LETTER K WITH OBLIQUE STROKE\nA7A3          ; valid                                  # 6.0  LATIN SMALL LETTER K WITH OBLIQUE STROKE\nA7A4          ; mapped                 ; A7A5          # 6.0  LATIN CAPITAL LETTER N WITH OBLIQUE STROKE\nA7A5          ; valid                                  # 6.0  LATIN SMALL LETTER N WITH OBLIQUE STROKE\nA7A6          ; mapped                 ; A7A7          # 6.0  LATIN CAPITAL LETTER R WITH OBLIQUE STROKE\nA7A7          ; valid                                  # 6.0  LATIN SMALL LETTER R WITH OBLIQUE STROKE\nA7A8          ; mapped                 ; A7A9          # 6.0  LATIN CAPITAL LETTER S WITH OBLIQUE STROKE\nA7A9          ; valid                                  # 6.0  LATIN SMALL LETTER S WITH OBLIQUE STROKE\nA7AA          ; mapped                 ; 0266          # 6.1  LATIN CAPITAL LETTER H WITH HOOK\nA7AB          ; mapped                 ; 025C          # 7.0  LATIN CAPITAL LETTER REVERSED OPEN E\nA7AC          ; mapped                 ; 0261          # 7.0  LATIN CAPITAL LETTER SCRIPT G\nA7AD          ; mapped                 ; 026C          # 7.0  LATIN CAPITAL LETTER L WITH BELT\nA7AE          ; mapped                 ; 026A          # 9.0  LATIN CAPITAL LETTER SMALL CAPITAL I\nA7AF          ; valid                                  # 11.0 LATIN LETTER SMALL CAPITAL Q\nA7B0          ; mapped                 ; 029E          # 7.0  LATIN CAPITAL LETTER TURNED K\nA7B1          ; mapped                 ; 0287          # 7.0  LATIN CAPITAL LETTER TURNED T\nA7B2          ; mapped                 ; 029D          # 8.0  LATIN CAPITAL LETTER J WITH CROSSED-TAIL\nA7B3          ; mapped                 ; AB53          # 8.0  LATIN CAPITAL LETTER CHI\nA7B4          ; mapped                 ; A7B5          # 8.0  LATIN CAPITAL LETTER BETA\nA7B5          ; valid                                  # 8.0  LATIN SMALL LETTER BETA\nA7B6          ; mapped                 ; A7B7          # 8.0  LATIN CAPITAL LETTER OMEGA\nA7B7          ; valid                                  # 8.0  LATIN SMALL LETTER OMEGA\nA7B8          ; mapped                 ; A7B9          # 11.0 LATIN CAPITAL LETTER U WITH STROKE\nA7B9          ; valid                                  # 11.0 LATIN SMALL LETTER U WITH STROKE\nA7BA          ; mapped                 ; A7BB          # 12.0 LATIN CAPITAL LETTER GLOTTAL A\nA7BB          ; valid                                  # 12.0 LATIN SMALL LETTER GLOTTAL A\nA7BC          ; mapped                 ; A7BD          # 12.0 LATIN CAPITAL LETTER GLOTTAL I\nA7BD          ; valid                                  # 12.0 LATIN SMALL LETTER GLOTTAL I\nA7BE          ; mapped                 ; A7BF          # 12.0 LATIN CAPITAL LETTER GLOTTAL U\nA7BF          ; valid                                  # 12.0 LATIN SMALL LETTER GLOTTAL U\nA7C0          ; mapped                 ; A7C1          # 14.0 LATIN CAPITAL LETTER OLD POLISH O\nA7C1          ; valid                                  # 14.0 LATIN SMALL LETTER OLD POLISH O\nA7C2          ; mapped                 ; A7C3          # 12.0 LATIN CAPITAL LETTER ANGLICANA W\nA7C3          ; valid                                  # 12.0 LATIN SMALL LETTER ANGLICANA W\nA7C4          ; mapped                 ; A794          # 12.0 LATIN CAPITAL LETTER C WITH PALATAL HOOK\nA7C5          ; mapped                 ; 0282          # 12.0 LATIN CAPITAL LETTER S WITH HOOK\nA7C6          ; mapped                 ; 1D8E          # 12.0 LATIN CAPITAL LETTER Z WITH PALATAL HOOK\nA7C7          ; mapped                 ; A7C8          # 13.0 LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY\nA7C8          ; valid                                  # 13.0 LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY\nA7C9          ; mapped                 ; A7CA          # 13.0 LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY\nA7CA          ; valid                                  # 13.0 LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY\nA7CB..A7CF    ; disallowed                             # NA   <reserved-A7CB>..<reserved-A7CF>\nA7D0          ; mapped                 ; A7D1          # 14.0 LATIN CAPITAL LETTER CLOSED INSULAR G\nA7D1          ; valid                                  # 14.0 LATIN SMALL LETTER CLOSED INSULAR G\nA7D2          ; disallowed                             # NA   <reserved-A7D2>\nA7D3          ; valid                                  # 14.0 LATIN SMALL LETTER DOUBLE THORN\nA7D4          ; disallowed                             # NA   <reserved-A7D4>\nA7D5          ; valid                                  # 14.0 LATIN SMALL LETTER DOUBLE WYNN\nA7D6          ; mapped                 ; A7D7          # 14.0 LATIN CAPITAL LETTER MIDDLE SCOTS S\nA7D7          ; valid                                  # 14.0 LATIN SMALL LETTER MIDDLE SCOTS S\nA7D8          ; mapped                 ; A7D9          # 14.0 LATIN CAPITAL LETTER SIGMOID S\nA7D9          ; valid                                  # 14.0 LATIN SMALL LETTER SIGMOID S\nA7DA..A7F1    ; disallowed                             # NA   <reserved-A7DA>..<reserved-A7F1>\nA7F2          ; mapped                 ; 0063          # 14.0 MODIFIER LETTER CAPITAL C\nA7F3          ; mapped                 ; 0066          # 14.0 MODIFIER LETTER CAPITAL F\nA7F4          ; mapped                 ; 0071          # 14.0 MODIFIER LETTER CAPITAL Q\nA7F5          ; mapped                 ; A7F6          # 13.0 LATIN CAPITAL LETTER REVERSED HALF H\nA7F6          ; valid                                  # 13.0 LATIN SMALL LETTER REVERSED HALF H\nA7F7          ; valid                                  # 7.0  LATIN EPIGRAPHIC LETTER SIDEWAYS I\nA7F8          ; mapped                 ; 0127          # 6.1  MODIFIER LETTER CAPITAL H WITH STROKE\nA7F9          ; mapped                 ; 0153          # 6.1  MODIFIER LETTER SMALL LIGATURE OE\nA7FA          ; valid                                  # 6.0  LATIN LETTER SMALL CAPITAL TURNED M\nA7FB..A7FF    ; valid                                  # 5.1  LATIN EPIGRAPHIC LETTER REVERSED F..LATIN EPIGRAPHIC LETTER ARCHAIC M\nA800..A827    ; valid                                  # 4.1  SYLOTI NAGRI LETTER A..SYLOTI NAGRI VOWEL SIGN OO\nA828..A82B    ; valid                  ;      ; NV8    # 4.1  SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4\nA82C          ; valid                                  # 13.0 SYLOTI NAGRI SIGN ALTERNATE HASANTA\nA82D..A82F    ; disallowed                             # NA   <reserved-A82D>..<reserved-A82F>\nA830..A839    ; valid                  ;      ; NV8    # 5.2  NORTH INDIC FRACTION ONE QUARTER..NORTH INDIC QUANTITY MARK\nA83A..A83F    ; disallowed                             # NA   <reserved-A83A>..<reserved-A83F>\nA840..A873    ; valid                                  # 5.0  PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU\nA874..A877    ; valid                  ;      ; NV8    # 5.0  PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD\nA878..A87F    ; disallowed                             # NA   <reserved-A878>..<reserved-A87F>\nA880..A8C4    ; valid                                  # 5.1  SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VIRAMA\nA8C5          ; valid                                  # 9.0  SAURASHTRA SIGN CANDRABINDU\nA8C6..A8CD    ; disallowed                             # NA   <reserved-A8C6>..<reserved-A8CD>\nA8CE..A8CF    ; valid                  ;      ; NV8    # 5.1  SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA\nA8D0..A8D9    ; valid                                  # 5.1  SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE\nA8DA..A8DF    ; disallowed                             # NA   <reserved-A8DA>..<reserved-A8DF>\nA8E0..A8F7    ; valid                                  # 5.2  COMBINING DEVANAGARI DIGIT ZERO..DEVANAGARI SIGN CANDRABINDU AVAGRAHA\nA8F8..A8FA    ; valid                  ;      ; NV8    # 5.2  DEVANAGARI SIGN PUSHPIKA..DEVANAGARI CARET\nA8FB          ; valid                                  # 5.2  DEVANAGARI HEADSTROKE\nA8FC          ; valid                  ;      ; NV8    # 8.0  DEVANAGARI SIGN SIDDHAM\nA8FD          ; valid                                  # 8.0  DEVANAGARI JAIN OM\nA8FE..A8FF    ; valid                                  # 11.0 DEVANAGARI LETTER AY..DEVANAGARI VOWEL SIGN AY\nA900..A92D    ; valid                                  # 5.1  KAYAH LI DIGIT ZERO..KAYAH LI TONE CALYA PLOPHU\nA92E..A92F    ; valid                  ;      ; NV8    # 5.1  KAYAH LI SIGN CWI..KAYAH LI SIGN SHYA\nA930..A953    ; valid                                  # 5.1  REJANG LETTER KA..REJANG VIRAMA\nA954..A95E    ; disallowed                             # NA   <reserved-A954>..<reserved-A95E>\nA95F          ; valid                  ;      ; NV8    # 5.1  REJANG SECTION MARK\nA960..A97C    ; valid                  ;      ; NV8    # 5.2  HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH\nA97D..A97F    ; disallowed                             # NA   <reserved-A97D>..<reserved-A97F>\nA980..A9C0    ; valid                                  # 5.2  JAVANESE SIGN PANYANGGA..JAVANESE PANGKON\nA9C1..A9CD    ; valid                  ;      ; NV8    # 5.2  JAVANESE LEFT RERENGGAN..JAVANESE TURNED PADA PISELEH\nA9CE          ; disallowed                             # NA   <reserved-A9CE>\nA9CF..A9D9    ; valid                                  # 5.2  JAVANESE PANGRANGKEP..JAVANESE DIGIT NINE\nA9DA..A9DD    ; disallowed                             # NA   <reserved-A9DA>..<reserved-A9DD>\nA9DE..A9DF    ; valid                  ;      ; NV8    # 5.2  JAVANESE PADA TIRTA TUMETES..JAVANESE PADA ISEN-ISEN\nA9E0..A9FE    ; valid                                  # 7.0  MYANMAR LETTER SHAN GHA..MYANMAR LETTER TAI LAING BHA\nA9FF          ; disallowed                             # NA   <reserved-A9FF>\nAA00..AA36    ; valid                                  # 5.1  CHAM LETTER A..CHAM CONSONANT SIGN WA\nAA37..AA3F    ; disallowed                             # NA   <reserved-AA37>..<reserved-AA3F>\nAA40..AA4D    ; valid                                  # 5.1  CHAM LETTER FINAL K..CHAM CONSONANT SIGN FINAL H\nAA4E..AA4F    ; disallowed                             # NA   <reserved-AA4E>..<reserved-AA4F>\nAA50..AA59    ; valid                                  # 5.1  CHAM DIGIT ZERO..CHAM DIGIT NINE\nAA5A..AA5B    ; disallowed                             # NA   <reserved-AA5A>..<reserved-AA5B>\nAA5C..AA5F    ; valid                  ;      ; NV8    # 5.1  CHAM PUNCTUATION SPIRAL..CHAM PUNCTUATION TRIPLE DANDA\nAA60..AA76    ; valid                                  # 5.2  MYANMAR LETTER KHAMTI GA..MYANMAR LOGOGRAM KHAMTI HM\nAA77..AA79    ; valid                  ;      ; NV8    # 5.2  MYANMAR SYMBOL AITON EXCLAMATION..MYANMAR SYMBOL AITON TWO\nAA7A..AA7B    ; valid                                  # 5.2  MYANMAR LETTER AITON RA..MYANMAR SIGN PAO KAREN TONE\nAA7C..AA7F    ; valid                                  # 7.0  MYANMAR SIGN TAI LAING TONE-2..MYANMAR LETTER SHWE PALAUNG SHA\nAA80..AAC2    ; valid                                  # 5.2  TAI VIET LETTER LOW KO..TAI VIET TONE MAI SONG\nAAC3..AADA    ; disallowed                             # NA   <reserved-AAC3>..<reserved-AADA>\nAADB..AADD    ; valid                                  # 5.2  TAI VIET SYMBOL KON..TAI VIET SYMBOL SAM\nAADE..AADF    ; valid                  ;      ; NV8    # 5.2  TAI VIET SYMBOL HO HOI..TAI VIET SYMBOL KOI KOI\nAAE0..AAEF    ; valid                                  # 6.1  MEETEI MAYEK LETTER E..MEETEI MAYEK VOWEL SIGN AAU\nAAF0..AAF1    ; valid                  ;      ; NV8    # 6.1  MEETEI MAYEK CHEIKHAN..MEETEI MAYEK AHANG KHUDAM\nAAF2..AAF6    ; valid                                  # 6.1  MEETEI MAYEK ANJI..MEETEI MAYEK VIRAMA\nAAF7..AB00    ; disallowed                             # NA   <reserved-AAF7>..<reserved-AB00>\nAB01..AB06    ; valid                                  # 6.0  ETHIOPIC SYLLABLE TTHU..ETHIOPIC SYLLABLE TTHO\nAB07..AB08    ; disallowed                             # NA   <reserved-AB07>..<reserved-AB08>\nAB09..AB0E    ; valid                                  # 6.0  ETHIOPIC SYLLABLE DDHU..ETHIOPIC SYLLABLE DDHO\nAB0F..AB10    ; disallowed                             # NA   <reserved-AB0F>..<reserved-AB10>\nAB11..AB16    ; valid                                  # 6.0  ETHIOPIC SYLLABLE DZU..ETHIOPIC SYLLABLE DZO\nAB17..AB1F    ; disallowed                             # NA   <reserved-AB17>..<reserved-AB1F>\nAB20..AB26    ; valid                                  # 6.0  ETHIOPIC SYLLABLE CCHHA..ETHIOPIC SYLLABLE CCHHO\nAB27          ; disallowed                             # NA   <reserved-AB27>\nAB28..AB2E    ; valid                                  # 6.0  ETHIOPIC SYLLABLE BBA..ETHIOPIC SYLLABLE BBO\nAB2F          ; disallowed                             # NA   <reserved-AB2F>\nAB30..AB5A    ; valid                                  # 7.0  LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL LETTER Y WITH SHORT RIGHT LEG\nAB5B          ; valid                  ;      ; NV8    # 7.0  MODIFIER BREVE WITH INVERTED BREVE\nAB5C          ; mapped                 ; A727          # 7.0  MODIFIER LETTER SMALL HENG\nAB5D          ; mapped                 ; AB37          # 7.0  MODIFIER LETTER SMALL L WITH INVERTED LAZY S\nAB5E          ; mapped                 ; 026B          # 7.0  MODIFIER LETTER SMALL L WITH MIDDLE TILDE\nAB5F          ; mapped                 ; AB52          # 7.0  MODIFIER LETTER SMALL U WITH LEFT HOOK\nAB60..AB63    ; valid                                  # 8.0  LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER UO\nAB64..AB65    ; valid                                  # 7.0  LATIN SMALL LETTER INVERTED ALPHA..GREEK LETTER SMALL CAPITAL OMEGA\nAB66..AB67    ; valid                                  # 12.0 LATIN SMALL LETTER DZ DIGRAPH WITH RETROFLEX HOOK..LATIN SMALL LETTER TS DIGRAPH WITH RETROFLEX HOOK\nAB68          ; valid                                  # 13.0 LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE\nAB69          ; mapped                 ; 028D          # 13.0 MODIFIER LETTER SMALL TURNED W\nAB6A..AB6B    ; valid                  ;      ; NV8    # 13.0 MODIFIER LETTER LEFT TACK..MODIFIER LETTER RIGHT TACK\nAB6C..AB6F    ; disallowed                             # NA   <reserved-AB6C>..<reserved-AB6F>\nAB70          ; mapped                 ; 13A0          # 8.0  CHEROKEE SMALL LETTER A\nAB71          ; mapped                 ; 13A1          # 8.0  CHEROKEE SMALL LETTER E\nAB72          ; mapped                 ; 13A2          # 8.0  CHEROKEE SMALL LETTER I\nAB73          ; mapped                 ; 13A3          # 8.0  CHEROKEE SMALL LETTER O\nAB74          ; mapped                 ; 13A4          # 8.0  CHEROKEE SMALL LETTER U\nAB75          ; mapped                 ; 13A5          # 8.0  CHEROKEE SMALL LETTER V\nAB76          ; mapped                 ; 13A6          # 8.0  CHEROKEE SMALL LETTER GA\nAB77          ; mapped                 ; 13A7          # 8.0  CHEROKEE SMALL LETTER KA\nAB78          ; mapped                 ; 13A8          # 8.0  CHEROKEE SMALL LETTER GE\nAB79          ; mapped                 ; 13A9          # 8.0  CHEROKEE SMALL LETTER GI\nAB7A          ; mapped                 ; 13AA          # 8.0  CHEROKEE SMALL LETTER GO\nAB7B          ; mapped                 ; 13AB          # 8.0  CHEROKEE SMALL LETTER GU\nAB7C          ; mapped                 ; 13AC          # 8.0  CHEROKEE SMALL LETTER GV\nAB7D          ; mapped                 ; 13AD          # 8.0  CHEROKEE SMALL LETTER HA\nAB7E          ; mapped                 ; 13AE          # 8.0  CHEROKEE SMALL LETTER HE\nAB7F          ; mapped                 ; 13AF          # 8.0  CHEROKEE SMALL LETTER HI\nAB80          ; mapped                 ; 13B0          # 8.0  CHEROKEE SMALL LETTER HO\nAB81          ; mapped                 ; 13B1          # 8.0  CHEROKEE SMALL LETTER HU\nAB82          ; mapped                 ; 13B2          # 8.0  CHEROKEE SMALL LETTER HV\nAB83          ; mapped                 ; 13B3          # 8.0  CHEROKEE SMALL LETTER LA\nAB84          ; mapped                 ; 13B4          # 8.0  CHEROKEE SMALL LETTER LE\nAB85          ; mapped                 ; 13B5          # 8.0  CHEROKEE SMALL LETTER LI\nAB86          ; mapped                 ; 13B6          # 8.0  CHEROKEE SMALL LETTER LO\nAB87          ; mapped                 ; 13B7          # 8.0  CHEROKEE SMALL LETTER LU\nAB88          ; mapped                 ; 13B8          # 8.0  CHEROKEE SMALL LETTER LV\nAB89          ; mapped                 ; 13B9          # 8.0  CHEROKEE SMALL LETTER MA\nAB8A          ; mapped                 ; 13BA          # 8.0  CHEROKEE SMALL LETTER ME\nAB8B          ; mapped                 ; 13BB          # 8.0  CHEROKEE SMALL LETTER MI\nAB8C          ; mapped                 ; 13BC          # 8.0  CHEROKEE SMALL LETTER MO\nAB8D          ; mapped                 ; 13BD          # 8.0  CHEROKEE SMALL LETTER MU\nAB8E          ; mapped                 ; 13BE          # 8.0  CHEROKEE SMALL LETTER NA\nAB8F          ; mapped                 ; 13BF          # 8.0  CHEROKEE SMALL LETTER HNA\nAB90          ; mapped                 ; 13C0          # 8.0  CHEROKEE SMALL LETTER NAH\nAB91          ; mapped                 ; 13C1          # 8.0  CHEROKEE SMALL LETTER NE\nAB92          ; mapped                 ; 13C2          # 8.0  CHEROKEE SMALL LETTER NI\nAB93          ; mapped                 ; 13C3          # 8.0  CHEROKEE SMALL LETTER NO\nAB94          ; mapped                 ; 13C4          # 8.0  CHEROKEE SMALL LETTER NU\nAB95          ; mapped                 ; 13C5          # 8.0  CHEROKEE SMALL LETTER NV\nAB96          ; mapped                 ; 13C6          # 8.0  CHEROKEE SMALL LETTER QUA\nAB97          ; mapped                 ; 13C7          # 8.0  CHEROKEE SMALL LETTER QUE\nAB98          ; mapped                 ; 13C8          # 8.0  CHEROKEE SMALL LETTER QUI\nAB99          ; mapped                 ; 13C9          # 8.0  CHEROKEE SMALL LETTER QUO\nAB9A          ; mapped                 ; 13CA          # 8.0  CHEROKEE SMALL LETTER QUU\nAB9B          ; mapped                 ; 13CB          # 8.0  CHEROKEE SMALL LETTER QUV\nAB9C          ; mapped                 ; 13CC          # 8.0  CHEROKEE SMALL LETTER SA\nAB9D          ; mapped                 ; 13CD          # 8.0  CHEROKEE SMALL LETTER S\nAB9E          ; mapped                 ; 13CE          # 8.0  CHEROKEE SMALL LETTER SE\nAB9F          ; mapped                 ; 13CF          # 8.0  CHEROKEE SMALL LETTER SI\nABA0          ; mapped                 ; 13D0          # 8.0  CHEROKEE SMALL LETTER SO\nABA1          ; mapped                 ; 13D1          # 8.0  CHEROKEE SMALL LETTER SU\nABA2          ; mapped                 ; 13D2          # 8.0  CHEROKEE SMALL LETTER SV\nABA3          ; mapped                 ; 13D3          # 8.0  CHEROKEE SMALL LETTER DA\nABA4          ; mapped                 ; 13D4          # 8.0  CHEROKEE SMALL LETTER TA\nABA5          ; mapped                 ; 13D5          # 8.0  CHEROKEE SMALL LETTER DE\nABA6          ; mapped                 ; 13D6          # 8.0  CHEROKEE SMALL LETTER TE\nABA7          ; mapped                 ; 13D7          # 8.0  CHEROKEE SMALL LETTER DI\nABA8          ; mapped                 ; 13D8          # 8.0  CHEROKEE SMALL LETTER TI\nABA9          ; mapped                 ; 13D9          # 8.0  CHEROKEE SMALL LETTER DO\nABAA          ; mapped                 ; 13DA          # 8.0  CHEROKEE SMALL LETTER DU\nABAB          ; mapped                 ; 13DB          # 8.0  CHEROKEE SMALL LETTER DV\nABAC          ; mapped                 ; 13DC          # 8.0  CHEROKEE SMALL LETTER DLA\nABAD          ; mapped                 ; 13DD          # 8.0  CHEROKEE SMALL LETTER TLA\nABAE          ; mapped                 ; 13DE          # 8.0  CHEROKEE SMALL LETTER TLE\nABAF          ; mapped                 ; 13DF          # 8.0  CHEROKEE SMALL LETTER TLI\nABB0          ; mapped                 ; 13E0          # 8.0  CHEROKEE SMALL LETTER TLO\nABB1          ; mapped                 ; 13E1          # 8.0  CHEROKEE SMALL LETTER TLU\nABB2          ; mapped                 ; 13E2          # 8.0  CHEROKEE SMALL LETTER TLV\nABB3          ; mapped                 ; 13E3          # 8.0  CHEROKEE SMALL LETTER TSA\nABB4          ; mapped                 ; 13E4          # 8.0  CHEROKEE SMALL LETTER TSE\nABB5          ; mapped                 ; 13E5          # 8.0  CHEROKEE SMALL LETTER TSI\nABB6          ; mapped                 ; 13E6          # 8.0  CHEROKEE SMALL LETTER TSO\nABB7          ; mapped                 ; 13E7          # 8.0  CHEROKEE SMALL LETTER TSU\nABB8          ; mapped                 ; 13E8          # 8.0  CHEROKEE SMALL LETTER TSV\nABB9          ; mapped                 ; 13E9          # 8.0  CHEROKEE SMALL LETTER WA\nABBA          ; mapped                 ; 13EA          # 8.0  CHEROKEE SMALL LETTER WE\nABBB          ; mapped                 ; 13EB          # 8.0  CHEROKEE SMALL LETTER WI\nABBC          ; mapped                 ; 13EC          # 8.0  CHEROKEE SMALL LETTER WO\nABBD          ; mapped                 ; 13ED          # 8.0  CHEROKEE SMALL LETTER WU\nABBE          ; mapped                 ; 13EE          # 8.0  CHEROKEE SMALL LETTER WV\nABBF          ; mapped                 ; 13EF          # 8.0  CHEROKEE SMALL LETTER YA\nABC0..ABEA    ; valid                                  # 5.2  MEETEI MAYEK LETTER KOK..MEETEI MAYEK VOWEL SIGN NUNG\nABEB          ; valid                  ;      ; NV8    # 5.2  MEETEI MAYEK CHEIKHEI\nABEC..ABED    ; valid                                  # 5.2  MEETEI MAYEK LUM IYEK..MEETEI MAYEK APUN IYEK\nABEE..ABEF    ; disallowed                             # NA   <reserved-ABEE>..<reserved-ABEF>\nABF0..ABF9    ; valid                                  # 5.2  MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DIGIT NINE\nABFA..ABFF    ; disallowed                             # NA   <reserved-ABFA>..<reserved-ABFF>\nAC00..D7A3    ; valid                                  # 2.0  HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH\nD7A4..D7AF    ; disallowed                             # NA   <reserved-D7A4>..<reserved-D7AF>\nD7B0..D7C6    ; valid                  ;      ; NV8    # 5.2  HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E\nD7C7..D7CA    ; disallowed                             # NA   <reserved-D7C7>..<reserved-D7CA>\nD7CB..D7FB    ; valid                  ;      ; NV8    # 5.2  HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH\nD7FC..D7FF    ; disallowed                             # NA   <reserved-D7FC>..<reserved-D7FF>\nD800..DFFF    ; disallowed                             # 2.0  <surrogate-D800>..<surrogate-DFFF>\nE000..F8FF    ; disallowed                             # 1.1  <private-use-E000>..<private-use-F8FF>\nF900          ; mapped                 ; 8C48          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F900\nF901          ; mapped                 ; 66F4          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F901\nF902          ; mapped                 ; 8ECA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F902\nF903          ; mapped                 ; 8CC8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F903\nF904          ; mapped                 ; 6ED1          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F904\nF905          ; mapped                 ; 4E32          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F905\nF906          ; mapped                 ; 53E5          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F906\nF907..F908    ; mapped                 ; 9F9C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F907..CJK COMPATIBILITY IDEOGRAPH-F908\nF909          ; mapped                 ; 5951          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F909\nF90A          ; mapped                 ; 91D1          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F90A\nF90B          ; mapped                 ; 5587          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F90B\nF90C          ; mapped                 ; 5948          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F90C\nF90D          ; mapped                 ; 61F6          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F90D\nF90E          ; mapped                 ; 7669          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F90E\nF90F          ; mapped                 ; 7F85          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F90F\nF910          ; mapped                 ; 863F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F910\nF911          ; mapped                 ; 87BA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F911\nF912          ; mapped                 ; 88F8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F912\nF913          ; mapped                 ; 908F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F913\nF914          ; mapped                 ; 6A02          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F914\nF915          ; mapped                 ; 6D1B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F915\nF916          ; mapped                 ; 70D9          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F916\nF917          ; mapped                 ; 73DE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F917\nF918          ; mapped                 ; 843D          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F918\nF919          ; mapped                 ; 916A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F919\nF91A          ; mapped                 ; 99F1          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F91A\nF91B          ; mapped                 ; 4E82          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F91B\nF91C          ; mapped                 ; 5375          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F91C\nF91D          ; mapped                 ; 6B04          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F91D\nF91E          ; mapped                 ; 721B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F91E\nF91F          ; mapped                 ; 862D          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F91F\nF920          ; mapped                 ; 9E1E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F920\nF921          ; mapped                 ; 5D50          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F921\nF922          ; mapped                 ; 6FEB          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F922\nF923          ; mapped                 ; 85CD          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F923\nF924          ; mapped                 ; 8964          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F924\nF925          ; mapped                 ; 62C9          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F925\nF926          ; mapped                 ; 81D8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F926\nF927          ; mapped                 ; 881F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F927\nF928          ; mapped                 ; 5ECA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F928\nF929          ; mapped                 ; 6717          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F929\nF92A          ; mapped                 ; 6D6A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F92A\nF92B          ; mapped                 ; 72FC          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F92B\nF92C          ; mapped                 ; 90CE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F92C\nF92D          ; mapped                 ; 4F86          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F92D\nF92E          ; mapped                 ; 51B7          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F92E\nF92F          ; mapped                 ; 52DE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F92F\nF930          ; mapped                 ; 64C4          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F930\nF931          ; mapped                 ; 6AD3          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F931\nF932          ; mapped                 ; 7210          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F932\nF933          ; mapped                 ; 76E7          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F933\nF934          ; mapped                 ; 8001          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F934\nF935          ; mapped                 ; 8606          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F935\nF936          ; mapped                 ; 865C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F936\nF937          ; mapped                 ; 8DEF          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F937\nF938          ; mapped                 ; 9732          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F938\nF939          ; mapped                 ; 9B6F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F939\nF93A          ; mapped                 ; 9DFA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F93A\nF93B          ; mapped                 ; 788C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F93B\nF93C          ; mapped                 ; 797F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F93C\nF93D          ; mapped                 ; 7DA0          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F93D\nF93E          ; mapped                 ; 83C9          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F93E\nF93F          ; mapped                 ; 9304          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F93F\nF940          ; mapped                 ; 9E7F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F940\nF941          ; mapped                 ; 8AD6          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F941\nF942          ; mapped                 ; 58DF          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F942\nF943          ; mapped                 ; 5F04          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F943\nF944          ; mapped                 ; 7C60          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F944\nF945          ; mapped                 ; 807E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F945\nF946          ; mapped                 ; 7262          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F946\nF947          ; mapped                 ; 78CA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F947\nF948          ; mapped                 ; 8CC2          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F948\nF949          ; mapped                 ; 96F7          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F949\nF94A          ; mapped                 ; 58D8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F94A\nF94B          ; mapped                 ; 5C62          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F94B\nF94C          ; mapped                 ; 6A13          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F94C\nF94D          ; mapped                 ; 6DDA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F94D\nF94E          ; mapped                 ; 6F0F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F94E\nF94F          ; mapped                 ; 7D2F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F94F\nF950          ; mapped                 ; 7E37          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F950\nF951          ; mapped                 ; 964B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F951\nF952          ; mapped                 ; 52D2          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F952\nF953          ; mapped                 ; 808B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F953\nF954          ; mapped                 ; 51DC          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F954\nF955          ; mapped                 ; 51CC          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F955\nF956          ; mapped                 ; 7A1C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F956\nF957          ; mapped                 ; 7DBE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F957\nF958          ; mapped                 ; 83F1          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F958\nF959          ; mapped                 ; 9675          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F959\nF95A          ; mapped                 ; 8B80          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F95A\nF95B          ; mapped                 ; 62CF          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F95B\nF95C          ; mapped                 ; 6A02          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F95C\nF95D          ; mapped                 ; 8AFE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F95D\nF95E          ; mapped                 ; 4E39          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F95E\nF95F          ; mapped                 ; 5BE7          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F95F\nF960          ; mapped                 ; 6012          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F960\nF961          ; mapped                 ; 7387          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F961\nF962          ; mapped                 ; 7570          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F962\nF963          ; mapped                 ; 5317          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F963\nF964          ; mapped                 ; 78FB          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F964\nF965          ; mapped                 ; 4FBF          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F965\nF966          ; mapped                 ; 5FA9          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F966\nF967          ; mapped                 ; 4E0D          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F967\nF968          ; mapped                 ; 6CCC          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F968\nF969          ; mapped                 ; 6578          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F969\nF96A          ; mapped                 ; 7D22          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F96A\nF96B          ; mapped                 ; 53C3          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F96B\nF96C          ; mapped                 ; 585E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F96C\nF96D          ; mapped                 ; 7701          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F96D\nF96E          ; mapped                 ; 8449          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F96E\nF96F          ; mapped                 ; 8AAA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F96F\nF970          ; mapped                 ; 6BBA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F970\nF971          ; mapped                 ; 8FB0          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F971\nF972          ; mapped                 ; 6C88          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F972\nF973          ; mapped                 ; 62FE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F973\nF974          ; mapped                 ; 82E5          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F974\nF975          ; mapped                 ; 63A0          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F975\nF976          ; mapped                 ; 7565          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F976\nF977          ; mapped                 ; 4EAE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F977\nF978          ; mapped                 ; 5169          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F978\nF979          ; mapped                 ; 51C9          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F979\nF97A          ; mapped                 ; 6881          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F97A\nF97B          ; mapped                 ; 7CE7          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F97B\nF97C          ; mapped                 ; 826F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F97C\nF97D          ; mapped                 ; 8AD2          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F97D\nF97E          ; mapped                 ; 91CF          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F97E\nF97F          ; mapped                 ; 52F5          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F97F\nF980          ; mapped                 ; 5442          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F980\nF981          ; mapped                 ; 5973          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F981\nF982          ; mapped                 ; 5EEC          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F982\nF983          ; mapped                 ; 65C5          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F983\nF984          ; mapped                 ; 6FFE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F984\nF985          ; mapped                 ; 792A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F985\nF986          ; mapped                 ; 95AD          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F986\nF987          ; mapped                 ; 9A6A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F987\nF988          ; mapped                 ; 9E97          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F988\nF989          ; mapped                 ; 9ECE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F989\nF98A          ; mapped                 ; 529B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F98A\nF98B          ; mapped                 ; 66C6          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F98B\nF98C          ; mapped                 ; 6B77          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F98C\nF98D          ; mapped                 ; 8F62          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F98D\nF98E          ; mapped                 ; 5E74          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F98E\nF98F          ; mapped                 ; 6190          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F98F\nF990          ; mapped                 ; 6200          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F990\nF991          ; mapped                 ; 649A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F991\nF992          ; mapped                 ; 6F23          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F992\nF993          ; mapped                 ; 7149          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F993\nF994          ; mapped                 ; 7489          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F994\nF995          ; mapped                 ; 79CA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F995\nF996          ; mapped                 ; 7DF4          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F996\nF997          ; mapped                 ; 806F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F997\nF998          ; mapped                 ; 8F26          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F998\nF999          ; mapped                 ; 84EE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F999\nF99A          ; mapped                 ; 9023          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F99A\nF99B          ; mapped                 ; 934A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F99B\nF99C          ; mapped                 ; 5217          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F99C\nF99D          ; mapped                 ; 52A3          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F99D\nF99E          ; mapped                 ; 54BD          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F99E\nF99F          ; mapped                 ; 70C8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F99F\nF9A0          ; mapped                 ; 88C2          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A0\nF9A1          ; mapped                 ; 8AAA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A1\nF9A2          ; mapped                 ; 5EC9          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A2\nF9A3          ; mapped                 ; 5FF5          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A3\nF9A4          ; mapped                 ; 637B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A4\nF9A5          ; mapped                 ; 6BAE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A5\nF9A6          ; mapped                 ; 7C3E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A6\nF9A7          ; mapped                 ; 7375          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A7\nF9A8          ; mapped                 ; 4EE4          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A8\nF9A9          ; mapped                 ; 56F9          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9A9\nF9AA          ; mapped                 ; 5BE7          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9AA\nF9AB          ; mapped                 ; 5DBA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9AB\nF9AC          ; mapped                 ; 601C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9AC\nF9AD          ; mapped                 ; 73B2          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9AD\nF9AE          ; mapped                 ; 7469          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9AE\nF9AF          ; mapped                 ; 7F9A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9AF\nF9B0          ; mapped                 ; 8046          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B0\nF9B1          ; mapped                 ; 9234          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B1\nF9B2          ; mapped                 ; 96F6          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B2\nF9B3          ; mapped                 ; 9748          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B3\nF9B4          ; mapped                 ; 9818          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B4\nF9B5          ; mapped                 ; 4F8B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B5\nF9B6          ; mapped                 ; 79AE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B6\nF9B7          ; mapped                 ; 91B4          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B7\nF9B8          ; mapped                 ; 96B8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B8\nF9B9          ; mapped                 ; 60E1          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9B9\nF9BA          ; mapped                 ; 4E86          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9BA\nF9BB          ; mapped                 ; 50DA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9BB\nF9BC          ; mapped                 ; 5BEE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9BC\nF9BD          ; mapped                 ; 5C3F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9BD\nF9BE          ; mapped                 ; 6599          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9BE\nF9BF          ; mapped                 ; 6A02          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9BF\nF9C0          ; mapped                 ; 71CE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C0\nF9C1          ; mapped                 ; 7642          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C1\nF9C2          ; mapped                 ; 84FC          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C2\nF9C3          ; mapped                 ; 907C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C3\nF9C4          ; mapped                 ; 9F8D          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C4\nF9C5          ; mapped                 ; 6688          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C5\nF9C6          ; mapped                 ; 962E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C6\nF9C7          ; mapped                 ; 5289          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C7\nF9C8          ; mapped                 ; 677B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C8\nF9C9          ; mapped                 ; 67F3          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9C9\nF9CA          ; mapped                 ; 6D41          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9CA\nF9CB          ; mapped                 ; 6E9C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9CB\nF9CC          ; mapped                 ; 7409          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9CC\nF9CD          ; mapped                 ; 7559          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9CD\nF9CE          ; mapped                 ; 786B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9CE\nF9CF          ; mapped                 ; 7D10          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9CF\nF9D0          ; mapped                 ; 985E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D0\nF9D1          ; mapped                 ; 516D          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D1\nF9D2          ; mapped                 ; 622E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D2\nF9D3          ; mapped                 ; 9678          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D3\nF9D4          ; mapped                 ; 502B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D4\nF9D5          ; mapped                 ; 5D19          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D5\nF9D6          ; mapped                 ; 6DEA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D6\nF9D7          ; mapped                 ; 8F2A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D7\nF9D8          ; mapped                 ; 5F8B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D8\nF9D9          ; mapped                 ; 6144          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9D9\nF9DA          ; mapped                 ; 6817          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9DA\nF9DB          ; mapped                 ; 7387          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9DB\nF9DC          ; mapped                 ; 9686          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9DC\nF9DD          ; mapped                 ; 5229          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9DD\nF9DE          ; mapped                 ; 540F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9DE\nF9DF          ; mapped                 ; 5C65          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9DF\nF9E0          ; mapped                 ; 6613          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E0\nF9E1          ; mapped                 ; 674E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E1\nF9E2          ; mapped                 ; 68A8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E2\nF9E3          ; mapped                 ; 6CE5          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E3\nF9E4          ; mapped                 ; 7406          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E4\nF9E5          ; mapped                 ; 75E2          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E5\nF9E6          ; mapped                 ; 7F79          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E6\nF9E7          ; mapped                 ; 88CF          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E7\nF9E8          ; mapped                 ; 88E1          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E8\nF9E9          ; mapped                 ; 91CC          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9E9\nF9EA          ; mapped                 ; 96E2          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9EA\nF9EB          ; mapped                 ; 533F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9EB\nF9EC          ; mapped                 ; 6EBA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9EC\nF9ED          ; mapped                 ; 541D          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9ED\nF9EE          ; mapped                 ; 71D0          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9EE\nF9EF          ; mapped                 ; 7498          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9EF\nF9F0          ; mapped                 ; 85FA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F0\nF9F1          ; mapped                 ; 96A3          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F1\nF9F2          ; mapped                 ; 9C57          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F2\nF9F3          ; mapped                 ; 9E9F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F3\nF9F4          ; mapped                 ; 6797          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F4\nF9F5          ; mapped                 ; 6DCB          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F5\nF9F6          ; mapped                 ; 81E8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F6\nF9F7          ; mapped                 ; 7ACB          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F7\nF9F8          ; mapped                 ; 7B20          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F8\nF9F9          ; mapped                 ; 7C92          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9F9\nF9FA          ; mapped                 ; 72C0          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9FA\nF9FB          ; mapped                 ; 7099          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9FB\nF9FC          ; mapped                 ; 8B58          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9FC\nF9FD          ; mapped                 ; 4EC0          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9FD\nF9FE          ; mapped                 ; 8336          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9FE\nF9FF          ; mapped                 ; 523A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-F9FF\nFA00          ; mapped                 ; 5207          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA00\nFA01          ; mapped                 ; 5EA6          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA01\nFA02          ; mapped                 ; 62D3          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA02\nFA03          ; mapped                 ; 7CD6          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA03\nFA04          ; mapped                 ; 5B85          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA04\nFA05          ; mapped                 ; 6D1E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA05\nFA06          ; mapped                 ; 66B4          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA06\nFA07          ; mapped                 ; 8F3B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA07\nFA08          ; mapped                 ; 884C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA08\nFA09          ; mapped                 ; 964D          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA09\nFA0A          ; mapped                 ; 898B          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA0A\nFA0B          ; mapped                 ; 5ED3          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA0B\nFA0C          ; mapped                 ; 5140          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA0C\nFA0D          ; mapped                 ; 55C0          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA0D\nFA0E..FA0F    ; valid                                  # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA0E..CJK COMPATIBILITY IDEOGRAPH-FA0F\nFA10          ; mapped                 ; 585A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA10\nFA11          ; valid                                  # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA11\nFA12          ; mapped                 ; 6674          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA12\nFA13..FA14    ; valid                                  # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA13..CJK COMPATIBILITY IDEOGRAPH-FA14\nFA15          ; mapped                 ; 51DE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA15\nFA16          ; mapped                 ; 732A          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA16\nFA17          ; mapped                 ; 76CA          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA17\nFA18          ; mapped                 ; 793C          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA18\nFA19          ; mapped                 ; 795E          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA19\nFA1A          ; mapped                 ; 7965          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA1A\nFA1B          ; mapped                 ; 798F          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA1B\nFA1C          ; mapped                 ; 9756          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA1C\nFA1D          ; mapped                 ; 7CBE          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA1D\nFA1E          ; mapped                 ; 7FBD          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA1E\nFA1F          ; valid                                  # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA1F\nFA20          ; mapped                 ; 8612          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA20\nFA21          ; valid                                  # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA21\nFA22          ; mapped                 ; 8AF8          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA22\nFA23..FA24    ; valid                                  # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA23..CJK COMPATIBILITY IDEOGRAPH-FA24\nFA25          ; mapped                 ; 9038          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA25\nFA26          ; mapped                 ; 90FD          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA26\nFA27..FA29    ; valid                                  # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA27..CJK COMPATIBILITY IDEOGRAPH-FA29\nFA2A          ; mapped                 ; 98EF          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA2A\nFA2B          ; mapped                 ; 98FC          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA2B\nFA2C          ; mapped                 ; 9928          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA2C\nFA2D          ; mapped                 ; 9DB4          # 1.1  CJK COMPATIBILITY IDEOGRAPH-FA2D\nFA2E          ; mapped                 ; 90DE          # 6.1  CJK COMPATIBILITY IDEOGRAPH-FA2E\nFA2F          ; mapped                 ; 96B7          # 6.1  CJK COMPATIBILITY IDEOGRAPH-FA2F\nFA30          ; mapped                 ; 4FAE          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA30\nFA31          ; mapped                 ; 50E7          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA31\nFA32          ; mapped                 ; 514D          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA32\nFA33          ; mapped                 ; 52C9          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA33\nFA34          ; mapped                 ; 52E4          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA34\nFA35          ; mapped                 ; 5351          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA35\nFA36          ; mapped                 ; 559D          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA36\nFA37          ; mapped                 ; 5606          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA37\nFA38          ; mapped                 ; 5668          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA38\nFA39          ; mapped                 ; 5840          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA39\nFA3A          ; mapped                 ; 58A8          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA3A\nFA3B          ; mapped                 ; 5C64          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA3B\nFA3C          ; mapped                 ; 5C6E          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA3C\nFA3D          ; mapped                 ; 6094          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA3D\nFA3E          ; mapped                 ; 6168          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA3E\nFA3F          ; mapped                 ; 618E          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA3F\nFA40          ; mapped                 ; 61F2          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA40\nFA41          ; mapped                 ; 654F          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA41\nFA42          ; mapped                 ; 65E2          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA42\nFA43          ; mapped                 ; 6691          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA43\nFA44          ; mapped                 ; 6885          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA44\nFA45          ; mapped                 ; 6D77          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA45\nFA46          ; mapped                 ; 6E1A          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA46\nFA47          ; mapped                 ; 6F22          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA47\nFA48          ; mapped                 ; 716E          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA48\nFA49          ; mapped                 ; 722B          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA49\nFA4A          ; mapped                 ; 7422          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA4A\nFA4B          ; mapped                 ; 7891          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA4B\nFA4C          ; mapped                 ; 793E          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA4C\nFA4D          ; mapped                 ; 7949          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA4D\nFA4E          ; mapped                 ; 7948          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA4E\nFA4F          ; mapped                 ; 7950          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA4F\nFA50          ; mapped                 ; 7956          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA50\nFA51          ; mapped                 ; 795D          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA51\nFA52          ; mapped                 ; 798D          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA52\nFA53          ; mapped                 ; 798E          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA53\nFA54          ; mapped                 ; 7A40          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA54\nFA55          ; mapped                 ; 7A81          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA55\nFA56          ; mapped                 ; 7BC0          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA56\nFA57          ; mapped                 ; 7DF4          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA57\nFA58          ; mapped                 ; 7E09          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA58\nFA59          ; mapped                 ; 7E41          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA59\nFA5A          ; mapped                 ; 7F72          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA5A\nFA5B          ; mapped                 ; 8005          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA5B\nFA5C          ; mapped                 ; 81ED          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA5C\nFA5D..FA5E    ; mapped                 ; 8279          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA5D..CJK COMPATIBILITY IDEOGRAPH-FA5E\nFA5F          ; mapped                 ; 8457          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA5F\nFA60          ; mapped                 ; 8910          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA60\nFA61          ; mapped                 ; 8996          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA61\nFA62          ; mapped                 ; 8B01          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA62\nFA63          ; mapped                 ; 8B39          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA63\nFA64          ; mapped                 ; 8CD3          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA64\nFA65          ; mapped                 ; 8D08          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA65\nFA66          ; mapped                 ; 8FB6          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA66\nFA67          ; mapped                 ; 9038          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA67\nFA68          ; mapped                 ; 96E3          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA68\nFA69          ; mapped                 ; 97FF          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA69\nFA6A          ; mapped                 ; 983B          # 3.2  CJK COMPATIBILITY IDEOGRAPH-FA6A\nFA6B          ; mapped                 ; 6075          # 5.2  CJK COMPATIBILITY IDEOGRAPH-FA6B\nFA6C          ; mapped                 ; 242EE         # 5.2  CJK COMPATIBILITY IDEOGRAPH-FA6C\nFA6D          ; mapped                 ; 8218          # 5.2  CJK COMPATIBILITY IDEOGRAPH-FA6D\nFA6E..FA6F    ; disallowed                             # NA   <reserved-FA6E>..<reserved-FA6F>\nFA70          ; mapped                 ; 4E26          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA70\nFA71          ; mapped                 ; 51B5          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA71\nFA72          ; mapped                 ; 5168          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA72\nFA73          ; mapped                 ; 4F80          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA73\nFA74          ; mapped                 ; 5145          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA74\nFA75          ; mapped                 ; 5180          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA75\nFA76          ; mapped                 ; 52C7          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA76\nFA77          ; mapped                 ; 52FA          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA77\nFA78          ; mapped                 ; 559D          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA78\nFA79          ; mapped                 ; 5555          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA79\nFA7A          ; mapped                 ; 5599          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA7A\nFA7B          ; mapped                 ; 55E2          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA7B\nFA7C          ; mapped                 ; 585A          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA7C\nFA7D          ; mapped                 ; 58B3          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA7D\nFA7E          ; mapped                 ; 5944          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA7E\nFA7F          ; mapped                 ; 5954          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA7F\nFA80          ; mapped                 ; 5A62          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA80\nFA81          ; mapped                 ; 5B28          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA81\nFA82          ; mapped                 ; 5ED2          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA82\nFA83          ; mapped                 ; 5ED9          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA83\nFA84          ; mapped                 ; 5F69          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA84\nFA85          ; mapped                 ; 5FAD          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA85\nFA86          ; mapped                 ; 60D8          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA86\nFA87          ; mapped                 ; 614E          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA87\nFA88          ; mapped                 ; 6108          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA88\nFA89          ; mapped                 ; 618E          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA89\nFA8A          ; mapped                 ; 6160          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA8A\nFA8B          ; mapped                 ; 61F2          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA8B\nFA8C          ; mapped                 ; 6234          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA8C\nFA8D          ; mapped                 ; 63C4          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA8D\nFA8E          ; mapped                 ; 641C          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA8E\nFA8F          ; mapped                 ; 6452          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA8F\nFA90          ; mapped                 ; 6556          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA90\nFA91          ; mapped                 ; 6674          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA91\nFA92          ; mapped                 ; 6717          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA92\nFA93          ; mapped                 ; 671B          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA93\nFA94          ; mapped                 ; 6756          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA94\nFA95          ; mapped                 ; 6B79          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA95\nFA96          ; mapped                 ; 6BBA          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA96\nFA97          ; mapped                 ; 6D41          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA97\nFA98          ; mapped                 ; 6EDB          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA98\nFA99          ; mapped                 ; 6ECB          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA99\nFA9A          ; mapped                 ; 6F22          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA9A\nFA9B          ; mapped                 ; 701E          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA9B\nFA9C          ; mapped                 ; 716E          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA9C\nFA9D          ; mapped                 ; 77A7          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA9D\nFA9E          ; mapped                 ; 7235          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA9E\nFA9F          ; mapped                 ; 72AF          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FA9F\nFAA0          ; mapped                 ; 732A          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA0\nFAA1          ; mapped                 ; 7471          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA1\nFAA2          ; mapped                 ; 7506          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA2\nFAA3          ; mapped                 ; 753B          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA3\nFAA4          ; mapped                 ; 761D          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA4\nFAA5          ; mapped                 ; 761F          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA5\nFAA6          ; mapped                 ; 76CA          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA6\nFAA7          ; mapped                 ; 76DB          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA7\nFAA8          ; mapped                 ; 76F4          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA8\nFAA9          ; mapped                 ; 774A          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAA9\nFAAA          ; mapped                 ; 7740          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAAA\nFAAB          ; mapped                 ; 78CC          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAAB\nFAAC          ; mapped                 ; 7AB1          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAAC\nFAAD          ; mapped                 ; 7BC0          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAAD\nFAAE          ; mapped                 ; 7C7B          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAAE\nFAAF          ; mapped                 ; 7D5B          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAAF\nFAB0          ; mapped                 ; 7DF4          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB0\nFAB1          ; mapped                 ; 7F3E          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB1\nFAB2          ; mapped                 ; 8005          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB2\nFAB3          ; mapped                 ; 8352          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB3\nFAB4          ; mapped                 ; 83EF          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB4\nFAB5          ; mapped                 ; 8779          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB5\nFAB6          ; mapped                 ; 8941          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB6\nFAB7          ; mapped                 ; 8986          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB7\nFAB8          ; mapped                 ; 8996          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB8\nFAB9          ; mapped                 ; 8ABF          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAB9\nFABA          ; mapped                 ; 8AF8          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FABA\nFABB          ; mapped                 ; 8ACB          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FABB\nFABC          ; mapped                 ; 8B01          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FABC\nFABD          ; mapped                 ; 8AFE          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FABD\nFABE          ; mapped                 ; 8AED          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FABE\nFABF          ; mapped                 ; 8B39          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FABF\nFAC0          ; mapped                 ; 8B8A          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC0\nFAC1          ; mapped                 ; 8D08          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC1\nFAC2          ; mapped                 ; 8F38          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC2\nFAC3          ; mapped                 ; 9072          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC3\nFAC4          ; mapped                 ; 9199          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC4\nFAC5          ; mapped                 ; 9276          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC5\nFAC6          ; mapped                 ; 967C          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC6\nFAC7          ; mapped                 ; 96E3          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC7\nFAC8          ; mapped                 ; 9756          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC8\nFAC9          ; mapped                 ; 97DB          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAC9\nFACA          ; mapped                 ; 97FF          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FACA\nFACB          ; mapped                 ; 980B          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FACB\nFACC          ; mapped                 ; 983B          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FACC\nFACD          ; mapped                 ; 9B12          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FACD\nFACE          ; mapped                 ; 9F9C          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FACE\nFACF          ; mapped                 ; 2284A         # 4.1  CJK COMPATIBILITY IDEOGRAPH-FACF\nFAD0          ; mapped                 ; 22844         # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD0\nFAD1          ; mapped                 ; 233D5         # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD1\nFAD2          ; mapped                 ; 3B9D          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD2\nFAD3          ; mapped                 ; 4018          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD3\nFAD4          ; mapped                 ; 4039          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD4\nFAD5          ; mapped                 ; 25249         # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD5\nFAD6          ; mapped                 ; 25CD0         # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD6\nFAD7          ; mapped                 ; 27ED3         # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD7\nFAD8          ; mapped                 ; 9F43          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD8\nFAD9          ; mapped                 ; 9F8E          # 4.1  CJK COMPATIBILITY IDEOGRAPH-FAD9\nFADA..FAFF    ; disallowed                             # NA   <reserved-FADA>..<reserved-FAFF>\nFB00          ; mapped                 ; 0066 0066     # 1.1  LATIN SMALL LIGATURE FF\nFB01          ; mapped                 ; 0066 0069     # 1.1  LATIN SMALL LIGATURE FI\nFB02          ; mapped                 ; 0066 006C     # 1.1  LATIN SMALL LIGATURE FL\nFB03          ; mapped                 ; 0066 0066 0069 #1.1  LATIN SMALL LIGATURE FFI\nFB04          ; mapped                 ; 0066 0066 006C #1.1  LATIN SMALL LIGATURE FFL\nFB05..FB06    ; mapped                 ; 0073 0074     # 1.1  LATIN SMALL LIGATURE LONG S T..LATIN SMALL LIGATURE ST\nFB07..FB12    ; disallowed                             # NA   <reserved-FB07>..<reserved-FB12>\nFB13          ; mapped                 ; 0574 0576     # 1.1  ARMENIAN SMALL LIGATURE MEN NOW\nFB14          ; mapped                 ; 0574 0565     # 1.1  ARMENIAN SMALL LIGATURE MEN ECH\nFB15          ; mapped                 ; 0574 056B     # 1.1  ARMENIAN SMALL LIGATURE MEN INI\nFB16          ; mapped                 ; 057E 0576     # 1.1  ARMENIAN SMALL LIGATURE VEW NOW\nFB17          ; mapped                 ; 0574 056D     # 1.1  ARMENIAN SMALL LIGATURE MEN XEH\nFB18..FB1C    ; disallowed                             # NA   <reserved-FB18>..<reserved-FB1C>\nFB1D          ; mapped                 ; 05D9 05B4     # 3.0  HEBREW LETTER YOD WITH HIRIQ\nFB1E          ; valid                                  # 1.1  HEBREW POINT JUDEO-SPANISH VARIKA\nFB1F          ; mapped                 ; 05F2 05B7     # 1.1  HEBREW LIGATURE YIDDISH YOD YOD PATAH\nFB20          ; mapped                 ; 05E2          # 1.1  HEBREW LETTER ALTERNATIVE AYIN\nFB21          ; mapped                 ; 05D0          # 1.1  HEBREW LETTER WIDE ALEF\nFB22          ; mapped                 ; 05D3          # 1.1  HEBREW LETTER WIDE DALET\nFB23          ; mapped                 ; 05D4          # 1.1  HEBREW LETTER WIDE HE\nFB24          ; mapped                 ; 05DB          # 1.1  HEBREW LETTER WIDE KAF\nFB25          ; mapped                 ; 05DC          # 1.1  HEBREW LETTER WIDE LAMED\nFB26          ; mapped                 ; 05DD          # 1.1  HEBREW LETTER WIDE FINAL MEM\nFB27          ; mapped                 ; 05E8          # 1.1  HEBREW LETTER WIDE RESH\nFB28          ; mapped                 ; 05EA          # 1.1  HEBREW LETTER WIDE TAV\nFB29          ; disallowed_STD3_mapped ; 002B          # 1.1  HEBREW LETTER ALTERNATIVE PLUS SIGN\nFB2A          ; mapped                 ; 05E9 05C1     # 1.1  HEBREW LETTER SHIN WITH SHIN DOT\nFB2B          ; mapped                 ; 05E9 05C2     # 1.1  HEBREW LETTER SHIN WITH SIN DOT\nFB2C          ; mapped                 ; 05E9 05BC 05C1 #1.1  HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT\nFB2D          ; mapped                 ; 05E9 05BC 05C2 #1.1  HEBREW LETTER SHIN WITH DAGESH AND SIN DOT\nFB2E          ; mapped                 ; 05D0 05B7     # 1.1  HEBREW LETTER ALEF WITH PATAH\nFB2F          ; mapped                 ; 05D0 05B8     # 1.1  HEBREW LETTER ALEF WITH QAMATS\nFB30          ; mapped                 ; 05D0 05BC     # 1.1  HEBREW LETTER ALEF WITH MAPIQ\nFB31          ; mapped                 ; 05D1 05BC     # 1.1  HEBREW LETTER BET WITH DAGESH\nFB32          ; mapped                 ; 05D2 05BC     # 1.1  HEBREW LETTER GIMEL WITH DAGESH\nFB33          ; mapped                 ; 05D3 05BC     # 1.1  HEBREW LETTER DALET WITH DAGESH\nFB34          ; mapped                 ; 05D4 05BC     # 1.1  HEBREW LETTER HE WITH MAPIQ\nFB35          ; mapped                 ; 05D5 05BC     # 1.1  HEBREW LETTER VAV WITH DAGESH\nFB36          ; mapped                 ; 05D6 05BC     # 1.1  HEBREW LETTER ZAYIN WITH DAGESH\nFB37          ; disallowed                             # NA   <reserved-FB37>\nFB38          ; mapped                 ; 05D8 05BC     # 1.1  HEBREW LETTER TET WITH DAGESH\nFB39          ; mapped                 ; 05D9 05BC     # 1.1  HEBREW LETTER YOD WITH DAGESH\nFB3A          ; mapped                 ; 05DA 05BC     # 1.1  HEBREW LETTER FINAL KAF WITH DAGESH\nFB3B          ; mapped                 ; 05DB 05BC     # 1.1  HEBREW LETTER KAF WITH DAGESH\nFB3C          ; mapped                 ; 05DC 05BC     # 1.1  HEBREW LETTER LAMED WITH DAGESH\nFB3D          ; disallowed                             # NA   <reserved-FB3D>\nFB3E          ; mapped                 ; 05DE 05BC     # 1.1  HEBREW LETTER MEM WITH DAGESH\nFB3F          ; disallowed                             # NA   <reserved-FB3F>\nFB40          ; mapped                 ; 05E0 05BC     # 1.1  HEBREW LETTER NUN WITH DAGESH\nFB41          ; mapped                 ; 05E1 05BC     # 1.1  HEBREW LETTER SAMEKH WITH DAGESH\nFB42          ; disallowed                             # NA   <reserved-FB42>\nFB43          ; mapped                 ; 05E3 05BC     # 1.1  HEBREW LETTER FINAL PE WITH DAGESH\nFB44          ; mapped                 ; 05E4 05BC     # 1.1  HEBREW LETTER PE WITH DAGESH\nFB45          ; disallowed                             # NA   <reserved-FB45>\nFB46          ; mapped                 ; 05E6 05BC     # 1.1  HEBREW LETTER TSADI WITH DAGESH\nFB47          ; mapped                 ; 05E7 05BC     # 1.1  HEBREW LETTER QOF WITH DAGESH\nFB48          ; mapped                 ; 05E8 05BC     # 1.1  HEBREW LETTER RESH WITH DAGESH\nFB49          ; mapped                 ; 05E9 05BC     # 1.1  HEBREW LETTER SHIN WITH DAGESH\nFB4A          ; mapped                 ; 05EA 05BC     # 1.1  HEBREW LETTER TAV WITH DAGESH\nFB4B          ; mapped                 ; 05D5 05B9     # 1.1  HEBREW LETTER VAV WITH HOLAM\nFB4C          ; mapped                 ; 05D1 05BF     # 1.1  HEBREW LETTER BET WITH RAFE\nFB4D          ; mapped                 ; 05DB 05BF     # 1.1  HEBREW LETTER KAF WITH RAFE\nFB4E          ; mapped                 ; 05E4 05BF     # 1.1  HEBREW LETTER PE WITH RAFE\nFB4F          ; mapped                 ; 05D0 05DC     # 1.1  HEBREW LIGATURE ALEF LAMED\nFB50..FB51    ; mapped                 ; 0671          # 1.1  ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER ALEF WASLA FINAL FORM\nFB52..FB55    ; mapped                 ; 067B          # 1.1  ARABIC LETTER BEEH ISOLATED FORM..ARABIC LETTER BEEH MEDIAL FORM\nFB56..FB59    ; mapped                 ; 067E          # 1.1  ARABIC LETTER PEH ISOLATED FORM..ARABIC LETTER PEH MEDIAL FORM\nFB5A..FB5D    ; mapped                 ; 0680          # 1.1  ARABIC LETTER BEHEH ISOLATED FORM..ARABIC LETTER BEHEH MEDIAL FORM\nFB5E..FB61    ; mapped                 ; 067A          # 1.1  ARABIC LETTER TTEHEH ISOLATED FORM..ARABIC LETTER TTEHEH MEDIAL FORM\nFB62..FB65    ; mapped                 ; 067F          # 1.1  ARABIC LETTER TEHEH ISOLATED FORM..ARABIC LETTER TEHEH MEDIAL FORM\nFB66..FB69    ; mapped                 ; 0679          # 1.1  ARABIC LETTER TTEH ISOLATED FORM..ARABIC LETTER TTEH MEDIAL FORM\nFB6A..FB6D    ; mapped                 ; 06A4          # 1.1  ARABIC LETTER VEH ISOLATED FORM..ARABIC LETTER VEH MEDIAL FORM\nFB6E..FB71    ; mapped                 ; 06A6          # 1.1  ARABIC LETTER PEHEH ISOLATED FORM..ARABIC LETTER PEHEH MEDIAL FORM\nFB72..FB75    ; mapped                 ; 0684          # 1.1  ARABIC LETTER DYEH ISOLATED FORM..ARABIC LETTER DYEH MEDIAL FORM\nFB76..FB79    ; mapped                 ; 0683          # 1.1  ARABIC LETTER NYEH ISOLATED FORM..ARABIC LETTER NYEH MEDIAL FORM\nFB7A..FB7D    ; mapped                 ; 0686          # 1.1  ARABIC LETTER TCHEH ISOLATED FORM..ARABIC LETTER TCHEH MEDIAL FORM\nFB7E..FB81    ; mapped                 ; 0687          # 1.1  ARABIC LETTER TCHEHEH ISOLATED FORM..ARABIC LETTER TCHEHEH MEDIAL FORM\nFB82..FB83    ; mapped                 ; 068D          # 1.1  ARABIC LETTER DDAHAL ISOLATED FORM..ARABIC LETTER DDAHAL FINAL FORM\nFB84..FB85    ; mapped                 ; 068C          # 1.1  ARABIC LETTER DAHAL ISOLATED FORM..ARABIC LETTER DAHAL FINAL FORM\nFB86..FB87    ; mapped                 ; 068E          # 1.1  ARABIC LETTER DUL ISOLATED FORM..ARABIC LETTER DUL FINAL FORM\nFB88..FB89    ; mapped                 ; 0688          # 1.1  ARABIC LETTER DDAL ISOLATED FORM..ARABIC LETTER DDAL FINAL FORM\nFB8A..FB8B    ; mapped                 ; 0698          # 1.1  ARABIC LETTER JEH ISOLATED FORM..ARABIC LETTER JEH FINAL FORM\nFB8C..FB8D    ; mapped                 ; 0691          # 1.1  ARABIC LETTER RREH ISOLATED FORM..ARABIC LETTER RREH FINAL FORM\nFB8E..FB91    ; mapped                 ; 06A9          # 1.1  ARABIC LETTER KEHEH ISOLATED FORM..ARABIC LETTER KEHEH MEDIAL FORM\nFB92..FB95    ; mapped                 ; 06AF          # 1.1  ARABIC LETTER GAF ISOLATED FORM..ARABIC LETTER GAF MEDIAL FORM\nFB96..FB99    ; mapped                 ; 06B3          # 1.1  ARABIC LETTER GUEH ISOLATED FORM..ARABIC LETTER GUEH MEDIAL FORM\nFB9A..FB9D    ; mapped                 ; 06B1          # 1.1  ARABIC LETTER NGOEH ISOLATED FORM..ARABIC LETTER NGOEH MEDIAL FORM\nFB9E..FB9F    ; mapped                 ; 06BA          # 1.1  ARABIC LETTER NOON GHUNNA ISOLATED FORM..ARABIC LETTER NOON GHUNNA FINAL FORM\nFBA0..FBA3    ; mapped                 ; 06BB          # 1.1  ARABIC LETTER RNOON ISOLATED FORM..ARABIC LETTER RNOON MEDIAL FORM\nFBA4..FBA5    ; mapped                 ; 06C0          # 1.1  ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM..ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM\nFBA6..FBA9    ; mapped                 ; 06C1          # 1.1  ARABIC LETTER HEH GOAL ISOLATED FORM..ARABIC LETTER HEH GOAL MEDIAL FORM\nFBAA..FBAD    ; mapped                 ; 06BE          # 1.1  ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM..ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM\nFBAE..FBAF    ; mapped                 ; 06D2          # 1.1  ARABIC LETTER YEH BARREE ISOLATED FORM..ARABIC LETTER YEH BARREE FINAL FORM\nFBB0..FBB1    ; mapped                 ; 06D3          # 1.1  ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM\nFBB2..FBC1    ; valid                  ;      ; NV8    # 6.0  ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL SMALL TAH BELOW\nFBC2          ; valid                  ;      ; NV8    # 14.0 ARABIC SYMBOL WASLA ABOVE\nFBC3..FBD2    ; disallowed                             # NA   <reserved-FBC3>..<reserved-FBD2>\nFBD3..FBD6    ; mapped                 ; 06AD          # 1.1  ARABIC LETTER NG ISOLATED FORM..ARABIC LETTER NG MEDIAL FORM\nFBD7..FBD8    ; mapped                 ; 06C7          # 1.1  ARABIC LETTER U ISOLATED FORM..ARABIC LETTER U FINAL FORM\nFBD9..FBDA    ; mapped                 ; 06C6          # 1.1  ARABIC LETTER OE ISOLATED FORM..ARABIC LETTER OE FINAL FORM\nFBDB..FBDC    ; mapped                 ; 06C8          # 1.1  ARABIC LETTER YU ISOLATED FORM..ARABIC LETTER YU FINAL FORM\nFBDD          ; mapped                 ; 06C7 0674     # 1.1  ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM\nFBDE..FBDF    ; mapped                 ; 06CB          # 1.1  ARABIC LETTER VE ISOLATED FORM..ARABIC LETTER VE FINAL FORM\nFBE0..FBE1    ; mapped                 ; 06C5          # 1.1  ARABIC LETTER KIRGHIZ OE ISOLATED FORM..ARABIC LETTER KIRGHIZ OE FINAL FORM\nFBE2..FBE3    ; mapped                 ; 06C9          # 1.1  ARABIC LETTER KIRGHIZ YU ISOLATED FORM..ARABIC LETTER KIRGHIZ YU FINAL FORM\nFBE4..FBE7    ; mapped                 ; 06D0          # 1.1  ARABIC LETTER E ISOLATED FORM..ARABIC LETTER E MEDIAL FORM\nFBE8..FBE9    ; mapped                 ; 0649          # 1.1  ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM..ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM\nFBEA..FBEB    ; mapped                 ; 0626 0627     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM..ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM\nFBEC..FBED    ; mapped                 ; 0626 06D5     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM..ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM\nFBEE..FBEF    ; mapped                 ; 0626 0648     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM..ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM\nFBF0..FBF1    ; mapped                 ; 0626 06C7     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM..ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM\nFBF2..FBF3    ; mapped                 ; 0626 06C6     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM..ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM\nFBF4..FBF5    ; mapped                 ; 0626 06C8     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM..ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM\nFBF6..FBF8    ; mapped                 ; 0626 06D0     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM..ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM\nFBF9..FBFB    ; mapped                 ; 0626 0649     # 1.1  ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM..ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM\nFBFC..FBFF    ; mapped                 ; 06CC          # 1.1  ARABIC LETTER FARSI YEH ISOLATED FORM..ARABIC LETTER FARSI YEH MEDIAL FORM\nFC00          ; mapped                 ; 0626 062C     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM\nFC01          ; mapped                 ; 0626 062D     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM\nFC02          ; mapped                 ; 0626 0645     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM\nFC03          ; mapped                 ; 0626 0649     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM\nFC04          ; mapped                 ; 0626 064A     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM\nFC05          ; mapped                 ; 0628 062C     # 1.1  ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM\nFC06          ; mapped                 ; 0628 062D     # 1.1  ARABIC LIGATURE BEH WITH HAH ISOLATED FORM\nFC07          ; mapped                 ; 0628 062E     # 1.1  ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM\nFC08          ; mapped                 ; 0628 0645     # 1.1  ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM\nFC09          ; mapped                 ; 0628 0649     # 1.1  ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM\nFC0A          ; mapped                 ; 0628 064A     # 1.1  ARABIC LIGATURE BEH WITH YEH ISOLATED FORM\nFC0B          ; mapped                 ; 062A 062C     # 1.1  ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM\nFC0C          ; mapped                 ; 062A 062D     # 1.1  ARABIC LIGATURE TEH WITH HAH ISOLATED FORM\nFC0D          ; mapped                 ; 062A 062E     # 1.1  ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM\nFC0E          ; mapped                 ; 062A 0645     # 1.1  ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM\nFC0F          ; mapped                 ; 062A 0649     # 1.1  ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM\nFC10          ; mapped                 ; 062A 064A     # 1.1  ARABIC LIGATURE TEH WITH YEH ISOLATED FORM\nFC11          ; mapped                 ; 062B 062C     # 1.1  ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM\nFC12          ; mapped                 ; 062B 0645     # 1.1  ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM\nFC13          ; mapped                 ; 062B 0649     # 1.1  ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM\nFC14          ; mapped                 ; 062B 064A     # 1.1  ARABIC LIGATURE THEH WITH YEH ISOLATED FORM\nFC15          ; mapped                 ; 062C 062D     # 1.1  ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM\nFC16          ; mapped                 ; 062C 0645     # 1.1  ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM\nFC17          ; mapped                 ; 062D 062C     # 1.1  ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM\nFC18          ; mapped                 ; 062D 0645     # 1.1  ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM\nFC19          ; mapped                 ; 062E 062C     # 1.1  ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM\nFC1A          ; mapped                 ; 062E 062D     # 1.1  ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM\nFC1B          ; mapped                 ; 062E 0645     # 1.1  ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM\nFC1C          ; mapped                 ; 0633 062C     # 1.1  ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM\nFC1D          ; mapped                 ; 0633 062D     # 1.1  ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM\nFC1E          ; mapped                 ; 0633 062E     # 1.1  ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM\nFC1F          ; mapped                 ; 0633 0645     # 1.1  ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM\nFC20          ; mapped                 ; 0635 062D     # 1.1  ARABIC LIGATURE SAD WITH HAH ISOLATED FORM\nFC21          ; mapped                 ; 0635 0645     # 1.1  ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM\nFC22          ; mapped                 ; 0636 062C     # 1.1  ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM\nFC23          ; mapped                 ; 0636 062D     # 1.1  ARABIC LIGATURE DAD WITH HAH ISOLATED FORM\nFC24          ; mapped                 ; 0636 062E     # 1.1  ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM\nFC25          ; mapped                 ; 0636 0645     # 1.1  ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM\nFC26          ; mapped                 ; 0637 062D     # 1.1  ARABIC LIGATURE TAH WITH HAH ISOLATED FORM\nFC27          ; mapped                 ; 0637 0645     # 1.1  ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM\nFC28          ; mapped                 ; 0638 0645     # 1.1  ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM\nFC29          ; mapped                 ; 0639 062C     # 1.1  ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM\nFC2A          ; mapped                 ; 0639 0645     # 1.1  ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM\nFC2B          ; mapped                 ; 063A 062C     # 1.1  ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM\nFC2C          ; mapped                 ; 063A 0645     # 1.1  ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM\nFC2D          ; mapped                 ; 0641 062C     # 1.1  ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM\nFC2E          ; mapped                 ; 0641 062D     # 1.1  ARABIC LIGATURE FEH WITH HAH ISOLATED FORM\nFC2F          ; mapped                 ; 0641 062E     # 1.1  ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM\nFC30          ; mapped                 ; 0641 0645     # 1.1  ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM\nFC31          ; mapped                 ; 0641 0649     # 1.1  ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM\nFC32          ; mapped                 ; 0641 064A     # 1.1  ARABIC LIGATURE FEH WITH YEH ISOLATED FORM\nFC33          ; mapped                 ; 0642 062D     # 1.1  ARABIC LIGATURE QAF WITH HAH ISOLATED FORM\nFC34          ; mapped                 ; 0642 0645     # 1.1  ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM\nFC35          ; mapped                 ; 0642 0649     # 1.1  ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM\nFC36          ; mapped                 ; 0642 064A     # 1.1  ARABIC LIGATURE QAF WITH YEH ISOLATED FORM\nFC37          ; mapped                 ; 0643 0627     # 1.1  ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM\nFC38          ; mapped                 ; 0643 062C     # 1.1  ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM\nFC39          ; mapped                 ; 0643 062D     # 1.1  ARABIC LIGATURE KAF WITH HAH ISOLATED FORM\nFC3A          ; mapped                 ; 0643 062E     # 1.1  ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM\nFC3B          ; mapped                 ; 0643 0644     # 1.1  ARABIC LIGATURE KAF WITH LAM ISOLATED FORM\nFC3C          ; mapped                 ; 0643 0645     # 1.1  ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM\nFC3D          ; mapped                 ; 0643 0649     # 1.1  ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM\nFC3E          ; mapped                 ; 0643 064A     # 1.1  ARABIC LIGATURE KAF WITH YEH ISOLATED FORM\nFC3F          ; mapped                 ; 0644 062C     # 1.1  ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM\nFC40          ; mapped                 ; 0644 062D     # 1.1  ARABIC LIGATURE LAM WITH HAH ISOLATED FORM\nFC41          ; mapped                 ; 0644 062E     # 1.1  ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM\nFC42          ; mapped                 ; 0644 0645     # 1.1  ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM\nFC43          ; mapped                 ; 0644 0649     # 1.1  ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM\nFC44          ; mapped                 ; 0644 064A     # 1.1  ARABIC LIGATURE LAM WITH YEH ISOLATED FORM\nFC45          ; mapped                 ; 0645 062C     # 1.1  ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM\nFC46          ; mapped                 ; 0645 062D     # 1.1  ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM\nFC47          ; mapped                 ; 0645 062E     # 1.1  ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM\nFC48          ; mapped                 ; 0645 0645     # 1.1  ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM\nFC49          ; mapped                 ; 0645 0649     # 1.1  ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM\nFC4A          ; mapped                 ; 0645 064A     # 1.1  ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM\nFC4B          ; mapped                 ; 0646 062C     # 1.1  ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM\nFC4C          ; mapped                 ; 0646 062D     # 1.1  ARABIC LIGATURE NOON WITH HAH ISOLATED FORM\nFC4D          ; mapped                 ; 0646 062E     # 1.1  ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM\nFC4E          ; mapped                 ; 0646 0645     # 1.1  ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM\nFC4F          ; mapped                 ; 0646 0649     # 1.1  ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM\nFC50          ; mapped                 ; 0646 064A     # 1.1  ARABIC LIGATURE NOON WITH YEH ISOLATED FORM\nFC51          ; mapped                 ; 0647 062C     # 1.1  ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM\nFC52          ; mapped                 ; 0647 0645     # 1.1  ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM\nFC53          ; mapped                 ; 0647 0649     # 1.1  ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM\nFC54          ; mapped                 ; 0647 064A     # 1.1  ARABIC LIGATURE HEH WITH YEH ISOLATED FORM\nFC55          ; mapped                 ; 064A 062C     # 1.1  ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM\nFC56          ; mapped                 ; 064A 062D     # 1.1  ARABIC LIGATURE YEH WITH HAH ISOLATED FORM\nFC57          ; mapped                 ; 064A 062E     # 1.1  ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM\nFC58          ; mapped                 ; 064A 0645     # 1.1  ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM\nFC59          ; mapped                 ; 064A 0649     # 1.1  ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM\nFC5A          ; mapped                 ; 064A 064A     # 1.1  ARABIC LIGATURE YEH WITH YEH ISOLATED FORM\nFC5B          ; mapped                 ; 0630 0670     # 1.1  ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM\nFC5C          ; mapped                 ; 0631 0670     # 1.1  ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM\nFC5D          ; mapped                 ; 0649 0670     # 1.1  ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM\nFC5E          ; disallowed_STD3_mapped ; 0020 064C 0651 #1.1  ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM\nFC5F          ; disallowed_STD3_mapped ; 0020 064D 0651 #1.1  ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM\nFC60          ; disallowed_STD3_mapped ; 0020 064E 0651 #1.1  ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM\nFC61          ; disallowed_STD3_mapped ; 0020 064F 0651 #1.1  ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM\nFC62          ; disallowed_STD3_mapped ; 0020 0650 0651 #1.1  ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM\nFC63          ; disallowed_STD3_mapped ; 0020 0651 0670 #1.1  ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM\nFC64          ; mapped                 ; 0626 0631     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM\nFC65          ; mapped                 ; 0626 0632     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM\nFC66          ; mapped                 ; 0626 0645     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM\nFC67          ; mapped                 ; 0626 0646     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM\nFC68          ; mapped                 ; 0626 0649     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM\nFC69          ; mapped                 ; 0626 064A     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM\nFC6A          ; mapped                 ; 0628 0631     # 1.1  ARABIC LIGATURE BEH WITH REH FINAL FORM\nFC6B          ; mapped                 ; 0628 0632     # 1.1  ARABIC LIGATURE BEH WITH ZAIN FINAL FORM\nFC6C          ; mapped                 ; 0628 0645     # 1.1  ARABIC LIGATURE BEH WITH MEEM FINAL FORM\nFC6D          ; mapped                 ; 0628 0646     # 1.1  ARABIC LIGATURE BEH WITH NOON FINAL FORM\nFC6E          ; mapped                 ; 0628 0649     # 1.1  ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM\nFC6F          ; mapped                 ; 0628 064A     # 1.1  ARABIC LIGATURE BEH WITH YEH FINAL FORM\nFC70          ; mapped                 ; 062A 0631     # 1.1  ARABIC LIGATURE TEH WITH REH FINAL FORM\nFC71          ; mapped                 ; 062A 0632     # 1.1  ARABIC LIGATURE TEH WITH ZAIN FINAL FORM\nFC72          ; mapped                 ; 062A 0645     # 1.1  ARABIC LIGATURE TEH WITH MEEM FINAL FORM\nFC73          ; mapped                 ; 062A 0646     # 1.1  ARABIC LIGATURE TEH WITH NOON FINAL FORM\nFC74          ; mapped                 ; 062A 0649     # 1.1  ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM\nFC75          ; mapped                 ; 062A 064A     # 1.1  ARABIC LIGATURE TEH WITH YEH FINAL FORM\nFC76          ; mapped                 ; 062B 0631     # 1.1  ARABIC LIGATURE THEH WITH REH FINAL FORM\nFC77          ; mapped                 ; 062B 0632     # 1.1  ARABIC LIGATURE THEH WITH ZAIN FINAL FORM\nFC78          ; mapped                 ; 062B 0645     # 1.1  ARABIC LIGATURE THEH WITH MEEM FINAL FORM\nFC79          ; mapped                 ; 062B 0646     # 1.1  ARABIC LIGATURE THEH WITH NOON FINAL FORM\nFC7A          ; mapped                 ; 062B 0649     # 1.1  ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM\nFC7B          ; mapped                 ; 062B 064A     # 1.1  ARABIC LIGATURE THEH WITH YEH FINAL FORM\nFC7C          ; mapped                 ; 0641 0649     # 1.1  ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM\nFC7D          ; mapped                 ; 0641 064A     # 1.1  ARABIC LIGATURE FEH WITH YEH FINAL FORM\nFC7E          ; mapped                 ; 0642 0649     # 1.1  ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM\nFC7F          ; mapped                 ; 0642 064A     # 1.1  ARABIC LIGATURE QAF WITH YEH FINAL FORM\nFC80          ; mapped                 ; 0643 0627     # 1.1  ARABIC LIGATURE KAF WITH ALEF FINAL FORM\nFC81          ; mapped                 ; 0643 0644     # 1.1  ARABIC LIGATURE KAF WITH LAM FINAL FORM\nFC82          ; mapped                 ; 0643 0645     # 1.1  ARABIC LIGATURE KAF WITH MEEM FINAL FORM\nFC83          ; mapped                 ; 0643 0649     # 1.1  ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM\nFC84          ; mapped                 ; 0643 064A     # 1.1  ARABIC LIGATURE KAF WITH YEH FINAL FORM\nFC85          ; mapped                 ; 0644 0645     # 1.1  ARABIC LIGATURE LAM WITH MEEM FINAL FORM\nFC86          ; mapped                 ; 0644 0649     # 1.1  ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM\nFC87          ; mapped                 ; 0644 064A     # 1.1  ARABIC LIGATURE LAM WITH YEH FINAL FORM\nFC88          ; mapped                 ; 0645 0627     # 1.1  ARABIC LIGATURE MEEM WITH ALEF FINAL FORM\nFC89          ; mapped                 ; 0645 0645     # 1.1  ARABIC LIGATURE MEEM WITH MEEM FINAL FORM\nFC8A          ; mapped                 ; 0646 0631     # 1.1  ARABIC LIGATURE NOON WITH REH FINAL FORM\nFC8B          ; mapped                 ; 0646 0632     # 1.1  ARABIC LIGATURE NOON WITH ZAIN FINAL FORM\nFC8C          ; mapped                 ; 0646 0645     # 1.1  ARABIC LIGATURE NOON WITH MEEM FINAL FORM\nFC8D          ; mapped                 ; 0646 0646     # 1.1  ARABIC LIGATURE NOON WITH NOON FINAL FORM\nFC8E          ; mapped                 ; 0646 0649     # 1.1  ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM\nFC8F          ; mapped                 ; 0646 064A     # 1.1  ARABIC LIGATURE NOON WITH YEH FINAL FORM\nFC90          ; mapped                 ; 0649 0670     # 1.1  ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM\nFC91          ; mapped                 ; 064A 0631     # 1.1  ARABIC LIGATURE YEH WITH REH FINAL FORM\nFC92          ; mapped                 ; 064A 0632     # 1.1  ARABIC LIGATURE YEH WITH ZAIN FINAL FORM\nFC93          ; mapped                 ; 064A 0645     # 1.1  ARABIC LIGATURE YEH WITH MEEM FINAL FORM\nFC94          ; mapped                 ; 064A 0646     # 1.1  ARABIC LIGATURE YEH WITH NOON FINAL FORM\nFC95          ; mapped                 ; 064A 0649     # 1.1  ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM\nFC96          ; mapped                 ; 064A 064A     # 1.1  ARABIC LIGATURE YEH WITH YEH FINAL FORM\nFC97          ; mapped                 ; 0626 062C     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM\nFC98          ; mapped                 ; 0626 062D     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM\nFC99          ; mapped                 ; 0626 062E     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM\nFC9A          ; mapped                 ; 0626 0645     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM\nFC9B          ; mapped                 ; 0626 0647     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM\nFC9C          ; mapped                 ; 0628 062C     # 1.1  ARABIC LIGATURE BEH WITH JEEM INITIAL FORM\nFC9D          ; mapped                 ; 0628 062D     # 1.1  ARABIC LIGATURE BEH WITH HAH INITIAL FORM\nFC9E          ; mapped                 ; 0628 062E     # 1.1  ARABIC LIGATURE BEH WITH KHAH INITIAL FORM\nFC9F          ; mapped                 ; 0628 0645     # 1.1  ARABIC LIGATURE BEH WITH MEEM INITIAL FORM\nFCA0          ; mapped                 ; 0628 0647     # 1.1  ARABIC LIGATURE BEH WITH HEH INITIAL FORM\nFCA1          ; mapped                 ; 062A 062C     # 1.1  ARABIC LIGATURE TEH WITH JEEM INITIAL FORM\nFCA2          ; mapped                 ; 062A 062D     # 1.1  ARABIC LIGATURE TEH WITH HAH INITIAL FORM\nFCA3          ; mapped                 ; 062A 062E     # 1.1  ARABIC LIGATURE TEH WITH KHAH INITIAL FORM\nFCA4          ; mapped                 ; 062A 0645     # 1.1  ARABIC LIGATURE TEH WITH MEEM INITIAL FORM\nFCA5          ; mapped                 ; 062A 0647     # 1.1  ARABIC LIGATURE TEH WITH HEH INITIAL FORM\nFCA6          ; mapped                 ; 062B 0645     # 1.1  ARABIC LIGATURE THEH WITH MEEM INITIAL FORM\nFCA7          ; mapped                 ; 062C 062D     # 1.1  ARABIC LIGATURE JEEM WITH HAH INITIAL FORM\nFCA8          ; mapped                 ; 062C 0645     # 1.1  ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM\nFCA9          ; mapped                 ; 062D 062C     # 1.1  ARABIC LIGATURE HAH WITH JEEM INITIAL FORM\nFCAA          ; mapped                 ; 062D 0645     # 1.1  ARABIC LIGATURE HAH WITH MEEM INITIAL FORM\nFCAB          ; mapped                 ; 062E 062C     # 1.1  ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM\nFCAC          ; mapped                 ; 062E 0645     # 1.1  ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM\nFCAD          ; mapped                 ; 0633 062C     # 1.1  ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM\nFCAE          ; mapped                 ; 0633 062D     # 1.1  ARABIC LIGATURE SEEN WITH HAH INITIAL FORM\nFCAF          ; mapped                 ; 0633 062E     # 1.1  ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM\nFCB0          ; mapped                 ; 0633 0645     # 1.1  ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM\nFCB1          ; mapped                 ; 0635 062D     # 1.1  ARABIC LIGATURE SAD WITH HAH INITIAL FORM\nFCB2          ; mapped                 ; 0635 062E     # 1.1  ARABIC LIGATURE SAD WITH KHAH INITIAL FORM\nFCB3          ; mapped                 ; 0635 0645     # 1.1  ARABIC LIGATURE SAD WITH MEEM INITIAL FORM\nFCB4          ; mapped                 ; 0636 062C     # 1.1  ARABIC LIGATURE DAD WITH JEEM INITIAL FORM\nFCB5          ; mapped                 ; 0636 062D     # 1.1  ARABIC LIGATURE DAD WITH HAH INITIAL FORM\nFCB6          ; mapped                 ; 0636 062E     # 1.1  ARABIC LIGATURE DAD WITH KHAH INITIAL FORM\nFCB7          ; mapped                 ; 0636 0645     # 1.1  ARABIC LIGATURE DAD WITH MEEM INITIAL FORM\nFCB8          ; mapped                 ; 0637 062D     # 1.1  ARABIC LIGATURE TAH WITH HAH INITIAL FORM\nFCB9          ; mapped                 ; 0638 0645     # 1.1  ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM\nFCBA          ; mapped                 ; 0639 062C     # 1.1  ARABIC LIGATURE AIN WITH JEEM INITIAL FORM\nFCBB          ; mapped                 ; 0639 0645     # 1.1  ARABIC LIGATURE AIN WITH MEEM INITIAL FORM\nFCBC          ; mapped                 ; 063A 062C     # 1.1  ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM\nFCBD          ; mapped                 ; 063A 0645     # 1.1  ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM\nFCBE          ; mapped                 ; 0641 062C     # 1.1  ARABIC LIGATURE FEH WITH JEEM INITIAL FORM\nFCBF          ; mapped                 ; 0641 062D     # 1.1  ARABIC LIGATURE FEH WITH HAH INITIAL FORM\nFCC0          ; mapped                 ; 0641 062E     # 1.1  ARABIC LIGATURE FEH WITH KHAH INITIAL FORM\nFCC1          ; mapped                 ; 0641 0645     # 1.1  ARABIC LIGATURE FEH WITH MEEM INITIAL FORM\nFCC2          ; mapped                 ; 0642 062D     # 1.1  ARABIC LIGATURE QAF WITH HAH INITIAL FORM\nFCC3          ; mapped                 ; 0642 0645     # 1.1  ARABIC LIGATURE QAF WITH MEEM INITIAL FORM\nFCC4          ; mapped                 ; 0643 062C     # 1.1  ARABIC LIGATURE KAF WITH JEEM INITIAL FORM\nFCC5          ; mapped                 ; 0643 062D     # 1.1  ARABIC LIGATURE KAF WITH HAH INITIAL FORM\nFCC6          ; mapped                 ; 0643 062E     # 1.1  ARABIC LIGATURE KAF WITH KHAH INITIAL FORM\nFCC7          ; mapped                 ; 0643 0644     # 1.1  ARABIC LIGATURE KAF WITH LAM INITIAL FORM\nFCC8          ; mapped                 ; 0643 0645     # 1.1  ARABIC LIGATURE KAF WITH MEEM INITIAL FORM\nFCC9          ; mapped                 ; 0644 062C     # 1.1  ARABIC LIGATURE LAM WITH JEEM INITIAL FORM\nFCCA          ; mapped                 ; 0644 062D     # 1.1  ARABIC LIGATURE LAM WITH HAH INITIAL FORM\nFCCB          ; mapped                 ; 0644 062E     # 1.1  ARABIC LIGATURE LAM WITH KHAH INITIAL FORM\nFCCC          ; mapped                 ; 0644 0645     # 1.1  ARABIC LIGATURE LAM WITH MEEM INITIAL FORM\nFCCD          ; mapped                 ; 0644 0647     # 1.1  ARABIC LIGATURE LAM WITH HEH INITIAL FORM\nFCCE          ; mapped                 ; 0645 062C     # 1.1  ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM\nFCCF          ; mapped                 ; 0645 062D     # 1.1  ARABIC LIGATURE MEEM WITH HAH INITIAL FORM\nFCD0          ; mapped                 ; 0645 062E     # 1.1  ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM\nFCD1          ; mapped                 ; 0645 0645     # 1.1  ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM\nFCD2          ; mapped                 ; 0646 062C     # 1.1  ARABIC LIGATURE NOON WITH JEEM INITIAL FORM\nFCD3          ; mapped                 ; 0646 062D     # 1.1  ARABIC LIGATURE NOON WITH HAH INITIAL FORM\nFCD4          ; mapped                 ; 0646 062E     # 1.1  ARABIC LIGATURE NOON WITH KHAH INITIAL FORM\nFCD5          ; mapped                 ; 0646 0645     # 1.1  ARABIC LIGATURE NOON WITH MEEM INITIAL FORM\nFCD6          ; mapped                 ; 0646 0647     # 1.1  ARABIC LIGATURE NOON WITH HEH INITIAL FORM\nFCD7          ; mapped                 ; 0647 062C     # 1.1  ARABIC LIGATURE HEH WITH JEEM INITIAL FORM\nFCD8          ; mapped                 ; 0647 0645     # 1.1  ARABIC LIGATURE HEH WITH MEEM INITIAL FORM\nFCD9          ; mapped                 ; 0647 0670     # 1.1  ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM\nFCDA          ; mapped                 ; 064A 062C     # 1.1  ARABIC LIGATURE YEH WITH JEEM INITIAL FORM\nFCDB          ; mapped                 ; 064A 062D     # 1.1  ARABIC LIGATURE YEH WITH HAH INITIAL FORM\nFCDC          ; mapped                 ; 064A 062E     # 1.1  ARABIC LIGATURE YEH WITH KHAH INITIAL FORM\nFCDD          ; mapped                 ; 064A 0645     # 1.1  ARABIC LIGATURE YEH WITH MEEM INITIAL FORM\nFCDE          ; mapped                 ; 064A 0647     # 1.1  ARABIC LIGATURE YEH WITH HEH INITIAL FORM\nFCDF          ; mapped                 ; 0626 0645     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM\nFCE0          ; mapped                 ; 0626 0647     # 1.1  ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM\nFCE1          ; mapped                 ; 0628 0645     # 1.1  ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM\nFCE2          ; mapped                 ; 0628 0647     # 1.1  ARABIC LIGATURE BEH WITH HEH MEDIAL FORM\nFCE3          ; mapped                 ; 062A 0645     # 1.1  ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM\nFCE4          ; mapped                 ; 062A 0647     # 1.1  ARABIC LIGATURE TEH WITH HEH MEDIAL FORM\nFCE5          ; mapped                 ; 062B 0645     # 1.1  ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM\nFCE6          ; mapped                 ; 062B 0647     # 1.1  ARABIC LIGATURE THEH WITH HEH MEDIAL FORM\nFCE7          ; mapped                 ; 0633 0645     # 1.1  ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM\nFCE8          ; mapped                 ; 0633 0647     # 1.1  ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM\nFCE9          ; mapped                 ; 0634 0645     # 1.1  ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM\nFCEA          ; mapped                 ; 0634 0647     # 1.1  ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM\nFCEB          ; mapped                 ; 0643 0644     # 1.1  ARABIC LIGATURE KAF WITH LAM MEDIAL FORM\nFCEC          ; mapped                 ; 0643 0645     # 1.1  ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM\nFCED          ; mapped                 ; 0644 0645     # 1.1  ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM\nFCEE          ; mapped                 ; 0646 0645     # 1.1  ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM\nFCEF          ; mapped                 ; 0646 0647     # 1.1  ARABIC LIGATURE NOON WITH HEH MEDIAL FORM\nFCF0          ; mapped                 ; 064A 0645     # 1.1  ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM\nFCF1          ; mapped                 ; 064A 0647     # 1.1  ARABIC LIGATURE YEH WITH HEH MEDIAL FORM\nFCF2          ; mapped                 ; 0640 064E 0651 #1.1  ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM\nFCF3          ; mapped                 ; 0640 064F 0651 #1.1  ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM\nFCF4          ; mapped                 ; 0640 0650 0651 #1.1  ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM\nFCF5          ; mapped                 ; 0637 0649     # 1.1  ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM\nFCF6          ; mapped                 ; 0637 064A     # 1.1  ARABIC LIGATURE TAH WITH YEH ISOLATED FORM\nFCF7          ; mapped                 ; 0639 0649     # 1.1  ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM\nFCF8          ; mapped                 ; 0639 064A     # 1.1  ARABIC LIGATURE AIN WITH YEH ISOLATED FORM\nFCF9          ; mapped                 ; 063A 0649     # 1.1  ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM\nFCFA          ; mapped                 ; 063A 064A     # 1.1  ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM\nFCFB          ; mapped                 ; 0633 0649     # 1.1  ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM\nFCFC          ; mapped                 ; 0633 064A     # 1.1  ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM\nFCFD          ; mapped                 ; 0634 0649     # 1.1  ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM\nFCFE          ; mapped                 ; 0634 064A     # 1.1  ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM\nFCFF          ; mapped                 ; 062D 0649     # 1.1  ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM\nFD00          ; mapped                 ; 062D 064A     # 1.1  ARABIC LIGATURE HAH WITH YEH ISOLATED FORM\nFD01          ; mapped                 ; 062C 0649     # 1.1  ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM\nFD02          ; mapped                 ; 062C 064A     # 1.1  ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM\nFD03          ; mapped                 ; 062E 0649     # 1.1  ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM\nFD04          ; mapped                 ; 062E 064A     # 1.1  ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM\nFD05          ; mapped                 ; 0635 0649     # 1.1  ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM\nFD06          ; mapped                 ; 0635 064A     # 1.1  ARABIC LIGATURE SAD WITH YEH ISOLATED FORM\nFD07          ; mapped                 ; 0636 0649     # 1.1  ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM\nFD08          ; mapped                 ; 0636 064A     # 1.1  ARABIC LIGATURE DAD WITH YEH ISOLATED FORM\nFD09          ; mapped                 ; 0634 062C     # 1.1  ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM\nFD0A          ; mapped                 ; 0634 062D     # 1.1  ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM\nFD0B          ; mapped                 ; 0634 062E     # 1.1  ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM\nFD0C          ; mapped                 ; 0634 0645     # 1.1  ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM\nFD0D          ; mapped                 ; 0634 0631     # 1.1  ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM\nFD0E          ; mapped                 ; 0633 0631     # 1.1  ARABIC LIGATURE SEEN WITH REH ISOLATED FORM\nFD0F          ; mapped                 ; 0635 0631     # 1.1  ARABIC LIGATURE SAD WITH REH ISOLATED FORM\nFD10          ; mapped                 ; 0636 0631     # 1.1  ARABIC LIGATURE DAD WITH REH ISOLATED FORM\nFD11          ; mapped                 ; 0637 0649     # 1.1  ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM\nFD12          ; mapped                 ; 0637 064A     # 1.1  ARABIC LIGATURE TAH WITH YEH FINAL FORM\nFD13          ; mapped                 ; 0639 0649     # 1.1  ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM\nFD14          ; mapped                 ; 0639 064A     # 1.1  ARABIC LIGATURE AIN WITH YEH FINAL FORM\nFD15          ; mapped                 ; 063A 0649     # 1.1  ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM\nFD16          ; mapped                 ; 063A 064A     # 1.1  ARABIC LIGATURE GHAIN WITH YEH FINAL FORM\nFD17          ; mapped                 ; 0633 0649     # 1.1  ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM\nFD18          ; mapped                 ; 0633 064A     # 1.1  ARABIC LIGATURE SEEN WITH YEH FINAL FORM\nFD19          ; mapped                 ; 0634 0649     # 1.1  ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM\nFD1A          ; mapped                 ; 0634 064A     # 1.1  ARABIC LIGATURE SHEEN WITH YEH FINAL FORM\nFD1B          ; mapped                 ; 062D 0649     # 1.1  ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM\nFD1C          ; mapped                 ; 062D 064A     # 1.1  ARABIC LIGATURE HAH WITH YEH FINAL FORM\nFD1D          ; mapped                 ; 062C 0649     # 1.1  ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM\nFD1E          ; mapped                 ; 062C 064A     # 1.1  ARABIC LIGATURE JEEM WITH YEH FINAL FORM\nFD1F          ; mapped                 ; 062E 0649     # 1.1  ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM\nFD20          ; mapped                 ; 062E 064A     # 1.1  ARABIC LIGATURE KHAH WITH YEH FINAL FORM\nFD21          ; mapped                 ; 0635 0649     # 1.1  ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM\nFD22          ; mapped                 ; 0635 064A     # 1.1  ARABIC LIGATURE SAD WITH YEH FINAL FORM\nFD23          ; mapped                 ; 0636 0649     # 1.1  ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM\nFD24          ; mapped                 ; 0636 064A     # 1.1  ARABIC LIGATURE DAD WITH YEH FINAL FORM\nFD25          ; mapped                 ; 0634 062C     # 1.1  ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM\nFD26          ; mapped                 ; 0634 062D     # 1.1  ARABIC LIGATURE SHEEN WITH HAH FINAL FORM\nFD27          ; mapped                 ; 0634 062E     # 1.1  ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM\nFD28          ; mapped                 ; 0634 0645     # 1.1  ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM\nFD29          ; mapped                 ; 0634 0631     # 1.1  ARABIC LIGATURE SHEEN WITH REH FINAL FORM\nFD2A          ; mapped                 ; 0633 0631     # 1.1  ARABIC LIGATURE SEEN WITH REH FINAL FORM\nFD2B          ; mapped                 ; 0635 0631     # 1.1  ARABIC LIGATURE SAD WITH REH FINAL FORM\nFD2C          ; mapped                 ; 0636 0631     # 1.1  ARABIC LIGATURE DAD WITH REH FINAL FORM\nFD2D          ; mapped                 ; 0634 062C     # 1.1  ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM\nFD2E          ; mapped                 ; 0634 062D     # 1.1  ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM\nFD2F          ; mapped                 ; 0634 062E     # 1.1  ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM\nFD30          ; mapped                 ; 0634 0645     # 1.1  ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM\nFD31          ; mapped                 ; 0633 0647     # 1.1  ARABIC LIGATURE SEEN WITH HEH INITIAL FORM\nFD32          ; mapped                 ; 0634 0647     # 1.1  ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM\nFD33          ; mapped                 ; 0637 0645     # 1.1  ARABIC LIGATURE TAH WITH MEEM INITIAL FORM\nFD34          ; mapped                 ; 0633 062C     # 1.1  ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM\nFD35          ; mapped                 ; 0633 062D     # 1.1  ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM\nFD36          ; mapped                 ; 0633 062E     # 1.1  ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM\nFD37          ; mapped                 ; 0634 062C     # 1.1  ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM\nFD38          ; mapped                 ; 0634 062D     # 1.1  ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM\nFD39          ; mapped                 ; 0634 062E     # 1.1  ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM\nFD3A          ; mapped                 ; 0637 0645     # 1.1  ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM\nFD3B          ; mapped                 ; 0638 0645     # 1.1  ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM\nFD3C..FD3D    ; mapped                 ; 0627 064B     # 1.1  ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM\nFD3E..FD3F    ; valid                  ;      ; NV8    # 1.1  ORNATE LEFT PARENTHESIS..ORNATE RIGHT PARENTHESIS\nFD40..FD4F    ; valid                  ;      ; NV8    # 14.0 ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH\nFD50          ; mapped                 ; 062A 062C 0645 #1.1  ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM\nFD51..FD52    ; mapped                 ; 062A 062D 062C #1.1  ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM..ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM\nFD53          ; mapped                 ; 062A 062D 0645 #1.1  ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM\nFD54          ; mapped                 ; 062A 062E 0645 #1.1  ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM\nFD55          ; mapped                 ; 062A 0645 062C #1.1  ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM\nFD56          ; mapped                 ; 062A 0645 062D #1.1  ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM\nFD57          ; mapped                 ; 062A 0645 062E #1.1  ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM\nFD58..FD59    ; mapped                 ; 062C 0645 062D #1.1  ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM..ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM\nFD5A          ; mapped                 ; 062D 0645 064A #1.1  ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM\nFD5B          ; mapped                 ; 062D 0645 0649 #1.1  ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM\nFD5C          ; mapped                 ; 0633 062D 062C #1.1  ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM\nFD5D          ; mapped                 ; 0633 062C 062D #1.1  ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM\nFD5E          ; mapped                 ; 0633 062C 0649 #1.1  ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM\nFD5F..FD60    ; mapped                 ; 0633 0645 062D #1.1  ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM..ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM\nFD61          ; mapped                 ; 0633 0645 062C #1.1  ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM\nFD62..FD63    ; mapped                 ; 0633 0645 0645 #1.1  ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM..ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM\nFD64..FD65    ; mapped                 ; 0635 062D 062D #1.1  ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM..ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM\nFD66          ; mapped                 ; 0635 0645 0645 #1.1  ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM\nFD67..FD68    ; mapped                 ; 0634 062D 0645 #1.1  ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM..ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM\nFD69          ; mapped                 ; 0634 062C 064A #1.1  ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM\nFD6A..FD6B    ; mapped                 ; 0634 0645 062E #1.1  ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM..ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM\nFD6C..FD6D    ; mapped                 ; 0634 0645 0645 #1.1  ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM..ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM\nFD6E          ; mapped                 ; 0636 062D 0649 #1.1  ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM\nFD6F..FD70    ; mapped                 ; 0636 062E 0645 #1.1  ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM..ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM\nFD71..FD72    ; mapped                 ; 0637 0645 062D #1.1  ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM..ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM\nFD73          ; mapped                 ; 0637 0645 0645 #1.1  ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM\nFD74          ; mapped                 ; 0637 0645 064A #1.1  ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM\nFD75          ; mapped                 ; 0639 062C 0645 #1.1  ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM\nFD76..FD77    ; mapped                 ; 0639 0645 0645 #1.1  ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM..ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM\nFD78          ; mapped                 ; 0639 0645 0649 #1.1  ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM\nFD79          ; mapped                 ; 063A 0645 0645 #1.1  ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM\nFD7A          ; mapped                 ; 063A 0645 064A #1.1  ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM\nFD7B          ; mapped                 ; 063A 0645 0649 #1.1  ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM\nFD7C..FD7D    ; mapped                 ; 0641 062E 0645 #1.1  ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM..ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM\nFD7E          ; mapped                 ; 0642 0645 062D #1.1  ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM\nFD7F          ; mapped                 ; 0642 0645 0645 #1.1  ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM\nFD80          ; mapped                 ; 0644 062D 0645 #1.1  ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM\nFD81          ; mapped                 ; 0644 062D 064A #1.1  ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM\nFD82          ; mapped                 ; 0644 062D 0649 #1.1  ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM\nFD83..FD84    ; mapped                 ; 0644 062C 062C #1.1  ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM..ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM\nFD85..FD86    ; mapped                 ; 0644 062E 0645 #1.1  ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM..ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM\nFD87..FD88    ; mapped                 ; 0644 0645 062D #1.1  ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM..ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM\nFD89          ; mapped                 ; 0645 062D 062C #1.1  ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM\nFD8A          ; mapped                 ; 0645 062D 0645 #1.1  ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM\nFD8B          ; mapped                 ; 0645 062D 064A #1.1  ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM\nFD8C          ; mapped                 ; 0645 062C 062D #1.1  ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM\nFD8D          ; mapped                 ; 0645 062C 0645 #1.1  ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM\nFD8E          ; mapped                 ; 0645 062E 062C #1.1  ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM\nFD8F          ; mapped                 ; 0645 062E 0645 #1.1  ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM\nFD90..FD91    ; disallowed                             # NA   <reserved-FD90>..<reserved-FD91>\nFD92          ; mapped                 ; 0645 062C 062E #1.1  ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM\nFD93          ; mapped                 ; 0647 0645 062C #1.1  ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM\nFD94          ; mapped                 ; 0647 0645 0645 #1.1  ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM\nFD95          ; mapped                 ; 0646 062D 0645 #1.1  ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM\nFD96          ; mapped                 ; 0646 062D 0649 #1.1  ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM\nFD97..FD98    ; mapped                 ; 0646 062C 0645 #1.1  ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM\nFD99          ; mapped                 ; 0646 062C 0649 #1.1  ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM\nFD9A          ; mapped                 ; 0646 0645 064A #1.1  ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM\nFD9B          ; mapped                 ; 0646 0645 0649 #1.1  ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM\nFD9C..FD9D    ; mapped                 ; 064A 0645 0645 #1.1  ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM..ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM\nFD9E          ; mapped                 ; 0628 062E 064A #1.1  ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM\nFD9F          ; mapped                 ; 062A 062C 064A #1.1  ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM\nFDA0          ; mapped                 ; 062A 062C 0649 #1.1  ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM\nFDA1          ; mapped                 ; 062A 062E 064A #1.1  ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM\nFDA2          ; mapped                 ; 062A 062E 0649 #1.1  ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM\nFDA3          ; mapped                 ; 062A 0645 064A #1.1  ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM\nFDA4          ; mapped                 ; 062A 0645 0649 #1.1  ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM\nFDA5          ; mapped                 ; 062C 0645 064A #1.1  ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM\nFDA6          ; mapped                 ; 062C 062D 0649 #1.1  ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM\nFDA7          ; mapped                 ; 062C 0645 0649 #1.1  ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM\nFDA8          ; mapped                 ; 0633 062E 0649 #1.1  ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM\nFDA9          ; mapped                 ; 0635 062D 064A #1.1  ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM\nFDAA          ; mapped                 ; 0634 062D 064A #1.1  ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM\nFDAB          ; mapped                 ; 0636 062D 064A #1.1  ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM\nFDAC          ; mapped                 ; 0644 062C 064A #1.1  ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM\nFDAD          ; mapped                 ; 0644 0645 064A #1.1  ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM\nFDAE          ; mapped                 ; 064A 062D 064A #1.1  ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM\nFDAF          ; mapped                 ; 064A 062C 064A #1.1  ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM\nFDB0          ; mapped                 ; 064A 0645 064A #1.1  ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM\nFDB1          ; mapped                 ; 0645 0645 064A #1.1  ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM\nFDB2          ; mapped                 ; 0642 0645 064A #1.1  ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM\nFDB3          ; mapped                 ; 0646 062D 064A #1.1  ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM\nFDB4          ; mapped                 ; 0642 0645 062D #1.1  ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM\nFDB5          ; mapped                 ; 0644 062D 0645 #1.1  ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM\nFDB6          ; mapped                 ; 0639 0645 064A #1.1  ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM\nFDB7          ; mapped                 ; 0643 0645 064A #1.1  ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM\nFDB8          ; mapped                 ; 0646 062C 062D #1.1  ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM\nFDB9          ; mapped                 ; 0645 062E 064A #1.1  ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM\nFDBA          ; mapped                 ; 0644 062C 0645 #1.1  ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM\nFDBB          ; mapped                 ; 0643 0645 0645 #1.1  ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM\nFDBC          ; mapped                 ; 0644 062C 0645 #1.1  ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM\nFDBD          ; mapped                 ; 0646 062C 062D #1.1  ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM\nFDBE          ; mapped                 ; 062C 062D 064A #1.1  ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM\nFDBF          ; mapped                 ; 062D 062C 064A #1.1  ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM\nFDC0          ; mapped                 ; 0645 062C 064A #1.1  ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM\nFDC1          ; mapped                 ; 0641 0645 064A #1.1  ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM\nFDC2          ; mapped                 ; 0628 062D 064A #1.1  ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM\nFDC3          ; mapped                 ; 0643 0645 0645 #1.1  ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM\nFDC4          ; mapped                 ; 0639 062C 0645 #1.1  ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM\nFDC5          ; mapped                 ; 0635 0645 0645 #1.1  ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM\nFDC6          ; mapped                 ; 0633 062E 064A #1.1  ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM\nFDC7          ; mapped                 ; 0646 062C 064A #1.1  ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM\nFDC8..FDCE    ; disallowed                             # NA   <reserved-FDC8>..<reserved-FDCE>\nFDCF          ; valid                  ;      ; NV8    # 14.0 ARABIC LIGATURE SALAAMUHU ALAYNAA\nFDD0..FDEF    ; disallowed                             # 3.1  <noncharacter-FDD0>..<noncharacter-FDEF>\nFDF0          ; mapped                 ; 0635 0644 06D2 #1.1  ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM\nFDF1          ; mapped                 ; 0642 0644 06D2 #1.1  ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM\nFDF2          ; mapped                 ; 0627 0644 0644 0647 #1.1 ARABIC LIGATURE ALLAH ISOLATED FORM\nFDF3          ; mapped                 ; 0627 0643 0628 0631 #1.1 ARABIC LIGATURE AKBAR ISOLATED FORM\nFDF4          ; mapped                 ; 0645 062D 0645 062F #1.1 ARABIC LIGATURE MOHAMMAD ISOLATED FORM\nFDF5          ; mapped                 ; 0635 0644 0639 0645 #1.1 ARABIC LIGATURE SALAM ISOLATED FORM\nFDF6          ; mapped                 ; 0631 0633 0648 0644 #1.1 ARABIC LIGATURE RASOUL ISOLATED FORM\nFDF7          ; mapped                 ; 0639 0644 064A 0647 #1.1 ARABIC LIGATURE ALAYHE ISOLATED FORM\nFDF8          ; mapped                 ; 0648 0633 0644 0645 #1.1 ARABIC LIGATURE WASALLAM ISOLATED FORM\nFDF9          ; mapped                 ; 0635 0644 0649 #1.1  ARABIC LIGATURE SALLA ISOLATED FORM\nFDFA          ; disallowed_STD3_mapped ; 0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645 #1.1 ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM\nFDFB          ; disallowed_STD3_mapped ; 062C 0644 0020 062C 0644 0627 0644 0647 #1.1 ARABIC LIGATURE JALLAJALALOUHOU\nFDFC          ; mapped                 ; 0631 06CC 0627 0644 #3.2 RIAL SIGN\nFDFD          ; valid                  ;      ; NV8    # 4.0  ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM\nFDFE..FDFF    ; valid                  ;      ; NV8    # 14.0 ARABIC LIGATURE SUBHAANAHU WA TAAALAA..ARABIC LIGATURE AZZA WA JALL\nFE00..FE0F    ; ignored                                # 3.2  VARIATION SELECTOR-1..VARIATION SELECTOR-16\nFE10          ; disallowed_STD3_mapped ; 002C          # 4.1  PRESENTATION FORM FOR VERTICAL COMMA\nFE11          ; mapped                 ; 3001          # 4.1  PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC COMMA\nFE12          ; disallowed                             # 4.1  PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC FULL STOP\nFE13          ; disallowed_STD3_mapped ; 003A          # 4.1  PRESENTATION FORM FOR VERTICAL COLON\nFE14          ; disallowed_STD3_mapped ; 003B          # 4.1  PRESENTATION FORM FOR VERTICAL SEMICOLON\nFE15          ; disallowed_STD3_mapped ; 0021          # 4.1  PRESENTATION FORM FOR VERTICAL EXCLAMATION MARK\nFE16          ; disallowed_STD3_mapped ; 003F          # 4.1  PRESENTATION FORM FOR VERTICAL QUESTION MARK\nFE17          ; mapped                 ; 3016          # 4.1  PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET\nFE18          ; mapped                 ; 3017          # 4.1  PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET\nFE19          ; disallowed                             # 4.1  PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS\nFE1A..FE1F    ; disallowed                             # NA   <reserved-FE1A>..<reserved-FE1F>\nFE20..FE23    ; valid                                  # 1.1  COMBINING LIGATURE LEFT HALF..COMBINING DOUBLE TILDE RIGHT HALF\nFE24..FE26    ; valid                                  # 5.1  COMBINING MACRON LEFT HALF..COMBINING CONJOINING MACRON\nFE27..FE2D    ; valid                                  # 7.0  COMBINING LIGATURE LEFT HALF BELOW..COMBINING CONJOINING MACRON BELOW\nFE2E..FE2F    ; valid                                  # 8.0  COMBINING CYRILLIC TITLO LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF\nFE30          ; disallowed                             # 1.1  PRESENTATION FORM FOR VERTICAL TWO DOT LEADER\nFE31          ; mapped                 ; 2014          # 1.1  PRESENTATION FORM FOR VERTICAL EM DASH\nFE32          ; mapped                 ; 2013          # 1.1  PRESENTATION FORM FOR VERTICAL EN DASH\nFE33..FE34    ; disallowed_STD3_mapped ; 005F          # 1.1  PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE\nFE35          ; disallowed_STD3_mapped ; 0028          # 1.1  PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS\nFE36          ; disallowed_STD3_mapped ; 0029          # 1.1  PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS\nFE37          ; disallowed_STD3_mapped ; 007B          # 1.1  PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET\nFE38          ; disallowed_STD3_mapped ; 007D          # 1.1  PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET\nFE39          ; mapped                 ; 3014          # 1.1  PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET\nFE3A          ; mapped                 ; 3015          # 1.1  PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET\nFE3B          ; mapped                 ; 3010          # 1.1  PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET\nFE3C          ; mapped                 ; 3011          # 1.1  PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET\nFE3D          ; mapped                 ; 300A          # 1.1  PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET\nFE3E          ; mapped                 ; 300B          # 1.1  PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET\nFE3F          ; mapped                 ; 3008          # 1.1  PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET\nFE40          ; mapped                 ; 3009          # 1.1  PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET\nFE41          ; mapped                 ; 300C          # 1.1  PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET\nFE42          ; mapped                 ; 300D          # 1.1  PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET\nFE43          ; mapped                 ; 300E          # 1.1  PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET\nFE44          ; mapped                 ; 300F          # 1.1  PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET\nFE45..FE46    ; valid                  ;      ; NV8    # 3.2  SESAME DOT..WHITE SESAME DOT\nFE47          ; disallowed_STD3_mapped ; 005B          # 4.0  PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET\nFE48          ; disallowed_STD3_mapped ; 005D          # 4.0  PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET\nFE49..FE4C    ; disallowed_STD3_mapped ; 0020 0305     # 1.1  DASHED OVERLINE..DOUBLE WAVY OVERLINE\nFE4D..FE4F    ; disallowed_STD3_mapped ; 005F          # 1.1  DASHED LOW LINE..WAVY LOW LINE\nFE50          ; disallowed_STD3_mapped ; 002C          # 1.1  SMALL COMMA\nFE51          ; mapped                 ; 3001          # 1.1  SMALL IDEOGRAPHIC COMMA\nFE52          ; disallowed                             # 1.1  SMALL FULL STOP\nFE53          ; disallowed                             # NA   <reserved-FE53>\nFE54          ; disallowed_STD3_mapped ; 003B          # 1.1  SMALL SEMICOLON\nFE55          ; disallowed_STD3_mapped ; 003A          # 1.1  SMALL COLON\nFE56          ; disallowed_STD3_mapped ; 003F          # 1.1  SMALL QUESTION MARK\nFE57          ; disallowed_STD3_mapped ; 0021          # 1.1  SMALL EXCLAMATION MARK\nFE58          ; mapped                 ; 2014          # 1.1  SMALL EM DASH\nFE59          ; disallowed_STD3_mapped ; 0028          # 1.1  SMALL LEFT PARENTHESIS\nFE5A          ; disallowed_STD3_mapped ; 0029          # 1.1  SMALL RIGHT PARENTHESIS\nFE5B          ; disallowed_STD3_mapped ; 007B          # 1.1  SMALL LEFT CURLY BRACKET\nFE5C          ; disallowed_STD3_mapped ; 007D          # 1.1  SMALL RIGHT CURLY BRACKET\nFE5D          ; mapped                 ; 3014          # 1.1  SMALL LEFT TORTOISE SHELL BRACKET\nFE5E          ; mapped                 ; 3015          # 1.1  SMALL RIGHT TORTOISE SHELL BRACKET\nFE5F          ; disallowed_STD3_mapped ; 0023          # 1.1  SMALL NUMBER SIGN\nFE60          ; disallowed_STD3_mapped ; 0026          # 1.1  SMALL AMPERSAND\nFE61          ; disallowed_STD3_mapped ; 002A          # 1.1  SMALL ASTERISK\nFE62          ; disallowed_STD3_mapped ; 002B          # 1.1  SMALL PLUS SIGN\nFE63          ; mapped                 ; 002D          # 1.1  SMALL HYPHEN-MINUS\nFE64          ; disallowed_STD3_mapped ; 003C          # 1.1  SMALL LESS-THAN SIGN\nFE65          ; disallowed_STD3_mapped ; 003E          # 1.1  SMALL GREATER-THAN SIGN\nFE66          ; disallowed_STD3_mapped ; 003D          # 1.1  SMALL EQUALS SIGN\nFE67          ; disallowed                             # NA   <reserved-FE67>\nFE68          ; disallowed_STD3_mapped ; 005C          # 1.1  SMALL REVERSE SOLIDUS\nFE69          ; disallowed_STD3_mapped ; 0024          # 1.1  SMALL DOLLAR SIGN\nFE6A          ; disallowed_STD3_mapped ; 0025          # 1.1  SMALL PERCENT SIGN\nFE6B          ; disallowed_STD3_mapped ; 0040          # 1.1  SMALL COMMERCIAL AT\nFE6C..FE6F    ; disallowed                             # NA   <reserved-FE6C>..<reserved-FE6F>\nFE70          ; disallowed_STD3_mapped ; 0020 064B     # 1.1  ARABIC FATHATAN ISOLATED FORM\nFE71          ; mapped                 ; 0640 064B     # 1.1  ARABIC TATWEEL WITH FATHATAN ABOVE\nFE72          ; disallowed_STD3_mapped ; 0020 064C     # 1.1  ARABIC DAMMATAN ISOLATED FORM\nFE73          ; valid                                  # 3.2  ARABIC TAIL FRAGMENT\nFE74          ; disallowed_STD3_mapped ; 0020 064D     # 1.1  ARABIC KASRATAN ISOLATED FORM\nFE75          ; disallowed                             # NA   <reserved-FE75>\nFE76          ; disallowed_STD3_mapped ; 0020 064E     # 1.1  ARABIC FATHA ISOLATED FORM\nFE77          ; mapped                 ; 0640 064E     # 1.1  ARABIC FATHA MEDIAL FORM\nFE78          ; disallowed_STD3_mapped ; 0020 064F     # 1.1  ARABIC DAMMA ISOLATED FORM\nFE79          ; mapped                 ; 0640 064F     # 1.1  ARABIC DAMMA MEDIAL FORM\nFE7A          ; disallowed_STD3_mapped ; 0020 0650     # 1.1  ARABIC KASRA ISOLATED FORM\nFE7B          ; mapped                 ; 0640 0650     # 1.1  ARABIC KASRA MEDIAL FORM\nFE7C          ; disallowed_STD3_mapped ; 0020 0651     # 1.1  ARABIC SHADDA ISOLATED FORM\nFE7D          ; mapped                 ; 0640 0651     # 1.1  ARABIC SHADDA MEDIAL FORM\nFE7E          ; disallowed_STD3_mapped ; 0020 0652     # 1.1  ARABIC SUKUN ISOLATED FORM\nFE7F          ; mapped                 ; 0640 0652     # 1.1  ARABIC SUKUN MEDIAL FORM\nFE80          ; mapped                 ; 0621          # 1.1  ARABIC LETTER HAMZA ISOLATED FORM\nFE81..FE82    ; mapped                 ; 0622          # 1.1  ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM..ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM\nFE83..FE84    ; mapped                 ; 0623          # 1.1  ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM..ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM\nFE85..FE86    ; mapped                 ; 0624          # 1.1  ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM..ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM\nFE87..FE88    ; mapped                 ; 0625          # 1.1  ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM..ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM\nFE89..FE8C    ; mapped                 ; 0626          # 1.1  ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM..ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM\nFE8D..FE8E    ; mapped                 ; 0627          # 1.1  ARABIC LETTER ALEF ISOLATED FORM..ARABIC LETTER ALEF FINAL FORM\nFE8F..FE92    ; mapped                 ; 0628          # 1.1  ARABIC LETTER BEH ISOLATED FORM..ARABIC LETTER BEH MEDIAL FORM\nFE93..FE94    ; mapped                 ; 0629          # 1.1  ARABIC LETTER TEH MARBUTA ISOLATED FORM..ARABIC LETTER TEH MARBUTA FINAL FORM\nFE95..FE98    ; mapped                 ; 062A          # 1.1  ARABIC LETTER TEH ISOLATED FORM..ARABIC LETTER TEH MEDIAL FORM\nFE99..FE9C    ; mapped                 ; 062B          # 1.1  ARABIC LETTER THEH ISOLATED FORM..ARABIC LETTER THEH MEDIAL FORM\nFE9D..FEA0    ; mapped                 ; 062C          # 1.1  ARABIC LETTER JEEM ISOLATED FORM..ARABIC LETTER JEEM MEDIAL FORM\nFEA1..FEA4    ; mapped                 ; 062D          # 1.1  ARABIC LETTER HAH ISOLATED FORM..ARABIC LETTER HAH MEDIAL FORM\nFEA5..FEA8    ; mapped                 ; 062E          # 1.1  ARABIC LETTER KHAH ISOLATED FORM..ARABIC LETTER KHAH MEDIAL FORM\nFEA9..FEAA    ; mapped                 ; 062F          # 1.1  ARABIC LETTER DAL ISOLATED FORM..ARABIC LETTER DAL FINAL FORM\nFEAB..FEAC    ; mapped                 ; 0630          # 1.1  ARABIC LETTER THAL ISOLATED FORM..ARABIC LETTER THAL FINAL FORM\nFEAD..FEAE    ; mapped                 ; 0631          # 1.1  ARABIC LETTER REH ISOLATED FORM..ARABIC LETTER REH FINAL FORM\nFEAF..FEB0    ; mapped                 ; 0632          # 1.1  ARABIC LETTER ZAIN ISOLATED FORM..ARABIC LETTER ZAIN FINAL FORM\nFEB1..FEB4    ; mapped                 ; 0633          # 1.1  ARABIC LETTER SEEN ISOLATED FORM..ARABIC LETTER SEEN MEDIAL FORM\nFEB5..FEB8    ; mapped                 ; 0634          # 1.1  ARABIC LETTER SHEEN ISOLATED FORM..ARABIC LETTER SHEEN MEDIAL FORM\nFEB9..FEBC    ; mapped                 ; 0635          # 1.1  ARABIC LETTER SAD ISOLATED FORM..ARABIC LETTER SAD MEDIAL FORM\nFEBD..FEC0    ; mapped                 ; 0636          # 1.1  ARABIC LETTER DAD ISOLATED FORM..ARABIC LETTER DAD MEDIAL FORM\nFEC1..FEC4    ; mapped                 ; 0637          # 1.1  ARABIC LETTER TAH ISOLATED FORM..ARABIC LETTER TAH MEDIAL FORM\nFEC5..FEC8    ; mapped                 ; 0638          # 1.1  ARABIC LETTER ZAH ISOLATED FORM..ARABIC LETTER ZAH MEDIAL FORM\nFEC9..FECC    ; mapped                 ; 0639          # 1.1  ARABIC LETTER AIN ISOLATED FORM..ARABIC LETTER AIN MEDIAL FORM\nFECD..FED0    ; mapped                 ; 063A          # 1.1  ARABIC LETTER GHAIN ISOLATED FORM..ARABIC LETTER GHAIN MEDIAL FORM\nFED1..FED4    ; mapped                 ; 0641          # 1.1  ARABIC LETTER FEH ISOLATED FORM..ARABIC LETTER FEH MEDIAL FORM\nFED5..FED8    ; mapped                 ; 0642          # 1.1  ARABIC LETTER QAF ISOLATED FORM..ARABIC LETTER QAF MEDIAL FORM\nFED9..FEDC    ; mapped                 ; 0643          # 1.1  ARABIC LETTER KAF ISOLATED FORM..ARABIC LETTER KAF MEDIAL FORM\nFEDD..FEE0    ; mapped                 ; 0644          # 1.1  ARABIC LETTER LAM ISOLATED FORM..ARABIC LETTER LAM MEDIAL FORM\nFEE1..FEE4    ; mapped                 ; 0645          # 1.1  ARABIC LETTER MEEM ISOLATED FORM..ARABIC LETTER MEEM MEDIAL FORM\nFEE5..FEE8    ; mapped                 ; 0646          # 1.1  ARABIC LETTER NOON ISOLATED FORM..ARABIC LETTER NOON MEDIAL FORM\nFEE9..FEEC    ; mapped                 ; 0647          # 1.1  ARABIC LETTER HEH ISOLATED FORM..ARABIC LETTER HEH MEDIAL FORM\nFEED..FEEE    ; mapped                 ; 0648          # 1.1  ARABIC LETTER WAW ISOLATED FORM..ARABIC LETTER WAW FINAL FORM\nFEEF..FEF0    ; mapped                 ; 0649          # 1.1  ARABIC LETTER ALEF MAKSURA ISOLATED FORM..ARABIC LETTER ALEF MAKSURA FINAL FORM\nFEF1..FEF4    ; mapped                 ; 064A          # 1.1  ARABIC LETTER YEH ISOLATED FORM..ARABIC LETTER YEH MEDIAL FORM\nFEF5..FEF6    ; mapped                 ; 0644 0622     # 1.1  ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM\nFEF7..FEF8    ; mapped                 ; 0644 0623     # 1.1  ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM\nFEF9..FEFA    ; mapped                 ; 0644 0625     # 1.1  ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM\nFEFB..FEFC    ; mapped                 ; 0644 0627     # 1.1  ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM\nFEFD..FEFE    ; disallowed                             # NA   <reserved-FEFD>..<reserved-FEFE>\nFEFF          ; ignored                                # 1.1  ZERO WIDTH NO-BREAK SPACE\nFF00          ; disallowed                             # NA   <reserved-FF00>\nFF01          ; disallowed_STD3_mapped ; 0021          # 1.1  FULLWIDTH EXCLAMATION MARK\nFF02          ; disallowed_STD3_mapped ; 0022          # 1.1  FULLWIDTH QUOTATION MARK\nFF03          ; disallowed_STD3_mapped ; 0023          # 1.1  FULLWIDTH NUMBER SIGN\nFF04          ; disallowed_STD3_mapped ; 0024          # 1.1  FULLWIDTH DOLLAR SIGN\nFF05          ; disallowed_STD3_mapped ; 0025          # 1.1  FULLWIDTH PERCENT SIGN\nFF06          ; disallowed_STD3_mapped ; 0026          # 1.1  FULLWIDTH AMPERSAND\nFF07          ; disallowed_STD3_mapped ; 0027          # 1.1  FULLWIDTH APOSTROPHE\nFF08          ; disallowed_STD3_mapped ; 0028          # 1.1  FULLWIDTH LEFT PARENTHESIS\nFF09          ; disallowed_STD3_mapped ; 0029          # 1.1  FULLWIDTH RIGHT PARENTHESIS\nFF0A          ; disallowed_STD3_mapped ; 002A          # 1.1  FULLWIDTH ASTERISK\nFF0B          ; disallowed_STD3_mapped ; 002B          # 1.1  FULLWIDTH PLUS SIGN\nFF0C          ; disallowed_STD3_mapped ; 002C          # 1.1  FULLWIDTH COMMA\nFF0D          ; mapped                 ; 002D          # 1.1  FULLWIDTH HYPHEN-MINUS\nFF0E          ; mapped                 ; 002E          # 1.1  FULLWIDTH FULL STOP\nFF0F          ; disallowed_STD3_mapped ; 002F          # 1.1  FULLWIDTH SOLIDUS\nFF10          ; mapped                 ; 0030          # 1.1  FULLWIDTH DIGIT ZERO\nFF11          ; mapped                 ; 0031          # 1.1  FULLWIDTH DIGIT ONE\nFF12          ; mapped                 ; 0032          # 1.1  FULLWIDTH DIGIT TWO\nFF13          ; mapped                 ; 0033          # 1.1  FULLWIDTH DIGIT THREE\nFF14          ; mapped                 ; 0034          # 1.1  FULLWIDTH DIGIT FOUR\nFF15          ; mapped                 ; 0035          # 1.1  FULLWIDTH DIGIT FIVE\nFF16          ; mapped                 ; 0036          # 1.1  FULLWIDTH DIGIT SIX\nFF17          ; mapped                 ; 0037          # 1.1  FULLWIDTH DIGIT SEVEN\nFF18          ; mapped                 ; 0038          # 1.1  FULLWIDTH DIGIT EIGHT\nFF19          ; mapped                 ; 0039          # 1.1  FULLWIDTH DIGIT NINE\nFF1A          ; disallowed_STD3_mapped ; 003A          # 1.1  FULLWIDTH COLON\nFF1B          ; disallowed_STD3_mapped ; 003B          # 1.1  FULLWIDTH SEMICOLON\nFF1C          ; disallowed_STD3_mapped ; 003C          # 1.1  FULLWIDTH LESS-THAN SIGN\nFF1D          ; disallowed_STD3_mapped ; 003D          # 1.1  FULLWIDTH EQUALS SIGN\nFF1E          ; disallowed_STD3_mapped ; 003E          # 1.1  FULLWIDTH GREATER-THAN SIGN\nFF1F          ; disallowed_STD3_mapped ; 003F          # 1.1  FULLWIDTH QUESTION MARK\nFF20          ; disallowed_STD3_mapped ; 0040          # 1.1  FULLWIDTH COMMERCIAL AT\nFF21          ; mapped                 ; 0061          # 1.1  FULLWIDTH LATIN CAPITAL LETTER A\nFF22          ; mapped                 ; 0062          # 1.1  FULLWIDTH LATIN CAPITAL LETTER B\nFF23          ; mapped                 ; 0063          # 1.1  FULLWIDTH LATIN CAPITAL LETTER C\nFF24          ; mapped                 ; 0064          # 1.1  FULLWIDTH LATIN CAPITAL LETTER D\nFF25          ; mapped                 ; 0065          # 1.1  FULLWIDTH LATIN CAPITAL LETTER E\nFF26          ; mapped                 ; 0066          # 1.1  FULLWIDTH LATIN CAPITAL LETTER F\nFF27          ; mapped                 ; 0067          # 1.1  FULLWIDTH LATIN CAPITAL LETTER G\nFF28          ; mapped                 ; 0068          # 1.1  FULLWIDTH LATIN CAPITAL LETTER H\nFF29          ; mapped                 ; 0069          # 1.1  FULLWIDTH LATIN CAPITAL LETTER I\nFF2A          ; mapped                 ; 006A          # 1.1  FULLWIDTH LATIN CAPITAL LETTER J\nFF2B          ; mapped                 ; 006B          # 1.1  FULLWIDTH LATIN CAPITAL LETTER K\nFF2C          ; mapped                 ; 006C          # 1.1  FULLWIDTH LATIN CAPITAL LETTER L\nFF2D          ; mapped                 ; 006D          # 1.1  FULLWIDTH LATIN CAPITAL LETTER M\nFF2E          ; mapped                 ; 006E          # 1.1  FULLWIDTH LATIN CAPITAL LETTER N\nFF2F          ; mapped                 ; 006F          # 1.1  FULLWIDTH LATIN CAPITAL LETTER O\nFF30          ; mapped                 ; 0070          # 1.1  FULLWIDTH LATIN CAPITAL LETTER P\nFF31          ; mapped                 ; 0071          # 1.1  FULLWIDTH LATIN CAPITAL LETTER Q\nFF32          ; mapped                 ; 0072          # 1.1  FULLWIDTH LATIN CAPITAL LETTER R\nFF33          ; mapped                 ; 0073          # 1.1  FULLWIDTH LATIN CAPITAL LETTER S\nFF34          ; mapped                 ; 0074          # 1.1  FULLWIDTH LATIN CAPITAL LETTER T\nFF35          ; mapped                 ; 0075          # 1.1  FULLWIDTH LATIN CAPITAL LETTER U\nFF36          ; mapped                 ; 0076          # 1.1  FULLWIDTH LATIN CAPITAL LETTER V\nFF37          ; mapped                 ; 0077          # 1.1  FULLWIDTH LATIN CAPITAL LETTER W\nFF38          ; mapped                 ; 0078          # 1.1  FULLWIDTH LATIN CAPITAL LETTER X\nFF39          ; mapped                 ; 0079          # 1.1  FULLWIDTH LATIN CAPITAL LETTER Y\nFF3A          ; mapped                 ; 007A          # 1.1  FULLWIDTH LATIN CAPITAL LETTER Z\nFF3B          ; disallowed_STD3_mapped ; 005B          # 1.1  FULLWIDTH LEFT SQUARE BRACKET\nFF3C          ; disallowed_STD3_mapped ; 005C          # 1.1  FULLWIDTH REVERSE SOLIDUS\nFF3D          ; disallowed_STD3_mapped ; 005D          # 1.1  FULLWIDTH RIGHT SQUARE BRACKET\nFF3E          ; disallowed_STD3_mapped ; 005E          # 1.1  FULLWIDTH CIRCUMFLEX ACCENT\nFF3F          ; disallowed_STD3_mapped ; 005F          # 1.1  FULLWIDTH LOW LINE\nFF40          ; disallowed_STD3_mapped ; 0060          # 1.1  FULLWIDTH GRAVE ACCENT\nFF41          ; mapped                 ; 0061          # 1.1  FULLWIDTH LATIN SMALL LETTER A\nFF42          ; mapped                 ; 0062          # 1.1  FULLWIDTH LATIN SMALL LETTER B\nFF43          ; mapped                 ; 0063          # 1.1  FULLWIDTH LATIN SMALL LETTER C\nFF44          ; mapped                 ; 0064          # 1.1  FULLWIDTH LATIN SMALL LETTER D\nFF45          ; mapped                 ; 0065          # 1.1  FULLWIDTH LATIN SMALL LETTER E\nFF46          ; mapped                 ; 0066          # 1.1  FULLWIDTH LATIN SMALL LETTER F\nFF47          ; mapped                 ; 0067          # 1.1  FULLWIDTH LATIN SMALL LETTER G\nFF48          ; mapped                 ; 0068          # 1.1  FULLWIDTH LATIN SMALL LETTER H\nFF49          ; mapped                 ; 0069          # 1.1  FULLWIDTH LATIN SMALL LETTER I\nFF4A          ; mapped                 ; 006A          # 1.1  FULLWIDTH LATIN SMALL LETTER J\nFF4B          ; mapped                 ; 006B          # 1.1  FULLWIDTH LATIN SMALL LETTER K\nFF4C          ; mapped                 ; 006C          # 1.1  FULLWIDTH LATIN SMALL LETTER L\nFF4D          ; mapped                 ; 006D          # 1.1  FULLWIDTH LATIN SMALL LETTER M\nFF4E          ; mapped                 ; 006E          # 1.1  FULLWIDTH LATIN SMALL LETTER N\nFF4F          ; mapped                 ; 006F          # 1.1  FULLWIDTH LATIN SMALL LETTER O\nFF50          ; mapped                 ; 0070          # 1.1  FULLWIDTH LATIN SMALL LETTER P\nFF51          ; mapped                 ; 0071          # 1.1  FULLWIDTH LATIN SMALL LETTER Q\nFF52          ; mapped                 ; 0072          # 1.1  FULLWIDTH LATIN SMALL LETTER R\nFF53          ; mapped                 ; 0073          # 1.1  FULLWIDTH LATIN SMALL LETTER S\nFF54          ; mapped                 ; 0074          # 1.1  FULLWIDTH LATIN SMALL LETTER T\nFF55          ; mapped                 ; 0075          # 1.1  FULLWIDTH LATIN SMALL LETTER U\nFF56          ; mapped                 ; 0076          # 1.1  FULLWIDTH LATIN SMALL LETTER V\nFF57          ; mapped                 ; 0077          # 1.1  FULLWIDTH LATIN SMALL LETTER W\nFF58          ; mapped                 ; 0078          # 1.1  FULLWIDTH LATIN SMALL LETTER X\nFF59          ; mapped                 ; 0079          # 1.1  FULLWIDTH LATIN SMALL LETTER Y\nFF5A          ; mapped                 ; 007A          # 1.1  FULLWIDTH LATIN SMALL LETTER Z\nFF5B          ; disallowed_STD3_mapped ; 007B          # 1.1  FULLWIDTH LEFT CURLY BRACKET\nFF5C          ; disallowed_STD3_mapped ; 007C          # 1.1  FULLWIDTH VERTICAL LINE\nFF5D          ; disallowed_STD3_mapped ; 007D          # 1.1  FULLWIDTH RIGHT CURLY BRACKET\nFF5E          ; disallowed_STD3_mapped ; 007E          # 1.1  FULLWIDTH TILDE\nFF5F          ; mapped                 ; 2985          # 3.2  FULLWIDTH LEFT WHITE PARENTHESIS\nFF60          ; mapped                 ; 2986          # 3.2  FULLWIDTH RIGHT WHITE PARENTHESIS\nFF61          ; mapped                 ; 002E          # 1.1  HALFWIDTH IDEOGRAPHIC FULL STOP\nFF62          ; mapped                 ; 300C          # 1.1  HALFWIDTH LEFT CORNER BRACKET\nFF63          ; mapped                 ; 300D          # 1.1  HALFWIDTH RIGHT CORNER BRACKET\nFF64          ; mapped                 ; 3001          # 1.1  HALFWIDTH IDEOGRAPHIC COMMA\nFF65          ; mapped                 ; 30FB          # 1.1  HALFWIDTH KATAKANA MIDDLE DOT\nFF66          ; mapped                 ; 30F2          # 1.1  HALFWIDTH KATAKANA LETTER WO\nFF67          ; mapped                 ; 30A1          # 1.1  HALFWIDTH KATAKANA LETTER SMALL A\nFF68          ; mapped                 ; 30A3          # 1.1  HALFWIDTH KATAKANA LETTER SMALL I\nFF69          ; mapped                 ; 30A5          # 1.1  HALFWIDTH KATAKANA LETTER SMALL U\nFF6A          ; mapped                 ; 30A7          # 1.1  HALFWIDTH KATAKANA LETTER SMALL E\nFF6B          ; mapped                 ; 30A9          # 1.1  HALFWIDTH KATAKANA LETTER SMALL O\nFF6C          ; mapped                 ; 30E3          # 1.1  HALFWIDTH KATAKANA LETTER SMALL YA\nFF6D          ; mapped                 ; 30E5          # 1.1  HALFWIDTH KATAKANA LETTER SMALL YU\nFF6E          ; mapped                 ; 30E7          # 1.1  HALFWIDTH KATAKANA LETTER SMALL YO\nFF6F          ; mapped                 ; 30C3          # 1.1  HALFWIDTH KATAKANA LETTER SMALL TU\nFF70          ; mapped                 ; 30FC          # 1.1  HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK\nFF71          ; mapped                 ; 30A2          # 1.1  HALFWIDTH KATAKANA LETTER A\nFF72          ; mapped                 ; 30A4          # 1.1  HALFWIDTH KATAKANA LETTER I\nFF73          ; mapped                 ; 30A6          # 1.1  HALFWIDTH KATAKANA LETTER U\nFF74          ; mapped                 ; 30A8          # 1.1  HALFWIDTH KATAKANA LETTER E\nFF75          ; mapped                 ; 30AA          # 1.1  HALFWIDTH KATAKANA LETTER O\nFF76          ; mapped                 ; 30AB          # 1.1  HALFWIDTH KATAKANA LETTER KA\nFF77          ; mapped                 ; 30AD          # 1.1  HALFWIDTH KATAKANA LETTER KI\nFF78          ; mapped                 ; 30AF          # 1.1  HALFWIDTH KATAKANA LETTER KU\nFF79          ; mapped                 ; 30B1          # 1.1  HALFWIDTH KATAKANA LETTER KE\nFF7A          ; mapped                 ; 30B3          # 1.1  HALFWIDTH KATAKANA LETTER KO\nFF7B          ; mapped                 ; 30B5          # 1.1  HALFWIDTH KATAKANA LETTER SA\nFF7C          ; mapped                 ; 30B7          # 1.1  HALFWIDTH KATAKANA LETTER SI\nFF7D          ; mapped                 ; 30B9          # 1.1  HALFWIDTH KATAKANA LETTER SU\nFF7E          ; mapped                 ; 30BB          # 1.1  HALFWIDTH KATAKANA LETTER SE\nFF7F          ; mapped                 ; 30BD          # 1.1  HALFWIDTH KATAKANA LETTER SO\nFF80          ; mapped                 ; 30BF          # 1.1  HALFWIDTH KATAKANA LETTER TA\nFF81          ; mapped                 ; 30C1          # 1.1  HALFWIDTH KATAKANA LETTER TI\nFF82          ; mapped                 ; 30C4          # 1.1  HALFWIDTH KATAKANA LETTER TU\nFF83          ; mapped                 ; 30C6          # 1.1  HALFWIDTH KATAKANA LETTER TE\nFF84          ; mapped                 ; 30C8          # 1.1  HALFWIDTH KATAKANA LETTER TO\nFF85          ; mapped                 ; 30CA          # 1.1  HALFWIDTH KATAKANA LETTER NA\nFF86          ; mapped                 ; 30CB          # 1.1  HALFWIDTH KATAKANA LETTER NI\nFF87          ; mapped                 ; 30CC          # 1.1  HALFWIDTH KATAKANA LETTER NU\nFF88          ; mapped                 ; 30CD          # 1.1  HALFWIDTH KATAKANA LETTER NE\nFF89          ; mapped                 ; 30CE          # 1.1  HALFWIDTH KATAKANA LETTER NO\nFF8A          ; mapped                 ; 30CF          # 1.1  HALFWIDTH KATAKANA LETTER HA\nFF8B          ; mapped                 ; 30D2          # 1.1  HALFWIDTH KATAKANA LETTER HI\nFF8C          ; mapped                 ; 30D5          # 1.1  HALFWIDTH KATAKANA LETTER HU\nFF8D          ; mapped                 ; 30D8          # 1.1  HALFWIDTH KATAKANA LETTER HE\nFF8E          ; mapped                 ; 30DB          # 1.1  HALFWIDTH KATAKANA LETTER HO\nFF8F          ; mapped                 ; 30DE          # 1.1  HALFWIDTH KATAKANA LETTER MA\nFF90          ; mapped                 ; 30DF          # 1.1  HALFWIDTH KATAKANA LETTER MI\nFF91          ; mapped                 ; 30E0          # 1.1  HALFWIDTH KATAKANA LETTER MU\nFF92          ; mapped                 ; 30E1          # 1.1  HALFWIDTH KATAKANA LETTER ME\nFF93          ; mapped                 ; 30E2          # 1.1  HALFWIDTH KATAKANA LETTER MO\nFF94          ; mapped                 ; 30E4          # 1.1  HALFWIDTH KATAKANA LETTER YA\nFF95          ; mapped                 ; 30E6          # 1.1  HALFWIDTH KATAKANA LETTER YU\nFF96          ; mapped                 ; 30E8          # 1.1  HALFWIDTH KATAKANA LETTER YO\nFF97          ; mapped                 ; 30E9          # 1.1  HALFWIDTH KATAKANA LETTER RA\nFF98          ; mapped                 ; 30EA          # 1.1  HALFWIDTH KATAKANA LETTER RI\nFF99          ; mapped                 ; 30EB          # 1.1  HALFWIDTH KATAKANA LETTER RU\nFF9A          ; mapped                 ; 30EC          # 1.1  HALFWIDTH KATAKANA LETTER RE\nFF9B          ; mapped                 ; 30ED          # 1.1  HALFWIDTH KATAKANA LETTER RO\nFF9C          ; mapped                 ; 30EF          # 1.1  HALFWIDTH KATAKANA LETTER WA\nFF9D          ; mapped                 ; 30F3          # 1.1  HALFWIDTH KATAKANA LETTER N\nFF9E          ; mapped                 ; 3099          # 1.1  HALFWIDTH KATAKANA VOICED SOUND MARK\nFF9F          ; mapped                 ; 309A          # 1.1  HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK\nFFA0          ; disallowed                             # 1.1  HALFWIDTH HANGUL FILLER\nFFA1          ; mapped                 ; 1100          # 1.1  HALFWIDTH HANGUL LETTER KIYEOK\nFFA2          ; mapped                 ; 1101          # 1.1  HALFWIDTH HANGUL LETTER SSANGKIYEOK\nFFA3          ; mapped                 ; 11AA          # 1.1  HALFWIDTH HANGUL LETTER KIYEOK-SIOS\nFFA4          ; mapped                 ; 1102          # 1.1  HALFWIDTH HANGUL LETTER NIEUN\nFFA5          ; mapped                 ; 11AC          # 1.1  HALFWIDTH HANGUL LETTER NIEUN-CIEUC\nFFA6          ; mapped                 ; 11AD          # 1.1  HALFWIDTH HANGUL LETTER NIEUN-HIEUH\nFFA7          ; mapped                 ; 1103          # 1.1  HALFWIDTH HANGUL LETTER TIKEUT\nFFA8          ; mapped                 ; 1104          # 1.1  HALFWIDTH HANGUL LETTER SSANGTIKEUT\nFFA9          ; mapped                 ; 1105          # 1.1  HALFWIDTH HANGUL LETTER RIEUL\nFFAA          ; mapped                 ; 11B0          # 1.1  HALFWIDTH HANGUL LETTER RIEUL-KIYEOK\nFFAB          ; mapped                 ; 11B1          # 1.1  HALFWIDTH HANGUL LETTER RIEUL-MIEUM\nFFAC          ; mapped                 ; 11B2          # 1.1  HALFWIDTH HANGUL LETTER RIEUL-PIEUP\nFFAD          ; mapped                 ; 11B3          # 1.1  HALFWIDTH HANGUL LETTER RIEUL-SIOS\nFFAE          ; mapped                 ; 11B4          # 1.1  HALFWIDTH HANGUL LETTER RIEUL-THIEUTH\nFFAF          ; mapped                 ; 11B5          # 1.1  HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH\nFFB0          ; mapped                 ; 111A          # 1.1  HALFWIDTH HANGUL LETTER RIEUL-HIEUH\nFFB1          ; mapped                 ; 1106          # 1.1  HALFWIDTH HANGUL LETTER MIEUM\nFFB2          ; mapped                 ; 1107          # 1.1  HALFWIDTH HANGUL LETTER PIEUP\nFFB3          ; mapped                 ; 1108          # 1.1  HALFWIDTH HANGUL LETTER SSANGPIEUP\nFFB4          ; mapped                 ; 1121          # 1.1  HALFWIDTH HANGUL LETTER PIEUP-SIOS\nFFB5          ; mapped                 ; 1109          # 1.1  HALFWIDTH HANGUL LETTER SIOS\nFFB6          ; mapped                 ; 110A          # 1.1  HALFWIDTH HANGUL LETTER SSANGSIOS\nFFB7          ; mapped                 ; 110B          # 1.1  HALFWIDTH HANGUL LETTER IEUNG\nFFB8          ; mapped                 ; 110C          # 1.1  HALFWIDTH HANGUL LETTER CIEUC\nFFB9          ; mapped                 ; 110D          # 1.1  HALFWIDTH HANGUL LETTER SSANGCIEUC\nFFBA          ; mapped                 ; 110E          # 1.1  HALFWIDTH HANGUL LETTER CHIEUCH\nFFBB          ; mapped                 ; 110F          # 1.1  HALFWIDTH HANGUL LETTER KHIEUKH\nFFBC          ; mapped                 ; 1110          # 1.1  HALFWIDTH HANGUL LETTER THIEUTH\nFFBD          ; mapped                 ; 1111          # 1.1  HALFWIDTH HANGUL LETTER PHIEUPH\nFFBE          ; mapped                 ; 1112          # 1.1  HALFWIDTH HANGUL LETTER HIEUH\nFFBF..FFC1    ; disallowed                             # NA   <reserved-FFBF>..<reserved-FFC1>\nFFC2          ; mapped                 ; 1161          # 1.1  HALFWIDTH HANGUL LETTER A\nFFC3          ; mapped                 ; 1162          # 1.1  HALFWIDTH HANGUL LETTER AE\nFFC4          ; mapped                 ; 1163          # 1.1  HALFWIDTH HANGUL LETTER YA\nFFC5          ; mapped                 ; 1164          # 1.1  HALFWIDTH HANGUL LETTER YAE\nFFC6          ; mapped                 ; 1165          # 1.1  HALFWIDTH HANGUL LETTER EO\nFFC7          ; mapped                 ; 1166          # 1.1  HALFWIDTH HANGUL LETTER E\nFFC8..FFC9    ; disallowed                             # NA   <reserved-FFC8>..<reserved-FFC9>\nFFCA          ; mapped                 ; 1167          # 1.1  HALFWIDTH HANGUL LETTER YEO\nFFCB          ; mapped                 ; 1168          # 1.1  HALFWIDTH HANGUL LETTER YE\nFFCC          ; mapped                 ; 1169          # 1.1  HALFWIDTH HANGUL LETTER O\nFFCD          ; mapped                 ; 116A          # 1.1  HALFWIDTH HANGUL LETTER WA\nFFCE          ; mapped                 ; 116B          # 1.1  HALFWIDTH HANGUL LETTER WAE\nFFCF          ; mapped                 ; 116C          # 1.1  HALFWIDTH HANGUL LETTER OE\nFFD0..FFD1    ; disallowed                             # NA   <reserved-FFD0>..<reserved-FFD1>\nFFD2          ; mapped                 ; 116D          # 1.1  HALFWIDTH HANGUL LETTER YO\nFFD3          ; mapped                 ; 116E          # 1.1  HALFWIDTH HANGUL LETTER U\nFFD4          ; mapped                 ; 116F          # 1.1  HALFWIDTH HANGUL LETTER WEO\nFFD5          ; mapped                 ; 1170          # 1.1  HALFWIDTH HANGUL LETTER WE\nFFD6          ; mapped                 ; 1171          # 1.1  HALFWIDTH HANGUL LETTER WI\nFFD7          ; mapped                 ; 1172          # 1.1  HALFWIDTH HANGUL LETTER YU\nFFD8..FFD9    ; disallowed                             # NA   <reserved-FFD8>..<reserved-FFD9>\nFFDA          ; mapped                 ; 1173          # 1.1  HALFWIDTH HANGUL LETTER EU\nFFDB          ; mapped                 ; 1174          # 1.1  HALFWIDTH HANGUL LETTER YI\nFFDC          ; mapped                 ; 1175          # 1.1  HALFWIDTH HANGUL LETTER I\nFFDD..FFDF    ; disallowed                             # NA   <reserved-FFDD>..<reserved-FFDF>\nFFE0          ; mapped                 ; 00A2          # 1.1  FULLWIDTH CENT SIGN\nFFE1          ; mapped                 ; 00A3          # 1.1  FULLWIDTH POUND SIGN\nFFE2          ; mapped                 ; 00AC          # 1.1  FULLWIDTH NOT SIGN\nFFE3          ; disallowed_STD3_mapped ; 0020 0304     # 1.1  FULLWIDTH MACRON\nFFE4          ; mapped                 ; 00A6          # 1.1  FULLWIDTH BROKEN BAR\nFFE5          ; mapped                 ; 00A5          # 1.1  FULLWIDTH YEN SIGN\nFFE6          ; mapped                 ; 20A9          # 1.1  FULLWIDTH WON SIGN\nFFE7          ; disallowed                             # NA   <reserved-FFE7>\nFFE8          ; mapped                 ; 2502          # 1.1  HALFWIDTH FORMS LIGHT VERTICAL\nFFE9          ; mapped                 ; 2190          # 1.1  HALFWIDTH LEFTWARDS ARROW\nFFEA          ; mapped                 ; 2191          # 1.1  HALFWIDTH UPWARDS ARROW\nFFEB          ; mapped                 ; 2192          # 1.1  HALFWIDTH RIGHTWARDS ARROW\nFFEC          ; mapped                 ; 2193          # 1.1  HALFWIDTH DOWNWARDS ARROW\nFFED          ; mapped                 ; 25A0          # 1.1  HALFWIDTH BLACK SQUARE\nFFEE          ; mapped                 ; 25CB          # 1.1  HALFWIDTH WHITE CIRCLE\nFFEF..FFF8    ; disallowed                             # NA   <reserved-FFEF>..<reserved-FFF8>\nFFF9..FFFB    ; disallowed                             # 3.0  INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR\nFFFC          ; disallowed                             # 2.1  OBJECT REPLACEMENT CHARACTER\nFFFD          ; disallowed                             # 1.1  REPLACEMENT CHARACTER\nFFFE..FFFF    ; disallowed                             # 1.1  <noncharacter-FFFE>..<noncharacter-FFFF>\n10000..1000B  ; valid                                  # 4.0  LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE\n1000C         ; disallowed                             # NA   <reserved-1000C>\n1000D..10026  ; valid                                  # 4.0  LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO\n10027         ; disallowed                             # NA   <reserved-10027>\n10028..1003A  ; valid                                  # 4.0  LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO\n1003B         ; disallowed                             # NA   <reserved-1003B>\n1003C..1003D  ; valid                                  # 4.0  LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE\n1003E         ; disallowed                             # NA   <reserved-1003E>\n1003F..1004D  ; valid                                  # 4.0  LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO\n1004E..1004F  ; disallowed                             # NA   <reserved-1004E>..<reserved-1004F>\n10050..1005D  ; valid                                  # 4.0  LINEAR B SYMBOL B018..LINEAR B SYMBOL B089\n1005E..1007F  ; disallowed                             # NA   <reserved-1005E>..<reserved-1007F>\n10080..100FA  ; valid                                  # 4.0  LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305\n100FB..100FF  ; disallowed                             # NA   <reserved-100FB>..<reserved-100FF>\n10100..10102  ; valid                  ;      ; NV8    # 4.0  AEGEAN WORD SEPARATOR LINE..AEGEAN CHECK MARK\n10103..10106  ; disallowed                             # NA   <reserved-10103>..<reserved-10106>\n10107..10133  ; valid                  ;      ; NV8    # 4.0  AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND\n10134..10136  ; disallowed                             # NA   <reserved-10134>..<reserved-10136>\n10137..1013F  ; valid                  ;      ; NV8    # 4.0  AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT\n10140..1018A  ; valid                  ;      ; NV8    # 4.1  GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ZERO SIGN\n1018B..1018C  ; valid                  ;      ; NV8    # 7.0  GREEK ONE QUARTER SIGN..GREEK SINUSOID SIGN\n1018D..1018E  ; valid                  ;      ; NV8    # 9.0  GREEK INDICTION SIGN..NOMISMA SIGN\n1018F         ; disallowed                             # NA   <reserved-1018F>\n10190..1019B  ; valid                  ;      ; NV8    # 5.1  ROMAN SEXTANS SIGN..ROMAN CENTURIAL SIGN\n1019C         ; valid                  ;      ; NV8    # 13.0 ASCIA SYMBOL\n1019D..1019F  ; disallowed                             # NA   <reserved-1019D>..<reserved-1019F>\n101A0         ; valid                  ;      ; NV8    # 7.0  GREEK SYMBOL TAU RHO\n101A1..101CF  ; disallowed                             # NA   <reserved-101A1>..<reserved-101CF>\n101D0..101FC  ; valid                  ;      ; NV8    # 5.1  PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND\n101FD         ; valid                                  # 5.1  PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE\n101FE..1027F  ; disallowed                             # NA   <reserved-101FE>..<reserved-1027F>\n10280..1029C  ; valid                                  # 5.1  LYCIAN LETTER A..LYCIAN LETTER X\n1029D..1029F  ; disallowed                             # NA   <reserved-1029D>..<reserved-1029F>\n102A0..102D0  ; valid                                  # 5.1  CARIAN LETTER A..CARIAN LETTER UUU3\n102D1..102DF  ; disallowed                             # NA   <reserved-102D1>..<reserved-102DF>\n102E0         ; valid                                  # 7.0  COPTIC EPACT THOUSANDS MARK\n102E1..102FB  ; valid                  ;      ; NV8    # 7.0  COPTIC EPACT DIGIT ONE..COPTIC EPACT NUMBER NINE HUNDRED\n102FC..102FF  ; disallowed                             # NA   <reserved-102FC>..<reserved-102FF>\n10300..1031E  ; valid                                  # 3.1  OLD ITALIC LETTER A..OLD ITALIC LETTER UU\n1031F         ; valid                                  # 7.0  OLD ITALIC LETTER ESS\n10320..10323  ; valid                  ;      ; NV8    # 3.1  OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY\n10324..1032C  ; disallowed                             # NA   <reserved-10324>..<reserved-1032C>\n1032D..1032F  ; valid                                  # 10.0 OLD ITALIC LETTER YE..OLD ITALIC LETTER SOUTHERN TSE\n10330..10340  ; valid                                  # 3.1  GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA\n10341         ; valid                  ;      ; NV8    # 3.1  GOTHIC LETTER NINETY\n10342..10349  ; valid                                  # 3.1  GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL\n1034A         ; valid                  ;      ; NV8    # 3.1  GOTHIC LETTER NINE HUNDRED\n1034B..1034F  ; disallowed                             # NA   <reserved-1034B>..<reserved-1034F>\n10350..1037A  ; valid                                  # 7.0  OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII\n1037B..1037F  ; disallowed                             # NA   <reserved-1037B>..<reserved-1037F>\n10380..1039D  ; valid                                  # 4.0  UGARITIC LETTER ALPA..UGARITIC LETTER SSU\n1039E         ; disallowed                             # NA   <reserved-1039E>\n1039F         ; valid                  ;      ; NV8    # 4.0  UGARITIC WORD DIVIDER\n103A0..103C3  ; valid                                  # 4.1  OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA\n103C4..103C7  ; disallowed                             # NA   <reserved-103C4>..<reserved-103C7>\n103C8..103CF  ; valid                                  # 4.1  OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH\n103D0..103D5  ; valid                  ;      ; NV8    # 4.1  OLD PERSIAN WORD DIVIDER..OLD PERSIAN NUMBER HUNDRED\n103D6..103FF  ; disallowed                             # NA   <reserved-103D6>..<reserved-103FF>\n10400         ; mapped                 ; 10428         # 3.1  DESERET CAPITAL LETTER LONG I\n10401         ; mapped                 ; 10429         # 3.1  DESERET CAPITAL LETTER LONG E\n10402         ; mapped                 ; 1042A         # 3.1  DESERET CAPITAL LETTER LONG A\n10403         ; mapped                 ; 1042B         # 3.1  DESERET CAPITAL LETTER LONG AH\n10404         ; mapped                 ; 1042C         # 3.1  DESERET CAPITAL LETTER LONG O\n10405         ; mapped                 ; 1042D         # 3.1  DESERET CAPITAL LETTER LONG OO\n10406         ; mapped                 ; 1042E         # 3.1  DESERET CAPITAL LETTER SHORT I\n10407         ; mapped                 ; 1042F         # 3.1  DESERET CAPITAL LETTER SHORT E\n10408         ; mapped                 ; 10430         # 3.1  DESERET CAPITAL LETTER SHORT A\n10409         ; mapped                 ; 10431         # 3.1  DESERET CAPITAL LETTER SHORT AH\n1040A         ; mapped                 ; 10432         # 3.1  DESERET CAPITAL LETTER SHORT O\n1040B         ; mapped                 ; 10433         # 3.1  DESERET CAPITAL LETTER SHORT OO\n1040C         ; mapped                 ; 10434         # 3.1  DESERET CAPITAL LETTER AY\n1040D         ; mapped                 ; 10435         # 3.1  DESERET CAPITAL LETTER OW\n1040E         ; mapped                 ; 10436         # 3.1  DESERET CAPITAL LETTER WU\n1040F         ; mapped                 ; 10437         # 3.1  DESERET CAPITAL LETTER YEE\n10410         ; mapped                 ; 10438         # 3.1  DESERET CAPITAL LETTER H\n10411         ; mapped                 ; 10439         # 3.1  DESERET CAPITAL LETTER PEE\n10412         ; mapped                 ; 1043A         # 3.1  DESERET CAPITAL LETTER BEE\n10413         ; mapped                 ; 1043B         # 3.1  DESERET CAPITAL LETTER TEE\n10414         ; mapped                 ; 1043C         # 3.1  DESERET CAPITAL LETTER DEE\n10415         ; mapped                 ; 1043D         # 3.1  DESERET CAPITAL LETTER CHEE\n10416         ; mapped                 ; 1043E         # 3.1  DESERET CAPITAL LETTER JEE\n10417         ; mapped                 ; 1043F         # 3.1  DESERET CAPITAL LETTER KAY\n10418         ; mapped                 ; 10440         # 3.1  DESERET CAPITAL LETTER GAY\n10419         ; mapped                 ; 10441         # 3.1  DESERET CAPITAL LETTER EF\n1041A         ; mapped                 ; 10442         # 3.1  DESERET CAPITAL LETTER VEE\n1041B         ; mapped                 ; 10443         # 3.1  DESERET CAPITAL LETTER ETH\n1041C         ; mapped                 ; 10444         # 3.1  DESERET CAPITAL LETTER THEE\n1041D         ; mapped                 ; 10445         # 3.1  DESERET CAPITAL LETTER ES\n1041E         ; mapped                 ; 10446         # 3.1  DESERET CAPITAL LETTER ZEE\n1041F         ; mapped                 ; 10447         # 3.1  DESERET CAPITAL LETTER ESH\n10420         ; mapped                 ; 10448         # 3.1  DESERET CAPITAL LETTER ZHEE\n10421         ; mapped                 ; 10449         # 3.1  DESERET CAPITAL LETTER ER\n10422         ; mapped                 ; 1044A         # 3.1  DESERET CAPITAL LETTER EL\n10423         ; mapped                 ; 1044B         # 3.1  DESERET CAPITAL LETTER EM\n10424         ; mapped                 ; 1044C         # 3.1  DESERET CAPITAL LETTER EN\n10425         ; mapped                 ; 1044D         # 3.1  DESERET CAPITAL LETTER ENG\n10426         ; mapped                 ; 1044E         # 4.0  DESERET CAPITAL LETTER OI\n10427         ; mapped                 ; 1044F         # 4.0  DESERET CAPITAL LETTER EW\n10428..1044D  ; valid                                  # 3.1  DESERET SMALL LETTER LONG I..DESERET SMALL LETTER ENG\n1044E..1049D  ; valid                                  # 4.0  DESERET SMALL LETTER OI..OSMANYA LETTER OO\n1049E..1049F  ; disallowed                             # NA   <reserved-1049E>..<reserved-1049F>\n104A0..104A9  ; valid                                  # 4.0  OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE\n104AA..104AF  ; disallowed                             # NA   <reserved-104AA>..<reserved-104AF>\n104B0         ; mapped                 ; 104D8         # 9.0  OSAGE CAPITAL LETTER A\n104B1         ; mapped                 ; 104D9         # 9.0  OSAGE CAPITAL LETTER AI\n104B2         ; mapped                 ; 104DA         # 9.0  OSAGE CAPITAL LETTER AIN\n104B3         ; mapped                 ; 104DB         # 9.0  OSAGE CAPITAL LETTER AH\n104B4         ; mapped                 ; 104DC         # 9.0  OSAGE CAPITAL LETTER BRA\n104B5         ; mapped                 ; 104DD         # 9.0  OSAGE CAPITAL LETTER CHA\n104B6         ; mapped                 ; 104DE         # 9.0  OSAGE CAPITAL LETTER EHCHA\n104B7         ; mapped                 ; 104DF         # 9.0  OSAGE CAPITAL LETTER E\n104B8         ; mapped                 ; 104E0         # 9.0  OSAGE CAPITAL LETTER EIN\n104B9         ; mapped                 ; 104E1         # 9.0  OSAGE CAPITAL LETTER HA\n104BA         ; mapped                 ; 104E2         # 9.0  OSAGE CAPITAL LETTER HYA\n104BB         ; mapped                 ; 104E3         # 9.0  OSAGE CAPITAL LETTER I\n104BC         ; mapped                 ; 104E4         # 9.0  OSAGE CAPITAL LETTER KA\n104BD         ; mapped                 ; 104E5         # 9.0  OSAGE CAPITAL LETTER EHKA\n104BE         ; mapped                 ; 104E6         # 9.0  OSAGE CAPITAL LETTER KYA\n104BF         ; mapped                 ; 104E7         # 9.0  OSAGE CAPITAL LETTER LA\n104C0         ; mapped                 ; 104E8         # 9.0  OSAGE CAPITAL LETTER MA\n104C1         ; mapped                 ; 104E9         # 9.0  OSAGE CAPITAL LETTER NA\n104C2         ; mapped                 ; 104EA         # 9.0  OSAGE CAPITAL LETTER O\n104C3         ; mapped                 ; 104EB         # 9.0  OSAGE CAPITAL LETTER OIN\n104C4         ; mapped                 ; 104EC         # 9.0  OSAGE CAPITAL LETTER PA\n104C5         ; mapped                 ; 104ED         # 9.0  OSAGE CAPITAL LETTER EHPA\n104C6         ; mapped                 ; 104EE         # 9.0  OSAGE CAPITAL LETTER SA\n104C7         ; mapped                 ; 104EF         # 9.0  OSAGE CAPITAL LETTER SHA\n104C8         ; mapped                 ; 104F0         # 9.0  OSAGE CAPITAL LETTER TA\n104C9         ; mapped                 ; 104F1         # 9.0  OSAGE CAPITAL LETTER EHTA\n104CA         ; mapped                 ; 104F2         # 9.0  OSAGE CAPITAL LETTER TSA\n104CB         ; mapped                 ; 104F3         # 9.0  OSAGE CAPITAL LETTER EHTSA\n104CC         ; mapped                 ; 104F4         # 9.0  OSAGE CAPITAL LETTER TSHA\n104CD         ; mapped                 ; 104F5         # 9.0  OSAGE CAPITAL LETTER DHA\n104CE         ; mapped                 ; 104F6         # 9.0  OSAGE CAPITAL LETTER U\n104CF         ; mapped                 ; 104F7         # 9.0  OSAGE CAPITAL LETTER WA\n104D0         ; mapped                 ; 104F8         # 9.0  OSAGE CAPITAL LETTER KHA\n104D1         ; mapped                 ; 104F9         # 9.0  OSAGE CAPITAL LETTER GHA\n104D2         ; mapped                 ; 104FA         # 9.0  OSAGE CAPITAL LETTER ZA\n104D3         ; mapped                 ; 104FB         # 9.0  OSAGE CAPITAL LETTER ZHA\n104D4..104D7  ; disallowed                             # NA   <reserved-104D4>..<reserved-104D7>\n104D8..104FB  ; valid                                  # 9.0  OSAGE SMALL LETTER A..OSAGE SMALL LETTER ZHA\n104FC..104FF  ; disallowed                             # NA   <reserved-104FC>..<reserved-104FF>\n10500..10527  ; valid                                  # 7.0  ELBASAN LETTER A..ELBASAN LETTER KHE\n10528..1052F  ; disallowed                             # NA   <reserved-10528>..<reserved-1052F>\n10530..10563  ; valid                                  # 7.0  CAUCASIAN ALBANIAN LETTER ALT..CAUCASIAN ALBANIAN LETTER KIW\n10564..1056E  ; disallowed                             # NA   <reserved-10564>..<reserved-1056E>\n1056F         ; valid                  ;      ; NV8    # 7.0  CAUCASIAN ALBANIAN CITATION MARK\n10570         ; mapped                 ; 10597         # 14.0 VITHKUQI CAPITAL LETTER A\n10571         ; mapped                 ; 10598         # 14.0 VITHKUQI CAPITAL LETTER BBE\n10572         ; mapped                 ; 10599         # 14.0 VITHKUQI CAPITAL LETTER BE\n10573         ; mapped                 ; 1059A         # 14.0 VITHKUQI CAPITAL LETTER CE\n10574         ; mapped                 ; 1059B         # 14.0 VITHKUQI CAPITAL LETTER CHE\n10575         ; mapped                 ; 1059C         # 14.0 VITHKUQI CAPITAL LETTER DE\n10576         ; mapped                 ; 1059D         # 14.0 VITHKUQI CAPITAL LETTER DHE\n10577         ; mapped                 ; 1059E         # 14.0 VITHKUQI CAPITAL LETTER EI\n10578         ; mapped                 ; 1059F         # 14.0 VITHKUQI CAPITAL LETTER E\n10579         ; mapped                 ; 105A0         # 14.0 VITHKUQI CAPITAL LETTER FE\n1057A         ; mapped                 ; 105A1         # 14.0 VITHKUQI CAPITAL LETTER GA\n1057B         ; disallowed                             # NA   <reserved-1057B>\n1057C         ; mapped                 ; 105A3         # 14.0 VITHKUQI CAPITAL LETTER HA\n1057D         ; mapped                 ; 105A4         # 14.0 VITHKUQI CAPITAL LETTER HHA\n1057E         ; mapped                 ; 105A5         # 14.0 VITHKUQI CAPITAL LETTER I\n1057F         ; mapped                 ; 105A6         # 14.0 VITHKUQI CAPITAL LETTER IJE\n10580         ; mapped                 ; 105A7         # 14.0 VITHKUQI CAPITAL LETTER JE\n10581         ; mapped                 ; 105A8         # 14.0 VITHKUQI CAPITAL LETTER KA\n10582         ; mapped                 ; 105A9         # 14.0 VITHKUQI CAPITAL LETTER LA\n10583         ; mapped                 ; 105AA         # 14.0 VITHKUQI CAPITAL LETTER LLA\n10584         ; mapped                 ; 105AB         # 14.0 VITHKUQI CAPITAL LETTER ME\n10585         ; mapped                 ; 105AC         # 14.0 VITHKUQI CAPITAL LETTER NE\n10586         ; mapped                 ; 105AD         # 14.0 VITHKUQI CAPITAL LETTER NJE\n10587         ; mapped                 ; 105AE         # 14.0 VITHKUQI CAPITAL LETTER O\n10588         ; mapped                 ; 105AF         # 14.0 VITHKUQI CAPITAL LETTER PE\n10589         ; mapped                 ; 105B0         # 14.0 VITHKUQI CAPITAL LETTER QA\n1058A         ; mapped                 ; 105B1         # 14.0 VITHKUQI CAPITAL LETTER RE\n1058B         ; disallowed                             # NA   <reserved-1058B>\n1058C         ; mapped                 ; 105B3         # 14.0 VITHKUQI CAPITAL LETTER SE\n1058D         ; mapped                 ; 105B4         # 14.0 VITHKUQI CAPITAL LETTER SHE\n1058E         ; mapped                 ; 105B5         # 14.0 VITHKUQI CAPITAL LETTER TE\n1058F         ; mapped                 ; 105B6         # 14.0 VITHKUQI CAPITAL LETTER THE\n10590         ; mapped                 ; 105B7         # 14.0 VITHKUQI CAPITAL LETTER U\n10591         ; mapped                 ; 105B8         # 14.0 VITHKUQI CAPITAL LETTER VE\n10592         ; mapped                 ; 105B9         # 14.0 VITHKUQI CAPITAL LETTER XE\n10593         ; disallowed                             # NA   <reserved-10593>\n10594         ; mapped                 ; 105BB         # 14.0 VITHKUQI CAPITAL LETTER Y\n10595         ; mapped                 ; 105BC         # 14.0 VITHKUQI CAPITAL LETTER ZE\n10596         ; disallowed                             # NA   <reserved-10596>\n10597..105A1  ; valid                                  # 14.0 VITHKUQI SMALL LETTER A..VITHKUQI SMALL LETTER GA\n105A2         ; disallowed                             # NA   <reserved-105A2>\n105A3..105B1  ; valid                                  # 14.0 VITHKUQI SMALL LETTER HA..VITHKUQI SMALL LETTER RE\n105B2         ; disallowed                             # NA   <reserved-105B2>\n105B3..105B9  ; valid                                  # 14.0 VITHKUQI SMALL LETTER SE..VITHKUQI SMALL LETTER XE\n105BA         ; disallowed                             # NA   <reserved-105BA>\n105BB..105BC  ; valid                                  # 14.0 VITHKUQI SMALL LETTER Y..VITHKUQI SMALL LETTER ZE\n105BD..105FF  ; disallowed                             # NA   <reserved-105BD>..<reserved-105FF>\n10600..10736  ; valid                                  # 7.0  LINEAR A SIGN AB001..LINEAR A SIGN A664\n10737..1073F  ; disallowed                             # NA   <reserved-10737>..<reserved-1073F>\n10740..10755  ; valid                                  # 7.0  LINEAR A SIGN A701 A..LINEAR A SIGN A732 JE\n10756..1075F  ; disallowed                             # NA   <reserved-10756>..<reserved-1075F>\n10760..10767  ; valid                                  # 7.0  LINEAR A SIGN A800..LINEAR A SIGN A807\n10768..1077F  ; disallowed                             # NA   <reserved-10768>..<reserved-1077F>\n10780         ; valid                                  # 14.0 MODIFIER LETTER SMALL CAPITAL AA\n10781         ; mapped                 ; 02D0          # 14.0 MODIFIER LETTER SUPERSCRIPT TRIANGULAR COLON\n10782         ; mapped                 ; 02D1          # 14.0 MODIFIER LETTER SUPERSCRIPT HALF TRIANGULAR COLON\n10783         ; mapped                 ; 00E6          # 14.0 MODIFIER LETTER SMALL AE\n10784         ; mapped                 ; 0299          # 14.0 MODIFIER LETTER SMALL CAPITAL B\n10785         ; mapped                 ; 0253          # 14.0 MODIFIER LETTER SMALL B WITH HOOK\n10786         ; disallowed                             # NA   <reserved-10786>\n10787         ; mapped                 ; 02A3          # 14.0 MODIFIER LETTER SMALL DZ DIGRAPH\n10788         ; mapped                 ; AB66          # 14.0 MODIFIER LETTER SMALL DZ DIGRAPH WITH RETROFLEX HOOK\n10789         ; mapped                 ; 02A5          # 14.0 MODIFIER LETTER SMALL DZ DIGRAPH WITH CURL\n1078A         ; mapped                 ; 02A4          # 14.0 MODIFIER LETTER SMALL DEZH DIGRAPH\n1078B         ; mapped                 ; 0256          # 14.0 MODIFIER LETTER SMALL D WITH TAIL\n1078C         ; mapped                 ; 0257          # 14.0 MODIFIER LETTER SMALL D WITH HOOK\n1078D         ; mapped                 ; 1D91          # 14.0 MODIFIER LETTER SMALL D WITH HOOK AND TAIL\n1078E         ; mapped                 ; 0258          # 14.0 MODIFIER LETTER SMALL REVERSED E\n1078F         ; mapped                 ; 025E          # 14.0 MODIFIER LETTER SMALL CLOSED REVERSED OPEN E\n10790         ; mapped                 ; 02A9          # 14.0 MODIFIER LETTER SMALL FENG DIGRAPH\n10791         ; mapped                 ; 0264          # 14.0 MODIFIER LETTER SMALL RAMS HORN\n10792         ; mapped                 ; 0262          # 14.0 MODIFIER LETTER SMALL CAPITAL G\n10793         ; mapped                 ; 0260          # 14.0 MODIFIER LETTER SMALL G WITH HOOK\n10794         ; mapped                 ; 029B          # 14.0 MODIFIER LETTER SMALL CAPITAL G WITH HOOK\n10795         ; mapped                 ; 0127          # 14.0 MODIFIER LETTER SMALL H WITH STROKE\n10796         ; mapped                 ; 029C          # 14.0 MODIFIER LETTER SMALL CAPITAL H\n10797         ; mapped                 ; 0267          # 14.0 MODIFIER LETTER SMALL HENG WITH HOOK\n10798         ; mapped                 ; 0284          # 14.0 MODIFIER LETTER SMALL DOTLESS J WITH STROKE AND HOOK\n10799         ; mapped                 ; 02AA          # 14.0 MODIFIER LETTER SMALL LS DIGRAPH\n1079A         ; mapped                 ; 02AB          # 14.0 MODIFIER LETTER SMALL LZ DIGRAPH\n1079B         ; mapped                 ; 026C          # 14.0 MODIFIER LETTER SMALL L WITH BELT\n1079C         ; mapped                 ; 1DF04         # 14.0 MODIFIER LETTER SMALL CAPITAL L WITH BELT\n1079D         ; mapped                 ; A78E          # 14.0 MODIFIER LETTER SMALL L WITH RETROFLEX HOOK AND BELT\n1079E         ; mapped                 ; 026E          # 14.0 MODIFIER LETTER SMALL LEZH\n1079F         ; mapped                 ; 1DF05         # 14.0 MODIFIER LETTER SMALL LEZH WITH RETROFLEX HOOK\n107A0         ; mapped                 ; 028E          # 14.0 MODIFIER LETTER SMALL TURNED Y\n107A1         ; mapped                 ; 1DF06         # 14.0 MODIFIER LETTER SMALL TURNED Y WITH BELT\n107A2         ; mapped                 ; 00F8          # 14.0 MODIFIER LETTER SMALL O WITH STROKE\n107A3         ; mapped                 ; 0276          # 14.0 MODIFIER LETTER SMALL CAPITAL OE\n107A4         ; mapped                 ; 0277          # 14.0 MODIFIER LETTER SMALL CLOSED OMEGA\n107A5         ; mapped                 ; 0071          # 14.0 MODIFIER LETTER SMALL Q\n107A6         ; mapped                 ; 027A          # 14.0 MODIFIER LETTER SMALL TURNED R WITH LONG LEG\n107A7         ; mapped                 ; 1DF08         # 14.0 MODIFIER LETTER SMALL TURNED R WITH LONG LEG AND RETROFLEX HOOK\n107A8         ; mapped                 ; 027D          # 14.0 MODIFIER LETTER SMALL R WITH TAIL\n107A9         ; mapped                 ; 027E          # 14.0 MODIFIER LETTER SMALL R WITH FISHHOOK\n107AA         ; mapped                 ; 0280          # 14.0 MODIFIER LETTER SMALL CAPITAL R\n107AB         ; mapped                 ; 02A8          # 14.0 MODIFIER LETTER SMALL TC DIGRAPH WITH CURL\n107AC         ; mapped                 ; 02A6          # 14.0 MODIFIER LETTER SMALL TS DIGRAPH\n107AD         ; mapped                 ; AB67          # 14.0 MODIFIER LETTER SMALL TS DIGRAPH WITH RETROFLEX HOOK\n107AE         ; mapped                 ; 02A7          # 14.0 MODIFIER LETTER SMALL TESH DIGRAPH\n107AF         ; mapped                 ; 0288          # 14.0 MODIFIER LETTER SMALL T WITH RETROFLEX HOOK\n107B0         ; mapped                 ; 2C71          # 14.0 MODIFIER LETTER SMALL V WITH RIGHT HOOK\n107B1         ; disallowed                             # NA   <reserved-107B1>\n107B2         ; mapped                 ; 028F          # 14.0 MODIFIER LETTER SMALL CAPITAL Y\n107B3         ; mapped                 ; 02A1          # 14.0 MODIFIER LETTER GLOTTAL STOP WITH STROKE\n107B4         ; mapped                 ; 02A2          # 14.0 MODIFIER LETTER REVERSED GLOTTAL STOP WITH STROKE\n107B5         ; mapped                 ; 0298          # 14.0 MODIFIER LETTER BILABIAL CLICK\n107B6         ; mapped                 ; 01C0          # 14.0 MODIFIER LETTER DENTAL CLICK\n107B7         ; mapped                 ; 01C1          # 14.0 MODIFIER LETTER LATERAL CLICK\n107B8         ; mapped                 ; 01C2          # 14.0 MODIFIER LETTER ALVEOLAR CLICK\n107B9         ; mapped                 ; 1DF0A         # 14.0 MODIFIER LETTER RETROFLEX CLICK WITH RETROFLEX HOOK\n107BA         ; mapped                 ; 1DF1E         # 14.0 MODIFIER LETTER SMALL S WITH CURL\n107BB..107FF  ; disallowed                             # NA   <reserved-107BB>..<reserved-107FF>\n10800..10805  ; valid                                  # 4.0  CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA\n10806..10807  ; disallowed                             # NA   <reserved-10806>..<reserved-10807>\n10808         ; valid                                  # 4.0  CYPRIOT SYLLABLE JO\n10809         ; disallowed                             # NA   <reserved-10809>\n1080A..10835  ; valid                                  # 4.0  CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO\n10836         ; disallowed                             # NA   <reserved-10836>\n10837..10838  ; valid                                  # 4.0  CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE\n10839..1083B  ; disallowed                             # NA   <reserved-10839>..<reserved-1083B>\n1083C         ; valid                                  # 4.0  CYPRIOT SYLLABLE ZA\n1083D..1083E  ; disallowed                             # NA   <reserved-1083D>..<reserved-1083E>\n1083F         ; valid                                  # 4.0  CYPRIOT SYLLABLE ZO\n10840..10855  ; valid                                  # 5.2  IMPERIAL ARAMAIC LETTER ALEPH..IMPERIAL ARAMAIC LETTER TAW\n10856         ; disallowed                             # NA   <reserved-10856>\n10857..1085F  ; valid                  ;      ; NV8    # 5.2  IMPERIAL ARAMAIC SECTION SIGN..IMPERIAL ARAMAIC NUMBER TEN THOUSAND\n10860..10876  ; valid                                  # 7.0  PALMYRENE LETTER ALEPH..PALMYRENE LETTER TAW\n10877..1087F  ; valid                  ;      ; NV8    # 7.0  PALMYRENE LEFT-POINTING FLEURON..PALMYRENE NUMBER TWENTY\n10880..1089E  ; valid                                  # 7.0  NABATAEAN LETTER FINAL ALEPH..NABATAEAN LETTER TAW\n1089F..108A6  ; disallowed                             # NA   <reserved-1089F>..<reserved-108A6>\n108A7..108AF  ; valid                  ;      ; NV8    # 7.0  NABATAEAN NUMBER ONE..NABATAEAN NUMBER ONE HUNDRED\n108B0..108DF  ; disallowed                             # NA   <reserved-108B0>..<reserved-108DF>\n108E0..108F2  ; valid                                  # 8.0  HATRAN LETTER ALEPH..HATRAN LETTER QOPH\n108F3         ; disallowed                             # NA   <reserved-108F3>\n108F4..108F5  ; valid                                  # 8.0  HATRAN LETTER SHIN..HATRAN LETTER TAW\n108F6..108FA  ; disallowed                             # NA   <reserved-108F6>..<reserved-108FA>\n108FB..108FF  ; valid                  ;      ; NV8    # 8.0  HATRAN NUMBER ONE..HATRAN NUMBER ONE HUNDRED\n10900..10915  ; valid                                  # 5.0  PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU\n10916..10919  ; valid                  ;      ; NV8    # 5.0  PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER ONE HUNDRED\n1091A..1091B  ; valid                  ;      ; NV8    # 5.2  PHOENICIAN NUMBER TWO..PHOENICIAN NUMBER THREE\n1091C..1091E  ; disallowed                             # NA   <reserved-1091C>..<reserved-1091E>\n1091F         ; valid                  ;      ; NV8    # 5.0  PHOENICIAN WORD SEPARATOR\n10920..10939  ; valid                                  # 5.1  LYDIAN LETTER A..LYDIAN LETTER C\n1093A..1093E  ; disallowed                             # NA   <reserved-1093A>..<reserved-1093E>\n1093F         ; valid                  ;      ; NV8    # 5.1  LYDIAN TRIANGULAR MARK\n10940..1097F  ; disallowed                             # NA   <reserved-10940>..<reserved-1097F>\n10980..109B7  ; valid                                  # 6.1  MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA\n109B8..109BB  ; disallowed                             # NA   <reserved-109B8>..<reserved-109BB>\n109BC..109BD  ; valid                  ;      ; NV8    # 8.0  MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS..MEROITIC CURSIVE FRACTION ONE HALF\n109BE..109BF  ; valid                                  # 6.1  MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN\n109C0..109CF  ; valid                  ;      ; NV8    # 8.0  MEROITIC CURSIVE NUMBER ONE..MEROITIC CURSIVE NUMBER SEVENTY\n109D0..109D1  ; disallowed                             # NA   <reserved-109D0>..<reserved-109D1>\n109D2..109FF  ; valid                  ;      ; NV8    # 8.0  MEROITIC CURSIVE NUMBER ONE HUNDRED..MEROITIC CURSIVE FRACTION TEN TWELFTHS\n10A00..10A03  ; valid                                  # 4.1  KHAROSHTHI LETTER A..KHAROSHTHI VOWEL SIGN VOCALIC R\n10A04         ; disallowed                             # NA   <reserved-10A04>\n10A05..10A06  ; valid                                  # 4.1  KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O\n10A07..10A0B  ; disallowed                             # NA   <reserved-10A07>..<reserved-10A0B>\n10A0C..10A13  ; valid                                  # 4.1  KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI LETTER GHA\n10A14         ; disallowed                             # NA   <reserved-10A14>\n10A15..10A17  ; valid                                  # 4.1  KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA\n10A18         ; disallowed                             # NA   <reserved-10A18>\n10A19..10A33  ; valid                                  # 4.1  KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER TTTHA\n10A34..10A35  ; valid                                  # 11.0 KHAROSHTHI LETTER TTTA..KHAROSHTHI LETTER VHA\n10A36..10A37  ; disallowed                             # NA   <reserved-10A36>..<reserved-10A37>\n10A38..10A3A  ; valid                                  # 4.1  KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW\n10A3B..10A3E  ; disallowed                             # NA   <reserved-10A3B>..<reserved-10A3E>\n10A3F         ; valid                                  # 4.1  KHAROSHTHI VIRAMA\n10A40..10A47  ; valid                  ;      ; NV8    # 4.1  KHAROSHTHI DIGIT ONE..KHAROSHTHI NUMBER ONE THOUSAND\n10A48         ; valid                  ;      ; NV8    # 11.0 KHAROSHTHI FRACTION ONE HALF\n10A49..10A4F  ; disallowed                             # NA   <reserved-10A49>..<reserved-10A4F>\n10A50..10A58  ; valid                  ;      ; NV8    # 4.1  KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES\n10A59..10A5F  ; disallowed                             # NA   <reserved-10A59>..<reserved-10A5F>\n10A60..10A7C  ; valid                                  # 5.2  OLD SOUTH ARABIAN LETTER HE..OLD SOUTH ARABIAN LETTER THETH\n10A7D..10A7F  ; valid                  ;      ; NV8    # 5.2  OLD SOUTH ARABIAN NUMBER ONE..OLD SOUTH ARABIAN NUMERIC INDICATOR\n10A80..10A9C  ; valid                                  # 7.0  OLD NORTH ARABIAN LETTER HEH..OLD NORTH ARABIAN LETTER ZAH\n10A9D..10A9F  ; valid                  ;      ; NV8    # 7.0  OLD NORTH ARABIAN NUMBER ONE..OLD NORTH ARABIAN NUMBER TWENTY\n10AA0..10ABF  ; disallowed                             # NA   <reserved-10AA0>..<reserved-10ABF>\n10AC0..10AC7  ; valid                                  # 7.0  MANICHAEAN LETTER ALEPH..MANICHAEAN LETTER WAW\n10AC8         ; valid                  ;      ; NV8    # 7.0  MANICHAEAN SIGN UD\n10AC9..10AE6  ; valid                                  # 7.0  MANICHAEAN LETTER ZAYIN..MANICHAEAN ABBREVIATION MARK BELOW\n10AE7..10AEA  ; disallowed                             # NA   <reserved-10AE7>..<reserved-10AEA>\n10AEB..10AF6  ; valid                  ;      ; NV8    # 7.0  MANICHAEAN NUMBER ONE..MANICHAEAN PUNCTUATION LINE FILLER\n10AF7..10AFF  ; disallowed                             # NA   <reserved-10AF7>..<reserved-10AFF>\n10B00..10B35  ; valid                                  # 5.2  AVESTAN LETTER A..AVESTAN LETTER HE\n10B36..10B38  ; disallowed                             # NA   <reserved-10B36>..<reserved-10B38>\n10B39..10B3F  ; valid                  ;      ; NV8    # 5.2  AVESTAN ABBREVIATION MARK..LARGE ONE RING OVER TWO RINGS PUNCTUATION\n10B40..10B55  ; valid                                  # 5.2  INSCRIPTIONAL PARTHIAN LETTER ALEPH..INSCRIPTIONAL PARTHIAN LETTER TAW\n10B56..10B57  ; disallowed                             # NA   <reserved-10B56>..<reserved-10B57>\n10B58..10B5F  ; valid                  ;      ; NV8    # 5.2  INSCRIPTIONAL PARTHIAN NUMBER ONE..INSCRIPTIONAL PARTHIAN NUMBER ONE THOUSAND\n10B60..10B72  ; valid                                  # 5.2  INSCRIPTIONAL PAHLAVI LETTER ALEPH..INSCRIPTIONAL PAHLAVI LETTER TAW\n10B73..10B77  ; disallowed                             # NA   <reserved-10B73>..<reserved-10B77>\n10B78..10B7F  ; valid                  ;      ; NV8    # 5.2  INSCRIPTIONAL PAHLAVI NUMBER ONE..INSCRIPTIONAL PAHLAVI NUMBER ONE THOUSAND\n10B80..10B91  ; valid                                  # 7.0  PSALTER PAHLAVI LETTER ALEPH..PSALTER PAHLAVI LETTER TAW\n10B92..10B98  ; disallowed                             # NA   <reserved-10B92>..<reserved-10B98>\n10B99..10B9C  ; valid                  ;      ; NV8    # 7.0  PSALTER PAHLAVI SECTION MARK..PSALTER PAHLAVI FOUR DOTS WITH DOT\n10B9D..10BA8  ; disallowed                             # NA   <reserved-10B9D>..<reserved-10BA8>\n10BA9..10BAF  ; valid                  ;      ; NV8    # 7.0  PSALTER PAHLAVI NUMBER ONE..PSALTER PAHLAVI NUMBER ONE HUNDRED\n10BB0..10BFF  ; disallowed                             # NA   <reserved-10BB0>..<reserved-10BFF>\n10C00..10C48  ; valid                                  # 5.2  OLD TURKIC LETTER ORKHON A..OLD TURKIC LETTER ORKHON BASH\n10C49..10C7F  ; disallowed                             # NA   <reserved-10C49>..<reserved-10C7F>\n10C80         ; mapped                 ; 10CC0         # 8.0  OLD HUNGARIAN CAPITAL LETTER A\n10C81         ; mapped                 ; 10CC1         # 8.0  OLD HUNGARIAN CAPITAL LETTER AA\n10C82         ; mapped                 ; 10CC2         # 8.0  OLD HUNGARIAN CAPITAL LETTER EB\n10C83         ; mapped                 ; 10CC3         # 8.0  OLD HUNGARIAN CAPITAL LETTER AMB\n10C84         ; mapped                 ; 10CC4         # 8.0  OLD HUNGARIAN CAPITAL LETTER EC\n10C85         ; mapped                 ; 10CC5         # 8.0  OLD HUNGARIAN CAPITAL LETTER ENC\n10C86         ; mapped                 ; 10CC6         # 8.0  OLD HUNGARIAN CAPITAL LETTER ECS\n10C87         ; mapped                 ; 10CC7         # 8.0  OLD HUNGARIAN CAPITAL LETTER ED\n10C88         ; mapped                 ; 10CC8         # 8.0  OLD HUNGARIAN CAPITAL LETTER AND\n10C89         ; mapped                 ; 10CC9         # 8.0  OLD HUNGARIAN CAPITAL LETTER E\n10C8A         ; mapped                 ; 10CCA         # 8.0  OLD HUNGARIAN CAPITAL LETTER CLOSE E\n10C8B         ; mapped                 ; 10CCB         # 8.0  OLD HUNGARIAN CAPITAL LETTER EE\n10C8C         ; mapped                 ; 10CCC         # 8.0  OLD HUNGARIAN CAPITAL LETTER EF\n10C8D         ; mapped                 ; 10CCD         # 8.0  OLD HUNGARIAN CAPITAL LETTER EG\n10C8E         ; mapped                 ; 10CCE         # 8.0  OLD HUNGARIAN CAPITAL LETTER EGY\n10C8F         ; mapped                 ; 10CCF         # 8.0  OLD HUNGARIAN CAPITAL LETTER EH\n10C90         ; mapped                 ; 10CD0         # 8.0  OLD HUNGARIAN CAPITAL LETTER I\n10C91         ; mapped                 ; 10CD1         # 8.0  OLD HUNGARIAN CAPITAL LETTER II\n10C92         ; mapped                 ; 10CD2         # 8.0  OLD HUNGARIAN CAPITAL LETTER EJ\n10C93         ; mapped                 ; 10CD3         # 8.0  OLD HUNGARIAN CAPITAL LETTER EK\n10C94         ; mapped                 ; 10CD4         # 8.0  OLD HUNGARIAN CAPITAL LETTER AK\n10C95         ; mapped                 ; 10CD5         # 8.0  OLD HUNGARIAN CAPITAL LETTER UNK\n10C96         ; mapped                 ; 10CD6         # 8.0  OLD HUNGARIAN CAPITAL LETTER EL\n10C97         ; mapped                 ; 10CD7         # 8.0  OLD HUNGARIAN CAPITAL LETTER ELY\n10C98         ; mapped                 ; 10CD8         # 8.0  OLD HUNGARIAN CAPITAL LETTER EM\n10C99         ; mapped                 ; 10CD9         # 8.0  OLD HUNGARIAN CAPITAL LETTER EN\n10C9A         ; mapped                 ; 10CDA         # 8.0  OLD HUNGARIAN CAPITAL LETTER ENY\n10C9B         ; mapped                 ; 10CDB         # 8.0  OLD HUNGARIAN CAPITAL LETTER O\n10C9C         ; mapped                 ; 10CDC         # 8.0  OLD HUNGARIAN CAPITAL LETTER OO\n10C9D         ; mapped                 ; 10CDD         # 8.0  OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG OE\n10C9E         ; mapped                 ; 10CDE         # 8.0  OLD HUNGARIAN CAPITAL LETTER RUDIMENTA OE\n10C9F         ; mapped                 ; 10CDF         # 8.0  OLD HUNGARIAN CAPITAL LETTER OEE\n10CA0         ; mapped                 ; 10CE0         # 8.0  OLD HUNGARIAN CAPITAL LETTER EP\n10CA1         ; mapped                 ; 10CE1         # 8.0  OLD HUNGARIAN CAPITAL LETTER EMP\n10CA2         ; mapped                 ; 10CE2         # 8.0  OLD HUNGARIAN CAPITAL LETTER ER\n10CA3         ; mapped                 ; 10CE3         # 8.0  OLD HUNGARIAN CAPITAL LETTER SHORT ER\n10CA4         ; mapped                 ; 10CE4         # 8.0  OLD HUNGARIAN CAPITAL LETTER ES\n10CA5         ; mapped                 ; 10CE5         # 8.0  OLD HUNGARIAN CAPITAL LETTER ESZ\n10CA6         ; mapped                 ; 10CE6         # 8.0  OLD HUNGARIAN CAPITAL LETTER ET\n10CA7         ; mapped                 ; 10CE7         # 8.0  OLD HUNGARIAN CAPITAL LETTER ENT\n10CA8         ; mapped                 ; 10CE8         # 8.0  OLD HUNGARIAN CAPITAL LETTER ETY\n10CA9         ; mapped                 ; 10CE9         # 8.0  OLD HUNGARIAN CAPITAL LETTER ECH\n10CAA         ; mapped                 ; 10CEA         # 8.0  OLD HUNGARIAN CAPITAL LETTER U\n10CAB         ; mapped                 ; 10CEB         # 8.0  OLD HUNGARIAN CAPITAL LETTER UU\n10CAC         ; mapped                 ; 10CEC         # 8.0  OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG UE\n10CAD         ; mapped                 ; 10CED         # 8.0  OLD HUNGARIAN CAPITAL LETTER RUDIMENTA UE\n10CAE         ; mapped                 ; 10CEE         # 8.0  OLD HUNGARIAN CAPITAL LETTER EV\n10CAF         ; mapped                 ; 10CEF         # 8.0  OLD HUNGARIAN CAPITAL LETTER EZ\n10CB0         ; mapped                 ; 10CF0         # 8.0  OLD HUNGARIAN CAPITAL LETTER EZS\n10CB1         ; mapped                 ; 10CF1         # 8.0  OLD HUNGARIAN CAPITAL LETTER ENT-SHAPED SIGN\n10CB2         ; mapped                 ; 10CF2         # 8.0  OLD HUNGARIAN CAPITAL LETTER US\n10CB3..10CBF  ; disallowed                             # NA   <reserved-10CB3>..<reserved-10CBF>\n10CC0..10CF2  ; valid                                  # 8.0  OLD HUNGARIAN SMALL LETTER A..OLD HUNGARIAN SMALL LETTER US\n10CF3..10CF9  ; disallowed                             # NA   <reserved-10CF3>..<reserved-10CF9>\n10CFA..10CFF  ; valid                  ;      ; NV8    # 8.0  OLD HUNGARIAN NUMBER ONE..OLD HUNGARIAN NUMBER ONE THOUSAND\n10D00..10D27  ; valid                                  # 11.0 HANIFI ROHINGYA LETTER A..HANIFI ROHINGYA SIGN TASSI\n10D28..10D2F  ; disallowed                             # NA   <reserved-10D28>..<reserved-10D2F>\n10D30..10D39  ; valid                                  # 11.0 HANIFI ROHINGYA DIGIT ZERO..HANIFI ROHINGYA DIGIT NINE\n10D3A..10E5F  ; disallowed                             # NA   <reserved-10D3A>..<reserved-10E5F>\n10E60..10E7E  ; valid                  ;      ; NV8    # 5.2  RUMI DIGIT ONE..RUMI FRACTION TWO THIRDS\n10E7F         ; disallowed                             # NA   <reserved-10E7F>\n10E80..10EA9  ; valid                                  # 13.0 YEZIDI LETTER ELIF..YEZIDI LETTER ET\n10EAA         ; disallowed                             # NA   <reserved-10EAA>\n10EAB..10EAC  ; valid                                  # 13.0 YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK\n10EAD         ; valid                  ;      ; NV8    # 13.0 YEZIDI HYPHENATION MARK\n10EAE..10EAF  ; disallowed                             # NA   <reserved-10EAE>..<reserved-10EAF>\n10EB0..10EB1  ; valid                                  # 13.0 YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE\n10EB2..10EFC  ; disallowed                             # NA   <reserved-10EB2>..<reserved-10EFC>\n10EFD..10EFF  ; valid                                  # 15.0 ARABIC SMALL LOW WORD SAKTA..ARABIC SMALL LOW WORD MADDA\n10F00..10F1C  ; valid                                  # 11.0 OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL\n10F1D..10F26  ; valid                  ;      ; NV8    # 11.0 OLD SOGDIAN NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF\n10F27         ; valid                                  # 11.0 OLD SOGDIAN LIGATURE AYIN-DALETH\n10F28..10F2F  ; disallowed                             # NA   <reserved-10F28>..<reserved-10F2F>\n10F30..10F50  ; valid                                  # 11.0 SOGDIAN LETTER ALEPH..SOGDIAN COMBINING STROKE BELOW\n10F51..10F59  ; valid                  ;      ; NV8    # 11.0 SOGDIAN NUMBER ONE..SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT\n10F5A..10F6F  ; disallowed                             # NA   <reserved-10F5A>..<reserved-10F6F>\n10F70..10F85  ; valid                                  # 14.0 OLD UYGHUR LETTER ALEPH..OLD UYGHUR COMBINING TWO DOTS BELOW\n10F86..10F89  ; valid                  ;      ; NV8    # 14.0 OLD UYGHUR PUNCTUATION BAR..OLD UYGHUR PUNCTUATION FOUR DOTS\n10F8A..10FAF  ; disallowed                             # NA   <reserved-10F8A>..<reserved-10FAF>\n10FB0..10FC4  ; valid                                  # 13.0 CHORASMIAN LETTER ALEPH..CHORASMIAN LETTER TAW\n10FC5..10FCB  ; valid                  ;      ; NV8    # 13.0 CHORASMIAN NUMBER ONE..CHORASMIAN NUMBER ONE HUNDRED\n10FCC..10FDF  ; disallowed                             # NA   <reserved-10FCC>..<reserved-10FDF>\n10FE0..10FF6  ; valid                                  # 12.0 ELYMAIC LETTER ALEPH..ELYMAIC LIGATURE ZAYIN-YODH\n10FF7..10FFF  ; disallowed                             # NA   <reserved-10FF7>..<reserved-10FFF>\n11000..11046  ; valid                                  # 6.0  BRAHMI SIGN CANDRABINDU..BRAHMI VIRAMA\n11047..1104D  ; valid                  ;      ; NV8    # 6.0  BRAHMI DANDA..BRAHMI PUNCTUATION LOTUS\n1104E..11051  ; disallowed                             # NA   <reserved-1104E>..<reserved-11051>\n11052..11065  ; valid                  ;      ; NV8    # 6.0  BRAHMI NUMBER ONE..BRAHMI NUMBER ONE THOUSAND\n11066..1106F  ; valid                                  # 6.0  BRAHMI DIGIT ZERO..BRAHMI DIGIT NINE\n11070..11075  ; valid                                  # 14.0 BRAHMI SIGN OLD TAMIL VIRAMA..BRAHMI LETTER OLD TAMIL LLA\n11076..1107E  ; disallowed                             # NA   <reserved-11076>..<reserved-1107E>\n1107F         ; valid                                  # 7.0  BRAHMI NUMBER JOINER\n11080..110BA  ; valid                                  # 5.2  KAITHI SIGN CANDRABINDU..KAITHI SIGN NUKTA\n110BB..110BC  ; valid                  ;      ; NV8    # 5.2  KAITHI ABBREVIATION SIGN..KAITHI ENUMERATION SIGN\n110BD         ; disallowed                             # 5.2  KAITHI NUMBER SIGN\n110BE..110C1  ; valid                  ;      ; NV8    # 5.2  KAITHI SECTION MARK..KAITHI DOUBLE DANDA\n110C2         ; valid                                  # 14.0 KAITHI VOWEL SIGN VOCALIC R\n110C3..110CC  ; disallowed                             # NA   <reserved-110C3>..<reserved-110CC>\n110CD         ; disallowed                             # 11.0 KAITHI NUMBER SIGN ABOVE\n110CE..110CF  ; disallowed                             # NA   <reserved-110CE>..<reserved-110CF>\n110D0..110E8  ; valid                                  # 6.1  SORA SOMPENG LETTER SAH..SORA SOMPENG LETTER MAE\n110E9..110EF  ; disallowed                             # NA   <reserved-110E9>..<reserved-110EF>\n110F0..110F9  ; valid                                  # 6.1  SORA SOMPENG DIGIT ZERO..SORA SOMPENG DIGIT NINE\n110FA..110FF  ; disallowed                             # NA   <reserved-110FA>..<reserved-110FF>\n11100..11134  ; valid                                  # 6.1  CHAKMA SIGN CANDRABINDU..CHAKMA MAAYYAA\n11135         ; disallowed                             # NA   <reserved-11135>\n11136..1113F  ; valid                                  # 6.1  CHAKMA DIGIT ZERO..CHAKMA DIGIT NINE\n11140..11143  ; valid                  ;      ; NV8    # 6.1  CHAKMA SECTION MARK..CHAKMA QUESTION MARK\n11144..11146  ; valid                                  # 11.0 CHAKMA LETTER LHAA..CHAKMA VOWEL SIGN EI\n11147         ; valid                                  # 13.0 CHAKMA LETTER VAA\n11148..1114F  ; disallowed                             # NA   <reserved-11148>..<reserved-1114F>\n11150..11173  ; valid                                  # 7.0  MAHAJANI LETTER A..MAHAJANI SIGN NUKTA\n11174..11175  ; valid                  ;      ; NV8    # 7.0  MAHAJANI ABBREVIATION SIGN..MAHAJANI SECTION MARK\n11176         ; valid                                  # 7.0  MAHAJANI LIGATURE SHRI\n11177..1117F  ; disallowed                             # NA   <reserved-11177>..<reserved-1117F>\n11180..111C4  ; valid                                  # 6.1  SHARADA SIGN CANDRABINDU..SHARADA OM\n111C5..111C8  ; valid                  ;      ; NV8    # 6.1  SHARADA DANDA..SHARADA SEPARATOR\n111C9..111CC  ; valid                                  # 8.0  SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK\n111CD         ; valid                  ;      ; NV8    # 7.0  SHARADA SUTRA MARK\n111CE..111CF  ; valid                                  # 13.0 SHARADA VOWEL SIGN PRISHTHAMATRA E..SHARADA SIGN INVERTED CANDRABINDU\n111D0..111D9  ; valid                                  # 6.1  SHARADA DIGIT ZERO..SHARADA DIGIT NINE\n111DA         ; valid                                  # 7.0  SHARADA EKAM\n111DB         ; valid                  ;      ; NV8    # 8.0  SHARADA SIGN SIDDHAM\n111DC         ; valid                                  # 8.0  SHARADA HEADSTROKE\n111DD..111DF  ; valid                  ;      ; NV8    # 8.0  SHARADA CONTINUATION SIGN..SHARADA SECTION MARK-2\n111E0         ; disallowed                             # NA   <reserved-111E0>\n111E1..111F4  ; valid                  ;      ; NV8    # 7.0  SINHALA ARCHAIC DIGIT ONE..SINHALA ARCHAIC NUMBER ONE THOUSAND\n111F5..111FF  ; disallowed                             # NA   <reserved-111F5>..<reserved-111FF>\n11200..11211  ; valid                                  # 7.0  KHOJKI LETTER A..KHOJKI LETTER JJA\n11212         ; disallowed                             # NA   <reserved-11212>\n11213..11237  ; valid                                  # 7.0  KHOJKI LETTER NYA..KHOJKI SIGN SHADDA\n11238..1123D  ; valid                  ;      ; NV8    # 7.0  KHOJKI DANDA..KHOJKI ABBREVIATION SIGN\n1123E         ; valid                                  # 9.0  KHOJKI SIGN SUKUN\n1123F..11241  ; valid                                  # 15.0 KHOJKI LETTER QA..KHOJKI VOWEL SIGN VOCALIC R\n11242..1127F  ; disallowed                             # NA   <reserved-11242>..<reserved-1127F>\n11280..11286  ; valid                                  # 8.0  MULTANI LETTER A..MULTANI LETTER GA\n11287         ; disallowed                             # NA   <reserved-11287>\n11288         ; valid                                  # 8.0  MULTANI LETTER GHA\n11289         ; disallowed                             # NA   <reserved-11289>\n1128A..1128D  ; valid                                  # 8.0  MULTANI LETTER CA..MULTANI LETTER JJA\n1128E         ; disallowed                             # NA   <reserved-1128E>\n1128F..1129D  ; valid                                  # 8.0  MULTANI LETTER NYA..MULTANI LETTER BA\n1129E         ; disallowed                             # NA   <reserved-1129E>\n1129F..112A8  ; valid                                  # 8.0  MULTANI LETTER BHA..MULTANI LETTER RHA\n112A9         ; valid                  ;      ; NV8    # 8.0  MULTANI SECTION MARK\n112AA..112AF  ; disallowed                             # NA   <reserved-112AA>..<reserved-112AF>\n112B0..112EA  ; valid                                  # 7.0  KHUDAWADI LETTER A..KHUDAWADI SIGN VIRAMA\n112EB..112EF  ; disallowed                             # NA   <reserved-112EB>..<reserved-112EF>\n112F0..112F9  ; valid                                  # 7.0  KHUDAWADI DIGIT ZERO..KHUDAWADI DIGIT NINE\n112FA..112FF  ; disallowed                             # NA   <reserved-112FA>..<reserved-112FF>\n11300         ; valid                                  # 8.0  GRANTHA SIGN COMBINING ANUSVARA ABOVE\n11301..11303  ; valid                                  # 7.0  GRANTHA SIGN CANDRABINDU..GRANTHA SIGN VISARGA\n11304         ; disallowed                             # NA   <reserved-11304>\n11305..1130C  ; valid                                  # 7.0  GRANTHA LETTER A..GRANTHA LETTER VOCALIC L\n1130D..1130E  ; disallowed                             # NA   <reserved-1130D>..<reserved-1130E>\n1130F..11310  ; valid                                  # 7.0  GRANTHA LETTER EE..GRANTHA LETTER AI\n11311..11312  ; disallowed                             # NA   <reserved-11311>..<reserved-11312>\n11313..11328  ; valid                                  # 7.0  GRANTHA LETTER OO..GRANTHA LETTER NA\n11329         ; disallowed                             # NA   <reserved-11329>\n1132A..11330  ; valid                                  # 7.0  GRANTHA LETTER PA..GRANTHA LETTER RA\n11331         ; disallowed                             # NA   <reserved-11331>\n11332..11333  ; valid                                  # 7.0  GRANTHA LETTER LA..GRANTHA LETTER LLA\n11334         ; disallowed                             # NA   <reserved-11334>\n11335..11339  ; valid                                  # 7.0  GRANTHA LETTER VA..GRANTHA LETTER HA\n1133A         ; disallowed                             # NA   <reserved-1133A>\n1133B         ; valid                                  # 11.0 COMBINING BINDU BELOW\n1133C..11344  ; valid                                  # 7.0  GRANTHA SIGN NUKTA..GRANTHA VOWEL SIGN VOCALIC RR\n11345..11346  ; disallowed                             # NA   <reserved-11345>..<reserved-11346>\n11347..11348  ; valid                                  # 7.0  GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI\n11349..1134A  ; disallowed                             # NA   <reserved-11349>..<reserved-1134A>\n1134B..1134D  ; valid                                  # 7.0  GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA\n1134E..1134F  ; disallowed                             # NA   <reserved-1134E>..<reserved-1134F>\n11350         ; valid                                  # 8.0  GRANTHA OM\n11351..11356  ; disallowed                             # NA   <reserved-11351>..<reserved-11356>\n11357         ; valid                                  # 7.0  GRANTHA AU LENGTH MARK\n11358..1135C  ; disallowed                             # NA   <reserved-11358>..<reserved-1135C>\n1135D..11363  ; valid                                  # 7.0  GRANTHA SIGN PLUTA..GRANTHA VOWEL SIGN VOCALIC LL\n11364..11365  ; disallowed                             # NA   <reserved-11364>..<reserved-11365>\n11366..1136C  ; valid                                  # 7.0  COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX\n1136D..1136F  ; disallowed                             # NA   <reserved-1136D>..<reserved-1136F>\n11370..11374  ; valid                                  # 7.0  COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA\n11375..113FF  ; disallowed                             # NA   <reserved-11375>..<reserved-113FF>\n11400..1144A  ; valid                                  # 9.0  NEWA LETTER A..NEWA SIDDHI\n1144B..1144F  ; valid                  ;      ; NV8    # 9.0  NEWA DANDA..NEWA ABBREVIATION SIGN\n11450..11459  ; valid                                  # 9.0  NEWA DIGIT ZERO..NEWA DIGIT NINE\n1145A         ; valid                  ;      ; NV8    # 13.0 NEWA DOUBLE COMMA\n1145B         ; valid                  ;      ; NV8    # 9.0  NEWA PLACEHOLDER MARK\n1145C         ; disallowed                             # NA   <reserved-1145C>\n1145D         ; valid                  ;      ; NV8    # 9.0  NEWA INSERTION SIGN\n1145E         ; valid                                  # 11.0 NEWA SANDHI MARK\n1145F         ; valid                                  # 12.0 NEWA LETTER VEDIC ANUSVARA\n11460..11461  ; valid                                  # 13.0 NEWA SIGN JIHVAMULIYA..NEWA SIGN UPADHMANIYA\n11462..1147F  ; disallowed                             # NA   <reserved-11462>..<reserved-1147F>\n11480..114C5  ; valid                                  # 7.0  TIRHUTA ANJI..TIRHUTA GVANG\n114C6         ; valid                  ;      ; NV8    # 7.0  TIRHUTA ABBREVIATION SIGN\n114C7         ; valid                                  # 7.0  TIRHUTA OM\n114C8..114CF  ; disallowed                             # NA   <reserved-114C8>..<reserved-114CF>\n114D0..114D9  ; valid                                  # 7.0  TIRHUTA DIGIT ZERO..TIRHUTA DIGIT NINE\n114DA..1157F  ; disallowed                             # NA   <reserved-114DA>..<reserved-1157F>\n11580..115B5  ; valid                                  # 7.0  SIDDHAM LETTER A..SIDDHAM VOWEL SIGN VOCALIC RR\n115B6..115B7  ; disallowed                             # NA   <reserved-115B6>..<reserved-115B7>\n115B8..115C0  ; valid                                  # 7.0  SIDDHAM VOWEL SIGN E..SIDDHAM SIGN NUKTA\n115C1..115C9  ; valid                  ;      ; NV8    # 7.0  SIDDHAM SIGN SIDDHAM..SIDDHAM END OF TEXT MARK\n115CA..115D7  ; valid                  ;      ; NV8    # 8.0  SIDDHAM SECTION MARK WITH TRIDENT AND U-SHAPED ORNAMENTS..SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES\n115D8..115DD  ; valid                                  # 8.0  SIDDHAM LETTER THREE-CIRCLE ALTERNATE I..SIDDHAM VOWEL SIGN ALTERNATE UU\n115DE..115FF  ; disallowed                             # NA   <reserved-115DE>..<reserved-115FF>\n11600..11640  ; valid                                  # 7.0  MODI LETTER A..MODI SIGN ARDHACANDRA\n11641..11643  ; valid                  ;      ; NV8    # 7.0  MODI DANDA..MODI ABBREVIATION SIGN\n11644         ; valid                                  # 7.0  MODI SIGN HUVA\n11645..1164F  ; disallowed                             # NA   <reserved-11645>..<reserved-1164F>\n11650..11659  ; valid                                  # 7.0  MODI DIGIT ZERO..MODI DIGIT NINE\n1165A..1165F  ; disallowed                             # NA   <reserved-1165A>..<reserved-1165F>\n11660..1166C  ; valid                  ;      ; NV8    # 9.0  MONGOLIAN BIRGA WITH ORNAMENT..MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT\n1166D..1167F  ; disallowed                             # NA   <reserved-1166D>..<reserved-1167F>\n11680..116B7  ; valid                                  # 6.1  TAKRI LETTER A..TAKRI SIGN NUKTA\n116B8         ; valid                                  # 12.0 TAKRI LETTER ARCHAIC KHA\n116B9         ; valid                  ;      ; NV8    # 14.0 TAKRI ABBREVIATION SIGN\n116BA..116BF  ; disallowed                             # NA   <reserved-116BA>..<reserved-116BF>\n116C0..116C9  ; valid                                  # 6.1  TAKRI DIGIT ZERO..TAKRI DIGIT NINE\n116CA..116FF  ; disallowed                             # NA   <reserved-116CA>..<reserved-116FF>\n11700..11719  ; valid                                  # 8.0  AHOM LETTER KA..AHOM LETTER JHA\n1171A         ; valid                                  # 11.0 AHOM LETTER ALTERNATE BA\n1171B..1171C  ; disallowed                             # NA   <reserved-1171B>..<reserved-1171C>\n1171D..1172B  ; valid                                  # 8.0  AHOM CONSONANT SIGN MEDIAL LA..AHOM SIGN KILLER\n1172C..1172F  ; disallowed                             # NA   <reserved-1172C>..<reserved-1172F>\n11730..11739  ; valid                                  # 8.0  AHOM DIGIT ZERO..AHOM DIGIT NINE\n1173A..1173F  ; valid                  ;      ; NV8    # 8.0  AHOM NUMBER TEN..AHOM SYMBOL VI\n11740..11746  ; valid                                  # 14.0 AHOM LETTER CA..AHOM LETTER LLA\n11747..117FF  ; disallowed                             # NA   <reserved-11747>..<reserved-117FF>\n11800..1183A  ; valid                                  # 11.0 DOGRA LETTER A..DOGRA SIGN NUKTA\n1183B         ; valid                  ;      ; NV8    # 11.0 DOGRA ABBREVIATION SIGN\n1183C..1189F  ; disallowed                             # NA   <reserved-1183C>..<reserved-1189F>\n118A0         ; mapped                 ; 118C0         # 7.0  WARANG CITI CAPITAL LETTER NGAA\n118A1         ; mapped                 ; 118C1         # 7.0  WARANG CITI CAPITAL LETTER A\n118A2         ; mapped                 ; 118C2         # 7.0  WARANG CITI CAPITAL LETTER WI\n118A3         ; mapped                 ; 118C3         # 7.0  WARANG CITI CAPITAL LETTER YU\n118A4         ; mapped                 ; 118C4         # 7.0  WARANG CITI CAPITAL LETTER YA\n118A5         ; mapped                 ; 118C5         # 7.0  WARANG CITI CAPITAL LETTER YO\n118A6         ; mapped                 ; 118C6         # 7.0  WARANG CITI CAPITAL LETTER II\n118A7         ; mapped                 ; 118C7         # 7.0  WARANG CITI CAPITAL LETTER UU\n118A8         ; mapped                 ; 118C8         # 7.0  WARANG CITI CAPITAL LETTER E\n118A9         ; mapped                 ; 118C9         # 7.0  WARANG CITI CAPITAL LETTER O\n118AA         ; mapped                 ; 118CA         # 7.0  WARANG CITI CAPITAL LETTER ANG\n118AB         ; mapped                 ; 118CB         # 7.0  WARANG CITI CAPITAL LETTER GA\n118AC         ; mapped                 ; 118CC         # 7.0  WARANG CITI CAPITAL LETTER KO\n118AD         ; mapped                 ; 118CD         # 7.0  WARANG CITI CAPITAL LETTER ENY\n118AE         ; mapped                 ; 118CE         # 7.0  WARANG CITI CAPITAL LETTER YUJ\n118AF         ; mapped                 ; 118CF         # 7.0  WARANG CITI CAPITAL LETTER UC\n118B0         ; mapped                 ; 118D0         # 7.0  WARANG CITI CAPITAL LETTER ENN\n118B1         ; mapped                 ; 118D1         # 7.0  WARANG CITI CAPITAL LETTER ODD\n118B2         ; mapped                 ; 118D2         # 7.0  WARANG CITI CAPITAL LETTER TTE\n118B3         ; mapped                 ; 118D3         # 7.0  WARANG CITI CAPITAL LETTER NUNG\n118B4         ; mapped                 ; 118D4         # 7.0  WARANG CITI CAPITAL LETTER DA\n118B5         ; mapped                 ; 118D5         # 7.0  WARANG CITI CAPITAL LETTER AT\n118B6         ; mapped                 ; 118D6         # 7.0  WARANG CITI CAPITAL LETTER AM\n118B7         ; mapped                 ; 118D7         # 7.0  WARANG CITI CAPITAL LETTER BU\n118B8         ; mapped                 ; 118D8         # 7.0  WARANG CITI CAPITAL LETTER PU\n118B9         ; mapped                 ; 118D9         # 7.0  WARANG CITI CAPITAL LETTER HIYO\n118BA         ; mapped                 ; 118DA         # 7.0  WARANG CITI CAPITAL LETTER HOLO\n118BB         ; mapped                 ; 118DB         # 7.0  WARANG CITI CAPITAL LETTER HORR\n118BC         ; mapped                 ; 118DC         # 7.0  WARANG CITI CAPITAL LETTER HAR\n118BD         ; mapped                 ; 118DD         # 7.0  WARANG CITI CAPITAL LETTER SSUU\n118BE         ; mapped                 ; 118DE         # 7.0  WARANG CITI CAPITAL LETTER SII\n118BF         ; mapped                 ; 118DF         # 7.0  WARANG CITI CAPITAL LETTER VIYO\n118C0..118E9  ; valid                                  # 7.0  WARANG CITI SMALL LETTER NGAA..WARANG CITI DIGIT NINE\n118EA..118F2  ; valid                  ;      ; NV8    # 7.0  WARANG CITI NUMBER TEN..WARANG CITI NUMBER NINETY\n118F3..118FE  ; disallowed                             # NA   <reserved-118F3>..<reserved-118FE>\n118FF         ; valid                                  # 7.0  WARANG CITI OM\n11900..11906  ; valid                                  # 13.0 DIVES AKURU LETTER A..DIVES AKURU LETTER E\n11907..11908  ; disallowed                             # NA   <reserved-11907>..<reserved-11908>\n11909         ; valid                                  # 13.0 DIVES AKURU LETTER O\n1190A..1190B  ; disallowed                             # NA   <reserved-1190A>..<reserved-1190B>\n1190C..11913  ; valid                                  # 13.0 DIVES AKURU LETTER KA..DIVES AKURU LETTER JA\n11914         ; disallowed                             # NA   <reserved-11914>\n11915..11916  ; valid                                  # 13.0 DIVES AKURU LETTER NYA..DIVES AKURU LETTER TTA\n11917         ; disallowed                             # NA   <reserved-11917>\n11918..11935  ; valid                                  # 13.0 DIVES AKURU LETTER DDA..DIVES AKURU VOWEL SIGN E\n11936         ; disallowed                             # NA   <reserved-11936>\n11937..11938  ; valid                                  # 13.0 DIVES AKURU VOWEL SIGN AI..DIVES AKURU VOWEL SIGN O\n11939..1193A  ; disallowed                             # NA   <reserved-11939>..<reserved-1193A>\n1193B..11943  ; valid                                  # 13.0 DIVES AKURU SIGN ANUSVARA..DIVES AKURU SIGN NUKTA\n11944..11946  ; valid                  ;      ; NV8    # 13.0 DIVES AKURU DOUBLE DANDA..DIVES AKURU END OF TEXT MARK\n11947..1194F  ; disallowed                             # NA   <reserved-11947>..<reserved-1194F>\n11950..11959  ; valid                                  # 13.0 DIVES AKURU DIGIT ZERO..DIVES AKURU DIGIT NINE\n1195A..1199F  ; disallowed                             # NA   <reserved-1195A>..<reserved-1199F>\n119A0..119A7  ; valid                                  # 12.0 NANDINAGARI LETTER A..NANDINAGARI LETTER VOCALIC RR\n119A8..119A9  ; disallowed                             # NA   <reserved-119A8>..<reserved-119A9>\n119AA..119D7  ; valid                                  # 12.0 NANDINAGARI LETTER E..NANDINAGARI VOWEL SIGN VOCALIC RR\n119D8..119D9  ; disallowed                             # NA   <reserved-119D8>..<reserved-119D9>\n119DA..119E1  ; valid                                  # 12.0 NANDINAGARI VOWEL SIGN E..NANDINAGARI SIGN AVAGRAHA\n119E2         ; valid                  ;      ; NV8    # 12.0 NANDINAGARI SIGN SIDDHAM\n119E3..119E4  ; valid                                  # 12.0 NANDINAGARI HEADSTROKE..NANDINAGARI VOWEL SIGN PRISHTHAMATRA E\n119E5..119FF  ; disallowed                             # NA   <reserved-119E5>..<reserved-119FF>\n11A00..11A3E  ; valid                                  # 10.0 ZANABAZAR SQUARE LETTER A..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA\n11A3F..11A46  ; valid                  ;      ; NV8    # 10.0 ZANABAZAR SQUARE INITIAL HEAD MARK..ZANABAZAR SQUARE CLOSING DOUBLE-LINED HEAD MARK\n11A47         ; valid                                  # 10.0 ZANABAZAR SQUARE SUBJOINER\n11A48..11A4F  ; disallowed                             # NA   <reserved-11A48>..<reserved-11A4F>\n11A50..11A83  ; valid                                  # 10.0 SOYOMBO LETTER A..SOYOMBO LETTER KSSA\n11A84..11A85  ; valid                                  # 12.0 SOYOMBO SIGN JIHVAMULIYA..SOYOMBO SIGN UPADHMANIYA\n11A86..11A99  ; valid                                  # 10.0 SOYOMBO CLUSTER-INITIAL LETTER RA..SOYOMBO SUBJOINER\n11A9A..11A9C  ; valid                  ;      ; NV8    # 10.0 SOYOMBO MARK TSHEG..SOYOMBO MARK DOUBLE SHAD\n11A9D         ; valid                                  # 11.0 SOYOMBO MARK PLUTA\n11A9E..11AA2  ; valid                  ;      ; NV8    # 10.0 SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME..SOYOMBO TERMINAL MARK-2\n11AA3..11AAF  ; disallowed                             # NA   <reserved-11AA3>..<reserved-11AAF>\n11AB0..11ABF  ; valid                                  # 14.0 CANADIAN SYLLABICS NATTILIK HI..CANADIAN SYLLABICS SPA\n11AC0..11AF8  ; valid                                  # 7.0  PAU CIN HAU LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL\n11AF9..11AFF  ; disallowed                             # NA   <reserved-11AF9>..<reserved-11AFF>\n11B00..11B09  ; valid                  ;      ; NV8    # 15.0 DEVANAGARI HEAD MARK..DEVANAGARI SIGN MINDU\n11B0A..11BFF  ; disallowed                             # NA   <reserved-11B0A>..<reserved-11BFF>\n11C00..11C08  ; valid                                  # 9.0  BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L\n11C09         ; disallowed                             # NA   <reserved-11C09>\n11C0A..11C36  ; valid                                  # 9.0  BHAIKSUKI LETTER E..BHAIKSUKI VOWEL SIGN VOCALIC L\n11C37         ; disallowed                             # NA   <reserved-11C37>\n11C38..11C40  ; valid                                  # 9.0  BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN AVAGRAHA\n11C41..11C45  ; valid                  ;      ; NV8    # 9.0  BHAIKSUKI DANDA..BHAIKSUKI GAP FILLER-2\n11C46..11C4F  ; disallowed                             # NA   <reserved-11C46>..<reserved-11C4F>\n11C50..11C59  ; valid                                  # 9.0  BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE\n11C5A..11C6C  ; valid                  ;      ; NV8    # 9.0  BHAIKSUKI NUMBER ONE..BHAIKSUKI HUNDREDS UNIT MARK\n11C6D..11C6F  ; disallowed                             # NA   <reserved-11C6D>..<reserved-11C6F>\n11C70..11C71  ; valid                  ;      ; NV8    # 9.0  MARCHEN HEAD MARK..MARCHEN MARK SHAD\n11C72..11C8F  ; valid                                  # 9.0  MARCHEN LETTER KA..MARCHEN LETTER A\n11C90..11C91  ; disallowed                             # NA   <reserved-11C90>..<reserved-11C91>\n11C92..11CA7  ; valid                                  # 9.0  MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA\n11CA8         ; disallowed                             # NA   <reserved-11CA8>\n11CA9..11CB6  ; valid                                  # 9.0  MARCHEN SUBJOINED LETTER YA..MARCHEN SIGN CANDRABINDU\n11CB7..11CFF  ; disallowed                             # NA   <reserved-11CB7>..<reserved-11CFF>\n11D00..11D06  ; valid                                  # 10.0 MASARAM GONDI LETTER A..MASARAM GONDI LETTER E\n11D07         ; disallowed                             # NA   <reserved-11D07>\n11D08..11D09  ; valid                                  # 10.0 MASARAM GONDI LETTER AI..MASARAM GONDI LETTER O\n11D0A         ; disallowed                             # NA   <reserved-11D0A>\n11D0B..11D36  ; valid                                  # 10.0 MASARAM GONDI LETTER AU..MASARAM GONDI VOWEL SIGN VOCALIC R\n11D37..11D39  ; disallowed                             # NA   <reserved-11D37>..<reserved-11D39>\n11D3A         ; valid                                  # 10.0 MASARAM GONDI VOWEL SIGN E\n11D3B         ; disallowed                             # NA   <reserved-11D3B>\n11D3C..11D3D  ; valid                                  # 10.0 MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O\n11D3E         ; disallowed                             # NA   <reserved-11D3E>\n11D3F..11D47  ; valid                                  # 10.0 MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI RA-KARA\n11D48..11D4F  ; disallowed                             # NA   <reserved-11D48>..<reserved-11D4F>\n11D50..11D59  ; valid                                  # 10.0 MASARAM GONDI DIGIT ZERO..MASARAM GONDI DIGIT NINE\n11D5A..11D5F  ; disallowed                             # NA   <reserved-11D5A>..<reserved-11D5F>\n11D60..11D65  ; valid                                  # 11.0 GUNJALA GONDI LETTER A..GUNJALA GONDI LETTER UU\n11D66         ; disallowed                             # NA   <reserved-11D66>\n11D67..11D68  ; valid                                  # 11.0 GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI\n11D69         ; disallowed                             # NA   <reserved-11D69>\n11D6A..11D8E  ; valid                                  # 11.0 GUNJALA GONDI LETTER OO..GUNJALA GONDI VOWEL SIGN UU\n11D8F         ; disallowed                             # NA   <reserved-11D8F>\n11D90..11D91  ; valid                                  # 11.0 GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI\n11D92         ; disallowed                             # NA   <reserved-11D92>\n11D93..11D98  ; valid                                  # 11.0 GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI OM\n11D99..11D9F  ; disallowed                             # NA   <reserved-11D99>..<reserved-11D9F>\n11DA0..11DA9  ; valid                                  # 11.0 GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE\n11DAA..11EDF  ; disallowed                             # NA   <reserved-11DAA>..<reserved-11EDF>\n11EE0..11EF6  ; valid                                  # 11.0 MAKASAR LETTER KA..MAKASAR VOWEL SIGN O\n11EF7..11EF8  ; valid                  ;      ; NV8    # 11.0 MAKASAR PASSIMBANG..MAKASAR END OF SECTION\n11EF9..11EFF  ; disallowed                             # NA   <reserved-11EF9>..<reserved-11EFF>\n11F00..11F10  ; valid                                  # 15.0 KAWI SIGN CANDRABINDU..KAWI LETTER O\n11F11         ; disallowed                             # NA   <reserved-11F11>\n11F12..11F3A  ; valid                                  # 15.0 KAWI LETTER KA..KAWI VOWEL SIGN VOCALIC R\n11F3B..11F3D  ; disallowed                             # NA   <reserved-11F3B>..<reserved-11F3D>\n11F3E..11F42  ; valid                                  # 15.0 KAWI VOWEL SIGN E..KAWI CONJOINER\n11F43..11F4F  ; valid                  ;      ; NV8    # 15.0 KAWI DANDA..KAWI PUNCTUATION CLOSING SPIRAL\n11F50..11F59  ; valid                                  # 15.0 KAWI DIGIT ZERO..KAWI DIGIT NINE\n11F5A..11FAF  ; disallowed                             # NA   <reserved-11F5A>..<reserved-11FAF>\n11FB0         ; valid                                  # 13.0 LISU LETTER YHA\n11FB1..11FBF  ; disallowed                             # NA   <reserved-11FB1>..<reserved-11FBF>\n11FC0..11FF1  ; valid                  ;      ; NV8    # 12.0 TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH..TAMIL SIGN VAKAIYARAA\n11FF2..11FFE  ; disallowed                             # NA   <reserved-11FF2>..<reserved-11FFE>\n11FFF         ; valid                  ;      ; NV8    # 12.0 TAMIL PUNCTUATION END OF TEXT\n12000..1236E  ; valid                                  # 5.0  CUNEIFORM SIGN A..CUNEIFORM SIGN ZUM\n1236F..12398  ; valid                                  # 7.0  CUNEIFORM SIGN KAP ELAMITE..CUNEIFORM SIGN UM TIMES ME\n12399         ; valid                                  # 8.0  CUNEIFORM SIGN U U\n1239A..123FF  ; disallowed                             # NA   <reserved-1239A>..<reserved-123FF>\n12400..12462  ; valid                  ;      ; NV8    # 5.0  CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE QUARTER\n12463..1246E  ; valid                  ;      ; NV8    # 7.0  CUNEIFORM NUMERIC SIGN ONE QUARTER GUR..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM\n1246F         ; disallowed                             # NA   <reserved-1246F>\n12470..12473  ; valid                  ;      ; NV8    # 5.0  CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON\n12474         ; valid                  ;      ; NV8    # 7.0  CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON\n12475..1247F  ; disallowed                             # NA   <reserved-12475>..<reserved-1247F>\n12480..12543  ; valid                                  # 8.0  CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU\n12544..12F8F  ; disallowed                             # NA   <reserved-12544>..<reserved-12F8F>\n12F90..12FF0  ; valid                                  # 14.0 CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114\n12FF1..12FF2  ; valid                  ;      ; NV8    # 14.0 CYPRO-MINOAN SIGN CM301..CYPRO-MINOAN SIGN CM302\n12FF3..12FFF  ; disallowed                             # NA   <reserved-12FF3>..<reserved-12FFF>\n13000..1342E  ; valid                                  # 5.2  EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032\n1342F         ; valid                                  # 15.0 EGYPTIAN HIEROGLYPH V011D\n13430..13438  ; disallowed                             # 12.0 EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT\n13439..1343F  ; disallowed                             # 15.0 EGYPTIAN HIEROGLYPH INSERT AT MIDDLE..EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE\n13440..13455  ; valid                                  # 15.0 EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED\n13456..143FF  ; disallowed                             # NA   <reserved-13456>..<reserved-143FF>\n14400..14646  ; valid                                  # 8.0  ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530\n14647..167FF  ; disallowed                             # NA   <reserved-14647>..<reserved-167FF>\n16800..16A38  ; valid                                  # 6.0  BAMUM LETTER PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ\n16A39..16A3F  ; disallowed                             # NA   <reserved-16A39>..<reserved-16A3F>\n16A40..16A5E  ; valid                                  # 7.0  MRO LETTER TA..MRO LETTER TEK\n16A5F         ; disallowed                             # NA   <reserved-16A5F>\n16A60..16A69  ; valid                                  # 7.0  MRO DIGIT ZERO..MRO DIGIT NINE\n16A6A..16A6D  ; disallowed                             # NA   <reserved-16A6A>..<reserved-16A6D>\n16A6E..16A6F  ; valid                  ;      ; NV8    # 7.0  MRO DANDA..MRO DOUBLE DANDA\n16A70..16ABE  ; valid                                  # 14.0 TANGSA LETTER OZ..TANGSA LETTER ZA\n16ABF         ; disallowed                             # NA   <reserved-16ABF>\n16AC0..16AC9  ; valid                                  # 14.0 TANGSA DIGIT ZERO..TANGSA DIGIT NINE\n16ACA..16ACF  ; disallowed                             # NA   <reserved-16ACA>..<reserved-16ACF>\n16AD0..16AED  ; valid                                  # 7.0  BASSA VAH LETTER ENNI..BASSA VAH LETTER I\n16AEE..16AEF  ; disallowed                             # NA   <reserved-16AEE>..<reserved-16AEF>\n16AF0..16AF4  ; valid                                  # 7.0  BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE\n16AF5         ; valid                  ;      ; NV8    # 7.0  BASSA VAH FULL STOP\n16AF6..16AFF  ; disallowed                             # NA   <reserved-16AF6>..<reserved-16AFF>\n16B00..16B36  ; valid                                  # 7.0  PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG MARK CIM TAUM\n16B37..16B3F  ; valid                  ;      ; NV8    # 7.0  PAHAWH HMONG SIGN VOS THOM..PAHAWH HMONG SIGN XYEEM FAIB\n16B40..16B43  ; valid                                  # 7.0  PAHAWH HMONG SIGN VOS SEEV..PAHAWH HMONG SIGN IB YAM\n16B44..16B45  ; valid                  ;      ; NV8    # 7.0  PAHAWH HMONG SIGN XAUS..PAHAWH HMONG SIGN CIM TSOV ROG\n16B46..16B4F  ; disallowed                             # NA   <reserved-16B46>..<reserved-16B4F>\n16B50..16B59  ; valid                                  # 7.0  PAHAWH HMONG DIGIT ZERO..PAHAWH HMONG DIGIT NINE\n16B5A         ; disallowed                             # NA   <reserved-16B5A>\n16B5B..16B61  ; valid                  ;      ; NV8    # 7.0  PAHAWH HMONG NUMBER TENS..PAHAWH HMONG NUMBER TRILLIONS\n16B62         ; disallowed                             # NA   <reserved-16B62>\n16B63..16B77  ; valid                                  # 7.0  PAHAWH HMONG SIGN VOS LUB..PAHAWH HMONG SIGN CIM NRES TOS\n16B78..16B7C  ; disallowed                             # NA   <reserved-16B78>..<reserved-16B7C>\n16B7D..16B8F  ; valid                                  # 7.0  PAHAWH HMONG CLAN SIGN TSHEEJ..PAHAWH HMONG CLAN SIGN VWJ\n16B90..16E3F  ; disallowed                             # NA   <reserved-16B90>..<reserved-16E3F>\n16E40         ; mapped                 ; 16E60         # 11.0 MEDEFAIDRIN CAPITAL LETTER M\n16E41         ; mapped                 ; 16E61         # 11.0 MEDEFAIDRIN CAPITAL LETTER S\n16E42         ; mapped                 ; 16E62         # 11.0 MEDEFAIDRIN CAPITAL LETTER V\n16E43         ; mapped                 ; 16E63         # 11.0 MEDEFAIDRIN CAPITAL LETTER W\n16E44         ; mapped                 ; 16E64         # 11.0 MEDEFAIDRIN CAPITAL LETTER ATIU\n16E45         ; mapped                 ; 16E65         # 11.0 MEDEFAIDRIN CAPITAL LETTER Z\n16E46         ; mapped                 ; 16E66         # 11.0 MEDEFAIDRIN CAPITAL LETTER KP\n16E47         ; mapped                 ; 16E67         # 11.0 MEDEFAIDRIN CAPITAL LETTER P\n16E48         ; mapped                 ; 16E68         # 11.0 MEDEFAIDRIN CAPITAL LETTER T\n16E49         ; mapped                 ; 16E69         # 11.0 MEDEFAIDRIN CAPITAL LETTER G\n16E4A         ; mapped                 ; 16E6A         # 11.0 MEDEFAIDRIN CAPITAL LETTER F\n16E4B         ; mapped                 ; 16E6B         # 11.0 MEDEFAIDRIN CAPITAL LETTER I\n16E4C         ; mapped                 ; 16E6C         # 11.0 MEDEFAIDRIN CAPITAL LETTER K\n16E4D         ; mapped                 ; 16E6D         # 11.0 MEDEFAIDRIN CAPITAL LETTER A\n16E4E         ; mapped                 ; 16E6E         # 11.0 MEDEFAIDRIN CAPITAL LETTER J\n16E4F         ; mapped                 ; 16E6F         # 11.0 MEDEFAIDRIN CAPITAL LETTER E\n16E50         ; mapped                 ; 16E70         # 11.0 MEDEFAIDRIN CAPITAL LETTER B\n16E51         ; mapped                 ; 16E71         # 11.0 MEDEFAIDRIN CAPITAL LETTER C\n16E52         ; mapped                 ; 16E72         # 11.0 MEDEFAIDRIN CAPITAL LETTER U\n16E53         ; mapped                 ; 16E73         # 11.0 MEDEFAIDRIN CAPITAL LETTER YU\n16E54         ; mapped                 ; 16E74         # 11.0 MEDEFAIDRIN CAPITAL LETTER L\n16E55         ; mapped                 ; 16E75         # 11.0 MEDEFAIDRIN CAPITAL LETTER Q\n16E56         ; mapped                 ; 16E76         # 11.0 MEDEFAIDRIN CAPITAL LETTER HP\n16E57         ; mapped                 ; 16E77         # 11.0 MEDEFAIDRIN CAPITAL LETTER NY\n16E58         ; mapped                 ; 16E78         # 11.0 MEDEFAIDRIN CAPITAL LETTER X\n16E59         ; mapped                 ; 16E79         # 11.0 MEDEFAIDRIN CAPITAL LETTER D\n16E5A         ; mapped                 ; 16E7A         # 11.0 MEDEFAIDRIN CAPITAL LETTER OE\n16E5B         ; mapped                 ; 16E7B         # 11.0 MEDEFAIDRIN CAPITAL LETTER N\n16E5C         ; mapped                 ; 16E7C         # 11.0 MEDEFAIDRIN CAPITAL LETTER R\n16E5D         ; mapped                 ; 16E7D         # 11.0 MEDEFAIDRIN CAPITAL LETTER O\n16E5E         ; mapped                 ; 16E7E         # 11.0 MEDEFAIDRIN CAPITAL LETTER AI\n16E5F         ; mapped                 ; 16E7F         # 11.0 MEDEFAIDRIN CAPITAL LETTER Y\n16E60..16E7F  ; valid                                  # 11.0 MEDEFAIDRIN SMALL LETTER M..MEDEFAIDRIN SMALL LETTER Y\n16E80..16E9A  ; valid                  ;      ; NV8    # 11.0 MEDEFAIDRIN DIGIT ZERO..MEDEFAIDRIN EXCLAMATION OH\n16E9B..16EFF  ; disallowed                             # NA   <reserved-16E9B>..<reserved-16EFF>\n16F00..16F44  ; valid                                  # 6.1  MIAO LETTER PA..MIAO LETTER HHA\n16F45..16F4A  ; valid                                  # 12.0 MIAO LETTER BRI..MIAO LETTER RTE\n16F4B..16F4E  ; disallowed                             # NA   <reserved-16F4B>..<reserved-16F4E>\n16F4F         ; valid                                  # 12.0 MIAO SIGN CONSONANT MODIFIER BAR\n16F50..16F7E  ; valid                                  # 6.1  MIAO LETTER NASALIZATION..MIAO VOWEL SIGN NG\n16F7F..16F87  ; valid                                  # 12.0 MIAO VOWEL SIGN UOG..MIAO VOWEL SIGN UI\n16F88..16F8E  ; disallowed                             # NA   <reserved-16F88>..<reserved-16F8E>\n16F8F..16F9F  ; valid                                  # 6.1  MIAO TONE RIGHT..MIAO LETTER REFORMED TONE-8\n16FA0..16FDF  ; disallowed                             # NA   <reserved-16FA0>..<reserved-16FDF>\n16FE0         ; valid                                  # 9.0  TANGUT ITERATION MARK\n16FE1         ; valid                                  # 10.0 NUSHU ITERATION MARK\n16FE2         ; valid                  ;      ; NV8    # 12.0 OLD CHINESE HOOK MARK\n16FE3         ; valid                                  # 12.0 OLD CHINESE ITERATION MARK\n16FE4         ; valid                                  # 13.0 KHITAN SMALL SCRIPT FILLER\n16FE5..16FEF  ; disallowed                             # NA   <reserved-16FE5>..<reserved-16FEF>\n16FF0..16FF1  ; valid                                  # 13.0 VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY\n16FF2..16FFF  ; disallowed                             # NA   <reserved-16FF2>..<reserved-16FFF>\n17000..187EC  ; valid                                  # 9.0  TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187EC\n187ED..187F1  ; valid                                  # 11.0 TANGUT IDEOGRAPH-187ED..TANGUT IDEOGRAPH-187F1\n187F2..187F7  ; valid                                  # 12.0 TANGUT IDEOGRAPH-187F2..TANGUT IDEOGRAPH-187F7\n187F8..187FF  ; disallowed                             # NA   <reserved-187F8>..<reserved-187FF>\n18800..18AF2  ; valid                                  # 9.0  TANGUT COMPONENT-001..TANGUT COMPONENT-755\n18AF3..18CD5  ; valid                                  # 13.0 TANGUT COMPONENT-756..KHITAN SMALL SCRIPT CHARACTER-18CD5\n18CD6..18CFF  ; disallowed                             # NA   <reserved-18CD6>..<reserved-18CFF>\n18D00..18D08  ; valid                                  # 13.0 TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D08\n18D09..1AFEF  ; disallowed                             # NA   <reserved-18D09>..<reserved-1AFEF>\n1AFF0..1AFF3  ; valid                                  # 14.0 KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5\n1AFF4         ; disallowed                             # NA   <reserved-1AFF4>\n1AFF5..1AFFB  ; valid                                  # 14.0 KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5\n1AFFC         ; disallowed                             # NA   <reserved-1AFFC>\n1AFFD..1AFFE  ; valid                                  # 14.0 KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8\n1AFFF         ; disallowed                             # NA   <reserved-1AFFF>\n1B000..1B001  ; valid                                  # 6.0  KATAKANA LETTER ARCHAIC E..HIRAGANA LETTER ARCHAIC YE\n1B002..1B11E  ; valid                                  # 10.0 HENTAIGANA LETTER A-1..HENTAIGANA LETTER N-MU-MO-2\n1B11F..1B122  ; valid                                  # 14.0 HIRAGANA LETTER ARCHAIC WU..KATAKANA LETTER ARCHAIC WU\n1B123..1B131  ; disallowed                             # NA   <reserved-1B123>..<reserved-1B131>\n1B132         ; valid                                  # 15.0 HIRAGANA LETTER SMALL KO\n1B133..1B14F  ; disallowed                             # NA   <reserved-1B133>..<reserved-1B14F>\n1B150..1B152  ; valid                                  # 12.0 HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO\n1B153..1B154  ; disallowed                             # NA   <reserved-1B153>..<reserved-1B154>\n1B155         ; valid                                  # 15.0 KATAKANA LETTER SMALL KO\n1B156..1B163  ; disallowed                             # NA   <reserved-1B156>..<reserved-1B163>\n1B164..1B167  ; valid                                  # 12.0 KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N\n1B168..1B16F  ; disallowed                             # NA   <reserved-1B168>..<reserved-1B16F>\n1B170..1B2FB  ; valid                                  # 10.0 NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB\n1B2FC..1BBFF  ; disallowed                             # NA   <reserved-1B2FC>..<reserved-1BBFF>\n1BC00..1BC6A  ; valid                                  # 7.0  DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M\n1BC6B..1BC6F  ; disallowed                             # NA   <reserved-1BC6B>..<reserved-1BC6F>\n1BC70..1BC7C  ; valid                                  # 7.0  DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK\n1BC7D..1BC7F  ; disallowed                             # NA   <reserved-1BC7D>..<reserved-1BC7F>\n1BC80..1BC88  ; valid                                  # 7.0  DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL\n1BC89..1BC8F  ; disallowed                             # NA   <reserved-1BC89>..<reserved-1BC8F>\n1BC90..1BC99  ; valid                                  # 7.0  DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW\n1BC9A..1BC9B  ; disallowed                             # NA   <reserved-1BC9A>..<reserved-1BC9B>\n1BC9C         ; valid                  ;      ; NV8    # 7.0  DUPLOYAN SIGN O WITH CROSS\n1BC9D..1BC9E  ; valid                                  # 7.0  DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK\n1BC9F         ; valid                  ;      ; NV8    # 7.0  DUPLOYAN PUNCTUATION CHINOOK FULL STOP\n1BCA0..1BCA3  ; ignored                                # 7.0  SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP\n1BCA4..1CEFF  ; disallowed                             # NA   <reserved-1BCA4>..<reserved-1CEFF>\n1CF00..1CF2D  ; valid                                  # 14.0 ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT\n1CF2E..1CF2F  ; disallowed                             # NA   <reserved-1CF2E>..<reserved-1CF2F>\n1CF30..1CF46  ; valid                                  # 14.0 ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG\n1CF47..1CF4F  ; disallowed                             # NA   <reserved-1CF47>..<reserved-1CF4F>\n1CF50..1CFC3  ; valid                  ;      ; NV8    # 14.0 ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK\n1CFC4..1CFFF  ; disallowed                             # NA   <reserved-1CFC4>..<reserved-1CFFF>\n1D000..1D0F5  ; valid                  ;      ; NV8    # 3.1  BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO\n1D0F6..1D0FF  ; disallowed                             # NA   <reserved-1D0F6>..<reserved-1D0FF>\n1D100..1D126  ; valid                  ;      ; NV8    # 3.1  MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2\n1D127..1D128  ; disallowed                             # NA   <reserved-1D127>..<reserved-1D128>\n1D129         ; valid                  ;      ; NV8    # 5.1  MUSICAL SYMBOL MULTIPLE MEASURE REST\n1D12A..1D15D  ; valid                  ;      ; NV8    # 3.1  MUSICAL SYMBOL DOUBLE SHARP..MUSICAL SYMBOL WHOLE NOTE\n1D15E         ; mapped                 ; 1D157 1D165   # 3.1  MUSICAL SYMBOL HALF NOTE\n1D15F         ; mapped                 ; 1D158 1D165   # 3.1  MUSICAL SYMBOL QUARTER NOTE\n1D160         ; mapped                 ; 1D158 1D165 1D16E #3.1 MUSICAL SYMBOL EIGHTH NOTE\n1D161         ; mapped                 ; 1D158 1D165 1D16F #3.1 MUSICAL SYMBOL SIXTEENTH NOTE\n1D162         ; mapped                 ; 1D158 1D165 1D170 #3.1 MUSICAL SYMBOL THIRTY-SECOND NOTE\n1D163         ; mapped                 ; 1D158 1D165 1D171 #3.1 MUSICAL SYMBOL SIXTY-FOURTH NOTE\n1D164         ; mapped                 ; 1D158 1D165 1D172 #3.1 MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE\n1D165..1D172  ; valid                  ;      ; NV8    # 3.1  MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING FLAG-5\n1D173..1D17A  ; disallowed                             # 3.1  MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE\n1D17B..1D1BA  ; valid                  ;      ; NV8    # 3.1  MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL SEMIBREVIS BLACK\n1D1BB         ; mapped                 ; 1D1B9 1D165   # 3.1  MUSICAL SYMBOL MINIMA\n1D1BC         ; mapped                 ; 1D1BA 1D165   # 3.1  MUSICAL SYMBOL MINIMA BLACK\n1D1BD         ; mapped                 ; 1D1B9 1D165 1D16E #3.1 MUSICAL SYMBOL SEMIMINIMA WHITE\n1D1BE         ; mapped                 ; 1D1BA 1D165 1D16E #3.1 MUSICAL SYMBOL SEMIMINIMA BLACK\n1D1BF         ; mapped                 ; 1D1B9 1D165 1D16F #3.1 MUSICAL SYMBOL FUSA WHITE\n1D1C0         ; mapped                 ; 1D1BA 1D165 1D16F #3.1 MUSICAL SYMBOL FUSA BLACK\n1D1C1..1D1DD  ; valid                  ;      ; NV8    # 3.1  MUSICAL SYMBOL LONGA PERFECTA REST..MUSICAL SYMBOL PES SUBPUNCTIS\n1D1DE..1D1E8  ; valid                  ;      ; NV8    # 8.0  MUSICAL SYMBOL KIEVAN C CLEF..MUSICAL SYMBOL KIEVAN FLAT SIGN\n1D1E9..1D1EA  ; valid                  ;      ; NV8    # 14.0 MUSICAL SYMBOL SORI..MUSICAL SYMBOL KORON\n1D1EB..1D1FF  ; disallowed                             # NA   <reserved-1D1EB>..<reserved-1D1FF>\n1D200..1D245  ; valid                  ;      ; NV8    # 4.1  GREEK VOCAL NOTATION SYMBOL-1..GREEK MUSICAL LEIMMA\n1D246..1D2BF  ; disallowed                             # NA   <reserved-1D246>..<reserved-1D2BF>\n1D2C0..1D2D3  ; valid                  ;      ; NV8    # 15.0 KAKTOVIK NUMERAL ZERO..KAKTOVIK NUMERAL NINETEEN\n1D2D4..1D2DF  ; disallowed                             # NA   <reserved-1D2D4>..<reserved-1D2DF>\n1D2E0..1D2F3  ; valid                  ;      ; NV8    # 11.0 MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN\n1D2F4..1D2FF  ; disallowed                             # NA   <reserved-1D2F4>..<reserved-1D2FF>\n1D300..1D356  ; valid                  ;      ; NV8    # 4.0  MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING\n1D357..1D35F  ; disallowed                             # NA   <reserved-1D357>..<reserved-1D35F>\n1D360..1D371  ; valid                  ;      ; NV8    # 5.0  COUNTING ROD UNIT DIGIT ONE..COUNTING ROD TENS DIGIT NINE\n1D372..1D378  ; valid                  ;      ; NV8    # 11.0 IDEOGRAPHIC TALLY MARK ONE..TALLY MARK FIVE\n1D379..1D3FF  ; disallowed                             # NA   <reserved-1D379>..<reserved-1D3FF>\n1D400         ; mapped                 ; 0061          # 3.1  MATHEMATICAL BOLD CAPITAL A\n1D401         ; mapped                 ; 0062          # 3.1  MATHEMATICAL BOLD CAPITAL B\n1D402         ; mapped                 ; 0063          # 3.1  MATHEMATICAL BOLD CAPITAL C\n1D403         ; mapped                 ; 0064          # 3.1  MATHEMATICAL BOLD CAPITAL D\n1D404         ; mapped                 ; 0065          # 3.1  MATHEMATICAL BOLD CAPITAL E\n1D405         ; mapped                 ; 0066          # 3.1  MATHEMATICAL BOLD CAPITAL F\n1D406         ; mapped                 ; 0067          # 3.1  MATHEMATICAL BOLD CAPITAL G\n1D407         ; mapped                 ; 0068          # 3.1  MATHEMATICAL BOLD CAPITAL H\n1D408         ; mapped                 ; 0069          # 3.1  MATHEMATICAL BOLD CAPITAL I\n1D409         ; mapped                 ; 006A          # 3.1  MATHEMATICAL BOLD CAPITAL J\n1D40A         ; mapped                 ; 006B          # 3.1  MATHEMATICAL BOLD CAPITAL K\n1D40B         ; mapped                 ; 006C          # 3.1  MATHEMATICAL BOLD CAPITAL L\n1D40C         ; mapped                 ; 006D          # 3.1  MATHEMATICAL BOLD CAPITAL M\n1D40D         ; mapped                 ; 006E          # 3.1  MATHEMATICAL BOLD CAPITAL N\n1D40E         ; mapped                 ; 006F          # 3.1  MATHEMATICAL BOLD CAPITAL O\n1D40F         ; mapped                 ; 0070          # 3.1  MATHEMATICAL BOLD CAPITAL P\n1D410         ; mapped                 ; 0071          # 3.1  MATHEMATICAL BOLD CAPITAL Q\n1D411         ; mapped                 ; 0072          # 3.1  MATHEMATICAL BOLD CAPITAL R\n1D412         ; mapped                 ; 0073          # 3.1  MATHEMATICAL BOLD CAPITAL S\n1D413         ; mapped                 ; 0074          # 3.1  MATHEMATICAL BOLD CAPITAL T\n1D414         ; mapped                 ; 0075          # 3.1  MATHEMATICAL BOLD CAPITAL U\n1D415         ; mapped                 ; 0076          # 3.1  MATHEMATICAL BOLD CAPITAL V\n1D416         ; mapped                 ; 0077          # 3.1  MATHEMATICAL BOLD CAPITAL W\n1D417         ; mapped                 ; 0078          # 3.1  MATHEMATICAL BOLD CAPITAL X\n1D418         ; mapped                 ; 0079          # 3.1  MATHEMATICAL BOLD CAPITAL Y\n1D419         ; mapped                 ; 007A          # 3.1  MATHEMATICAL BOLD CAPITAL Z\n1D41A         ; mapped                 ; 0061          # 3.1  MATHEMATICAL BOLD SMALL A\n1D41B         ; mapped                 ; 0062          # 3.1  MATHEMATICAL BOLD SMALL B\n1D41C         ; mapped                 ; 0063          # 3.1  MATHEMATICAL BOLD SMALL C\n1D41D         ; mapped                 ; 0064          # 3.1  MATHEMATICAL BOLD SMALL D\n1D41E         ; mapped                 ; 0065          # 3.1  MATHEMATICAL BOLD SMALL E\n1D41F         ; mapped                 ; 0066          # 3.1  MATHEMATICAL BOLD SMALL F\n1D420         ; mapped                 ; 0067          # 3.1  MATHEMATICAL BOLD SMALL G\n1D421         ; mapped                 ; 0068          # 3.1  MATHEMATICAL BOLD SMALL H\n1D422         ; mapped                 ; 0069          # 3.1  MATHEMATICAL BOLD SMALL I\n1D423         ; mapped                 ; 006A          # 3.1  MATHEMATICAL BOLD SMALL J\n1D424         ; mapped                 ; 006B          # 3.1  MATHEMATICAL BOLD SMALL K\n1D425         ; mapped                 ; 006C          # 3.1  MATHEMATICAL BOLD SMALL L\n1D426         ; mapped                 ; 006D          # 3.1  MATHEMATICAL BOLD SMALL M\n1D427         ; mapped                 ; 006E          # 3.1  MATHEMATICAL BOLD SMALL N\n1D428         ; mapped                 ; 006F          # 3.1  MATHEMATICAL BOLD SMALL O\n1D429         ; mapped                 ; 0070          # 3.1  MATHEMATICAL BOLD SMALL P\n1D42A         ; mapped                 ; 0071          # 3.1  MATHEMATICAL BOLD SMALL Q\n1D42B         ; mapped                 ; 0072          # 3.1  MATHEMATICAL BOLD SMALL R\n1D42C         ; mapped                 ; 0073          # 3.1  MATHEMATICAL BOLD SMALL S\n1D42D         ; mapped                 ; 0074          # 3.1  MATHEMATICAL BOLD SMALL T\n1D42E         ; mapped                 ; 0075          # 3.1  MATHEMATICAL BOLD SMALL U\n1D42F         ; mapped                 ; 0076          # 3.1  MATHEMATICAL BOLD SMALL V\n1D430         ; mapped                 ; 0077          # 3.1  MATHEMATICAL BOLD SMALL W\n1D431         ; mapped                 ; 0078          # 3.1  MATHEMATICAL BOLD SMALL X\n1D432         ; mapped                 ; 0079          # 3.1  MATHEMATICAL BOLD SMALL Y\n1D433         ; mapped                 ; 007A          # 3.1  MATHEMATICAL BOLD SMALL Z\n1D434         ; mapped                 ; 0061          # 3.1  MATHEMATICAL ITALIC CAPITAL A\n1D435         ; mapped                 ; 0062          # 3.1  MATHEMATICAL ITALIC CAPITAL B\n1D436         ; mapped                 ; 0063          # 3.1  MATHEMATICAL ITALIC CAPITAL C\n1D437         ; mapped                 ; 0064          # 3.1  MATHEMATICAL ITALIC CAPITAL D\n1D438         ; mapped                 ; 0065          # 3.1  MATHEMATICAL ITALIC CAPITAL E\n1D439         ; mapped                 ; 0066          # 3.1  MATHEMATICAL ITALIC CAPITAL F\n1D43A         ; mapped                 ; 0067          # 3.1  MATHEMATICAL ITALIC CAPITAL G\n1D43B         ; mapped                 ; 0068          # 3.1  MATHEMATICAL ITALIC CAPITAL H\n1D43C         ; mapped                 ; 0069          # 3.1  MATHEMATICAL ITALIC CAPITAL I\n1D43D         ; mapped                 ; 006A          # 3.1  MATHEMATICAL ITALIC CAPITAL J\n1D43E         ; mapped                 ; 006B          # 3.1  MATHEMATICAL ITALIC CAPITAL K\n1D43F         ; mapped                 ; 006C          # 3.1  MATHEMATICAL ITALIC CAPITAL L\n1D440         ; mapped                 ; 006D          # 3.1  MATHEMATICAL ITALIC CAPITAL M\n1D441         ; mapped                 ; 006E          # 3.1  MATHEMATICAL ITALIC CAPITAL N\n1D442         ; mapped                 ; 006F          # 3.1  MATHEMATICAL ITALIC CAPITAL O\n1D443         ; mapped                 ; 0070          # 3.1  MATHEMATICAL ITALIC CAPITAL P\n1D444         ; mapped                 ; 0071          # 3.1  MATHEMATICAL ITALIC CAPITAL Q\n1D445         ; mapped                 ; 0072          # 3.1  MATHEMATICAL ITALIC CAPITAL R\n1D446         ; mapped                 ; 0073          # 3.1  MATHEMATICAL ITALIC CAPITAL S\n1D447         ; mapped                 ; 0074          # 3.1  MATHEMATICAL ITALIC CAPITAL T\n1D448         ; mapped                 ; 0075          # 3.1  MATHEMATICAL ITALIC CAPITAL U\n1D449         ; mapped                 ; 0076          # 3.1  MATHEMATICAL ITALIC CAPITAL V\n1D44A         ; mapped                 ; 0077          # 3.1  MATHEMATICAL ITALIC CAPITAL W\n1D44B         ; mapped                 ; 0078          # 3.1  MATHEMATICAL ITALIC CAPITAL X\n1D44C         ; mapped                 ; 0079          # 3.1  MATHEMATICAL ITALIC CAPITAL Y\n1D44D         ; mapped                 ; 007A          # 3.1  MATHEMATICAL ITALIC CAPITAL Z\n1D44E         ; mapped                 ; 0061          # 3.1  MATHEMATICAL ITALIC SMALL A\n1D44F         ; mapped                 ; 0062          # 3.1  MATHEMATICAL ITALIC SMALL B\n1D450         ; mapped                 ; 0063          # 3.1  MATHEMATICAL ITALIC SMALL C\n1D451         ; mapped                 ; 0064          # 3.1  MATHEMATICAL ITALIC SMALL D\n1D452         ; mapped                 ; 0065          # 3.1  MATHEMATICAL ITALIC SMALL E\n1D453         ; mapped                 ; 0066          # 3.1  MATHEMATICAL ITALIC SMALL F\n1D454         ; mapped                 ; 0067          # 3.1  MATHEMATICAL ITALIC SMALL G\n1D455         ; disallowed                             # NA   <reserved-1D455>\n1D456         ; mapped                 ; 0069          # 3.1  MATHEMATICAL ITALIC SMALL I\n1D457         ; mapped                 ; 006A          # 3.1  MATHEMATICAL ITALIC SMALL J\n1D458         ; mapped                 ; 006B          # 3.1  MATHEMATICAL ITALIC SMALL K\n1D459         ; mapped                 ; 006C          # 3.1  MATHEMATICAL ITALIC SMALL L\n1D45A         ; mapped                 ; 006D          # 3.1  MATHEMATICAL ITALIC SMALL M\n1D45B         ; mapped                 ; 006E          # 3.1  MATHEMATICAL ITALIC SMALL N\n1D45C         ; mapped                 ; 006F          # 3.1  MATHEMATICAL ITALIC SMALL O\n1D45D         ; mapped                 ; 0070          # 3.1  MATHEMATICAL ITALIC SMALL P\n1D45E         ; mapped                 ; 0071          # 3.1  MATHEMATICAL ITALIC SMALL Q\n1D45F         ; mapped                 ; 0072          # 3.1  MATHEMATICAL ITALIC SMALL R\n1D460         ; mapped                 ; 0073          # 3.1  MATHEMATICAL ITALIC SMALL S\n1D461         ; mapped                 ; 0074          # 3.1  MATHEMATICAL ITALIC SMALL T\n1D462         ; mapped                 ; 0075          # 3.1  MATHEMATICAL ITALIC SMALL U\n1D463         ; mapped                 ; 0076          # 3.1  MATHEMATICAL ITALIC SMALL V\n1D464         ; mapped                 ; 0077          # 3.1  MATHEMATICAL ITALIC SMALL W\n1D465         ; mapped                 ; 0078          # 3.1  MATHEMATICAL ITALIC SMALL X\n1D466         ; mapped                 ; 0079          # 3.1  MATHEMATICAL ITALIC SMALL Y\n1D467         ; mapped                 ; 007A          # 3.1  MATHEMATICAL ITALIC SMALL Z\n1D468         ; mapped                 ; 0061          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL A\n1D469         ; mapped                 ; 0062          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL B\n1D46A         ; mapped                 ; 0063          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL C\n1D46B         ; mapped                 ; 0064          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL D\n1D46C         ; mapped                 ; 0065          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL E\n1D46D         ; mapped                 ; 0066          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL F\n1D46E         ; mapped                 ; 0067          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL G\n1D46F         ; mapped                 ; 0068          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL H\n1D470         ; mapped                 ; 0069          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL I\n1D471         ; mapped                 ; 006A          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL J\n1D472         ; mapped                 ; 006B          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL K\n1D473         ; mapped                 ; 006C          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL L\n1D474         ; mapped                 ; 006D          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL M\n1D475         ; mapped                 ; 006E          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL N\n1D476         ; mapped                 ; 006F          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL O\n1D477         ; mapped                 ; 0070          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL P\n1D478         ; mapped                 ; 0071          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL Q\n1D479         ; mapped                 ; 0072          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL R\n1D47A         ; mapped                 ; 0073          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL S\n1D47B         ; mapped                 ; 0074          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL T\n1D47C         ; mapped                 ; 0075          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL U\n1D47D         ; mapped                 ; 0076          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL V\n1D47E         ; mapped                 ; 0077          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL W\n1D47F         ; mapped                 ; 0078          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL X\n1D480         ; mapped                 ; 0079          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL Y\n1D481         ; mapped                 ; 007A          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL Z\n1D482         ; mapped                 ; 0061          # 3.1  MATHEMATICAL BOLD ITALIC SMALL A\n1D483         ; mapped                 ; 0062          # 3.1  MATHEMATICAL BOLD ITALIC SMALL B\n1D484         ; mapped                 ; 0063          # 3.1  MATHEMATICAL BOLD ITALIC SMALL C\n1D485         ; mapped                 ; 0064          # 3.1  MATHEMATICAL BOLD ITALIC SMALL D\n1D486         ; mapped                 ; 0065          # 3.1  MATHEMATICAL BOLD ITALIC SMALL E\n1D487         ; mapped                 ; 0066          # 3.1  MATHEMATICAL BOLD ITALIC SMALL F\n1D488         ; mapped                 ; 0067          # 3.1  MATHEMATICAL BOLD ITALIC SMALL G\n1D489         ; mapped                 ; 0068          # 3.1  MATHEMATICAL BOLD ITALIC SMALL H\n1D48A         ; mapped                 ; 0069          # 3.1  MATHEMATICAL BOLD ITALIC SMALL I\n1D48B         ; mapped                 ; 006A          # 3.1  MATHEMATICAL BOLD ITALIC SMALL J\n1D48C         ; mapped                 ; 006B          # 3.1  MATHEMATICAL BOLD ITALIC SMALL K\n1D48D         ; mapped                 ; 006C          # 3.1  MATHEMATICAL BOLD ITALIC SMALL L\n1D48E         ; mapped                 ; 006D          # 3.1  MATHEMATICAL BOLD ITALIC SMALL M\n1D48F         ; mapped                 ; 006E          # 3.1  MATHEMATICAL BOLD ITALIC SMALL N\n1D490         ; mapped                 ; 006F          # 3.1  MATHEMATICAL BOLD ITALIC SMALL O\n1D491         ; mapped                 ; 0070          # 3.1  MATHEMATICAL BOLD ITALIC SMALL P\n1D492         ; mapped                 ; 0071          # 3.1  MATHEMATICAL BOLD ITALIC SMALL Q\n1D493         ; mapped                 ; 0072          # 3.1  MATHEMATICAL BOLD ITALIC SMALL R\n1D494         ; mapped                 ; 0073          # 3.1  MATHEMATICAL BOLD ITALIC SMALL S\n1D495         ; mapped                 ; 0074          # 3.1  MATHEMATICAL BOLD ITALIC SMALL T\n1D496         ; mapped                 ; 0075          # 3.1  MATHEMATICAL BOLD ITALIC SMALL U\n1D497         ; mapped                 ; 0076          # 3.1  MATHEMATICAL BOLD ITALIC SMALL V\n1D498         ; mapped                 ; 0077          # 3.1  MATHEMATICAL BOLD ITALIC SMALL W\n1D499         ; mapped                 ; 0078          # 3.1  MATHEMATICAL BOLD ITALIC SMALL X\n1D49A         ; mapped                 ; 0079          # 3.1  MATHEMATICAL BOLD ITALIC SMALL Y\n1D49B         ; mapped                 ; 007A          # 3.1  MATHEMATICAL BOLD ITALIC SMALL Z\n1D49C         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SCRIPT CAPITAL A\n1D49D         ; disallowed                             # NA   <reserved-1D49D>\n1D49E         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SCRIPT CAPITAL C\n1D49F         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SCRIPT CAPITAL D\n1D4A0..1D4A1  ; disallowed                             # NA   <reserved-1D4A0>..<reserved-1D4A1>\n1D4A2         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SCRIPT CAPITAL G\n1D4A3..1D4A4  ; disallowed                             # NA   <reserved-1D4A3>..<reserved-1D4A4>\n1D4A5         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SCRIPT CAPITAL J\n1D4A6         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SCRIPT CAPITAL K\n1D4A7..1D4A8  ; disallowed                             # NA   <reserved-1D4A7>..<reserved-1D4A8>\n1D4A9         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SCRIPT CAPITAL N\n1D4AA         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SCRIPT CAPITAL O\n1D4AB         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SCRIPT CAPITAL P\n1D4AC         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SCRIPT CAPITAL Q\n1D4AD         ; disallowed                             # NA   <reserved-1D4AD>\n1D4AE         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SCRIPT CAPITAL S\n1D4AF         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SCRIPT CAPITAL T\n1D4B0         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SCRIPT CAPITAL U\n1D4B1         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SCRIPT CAPITAL V\n1D4B2         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SCRIPT CAPITAL W\n1D4B3         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SCRIPT CAPITAL X\n1D4B4         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SCRIPT CAPITAL Y\n1D4B5         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SCRIPT CAPITAL Z\n1D4B6         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SCRIPT SMALL A\n1D4B7         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SCRIPT SMALL B\n1D4B8         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SCRIPT SMALL C\n1D4B9         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SCRIPT SMALL D\n1D4BA         ; disallowed                             # NA   <reserved-1D4BA>\n1D4BB         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SCRIPT SMALL F\n1D4BC         ; disallowed                             # NA   <reserved-1D4BC>\n1D4BD         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SCRIPT SMALL H\n1D4BE         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SCRIPT SMALL I\n1D4BF         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SCRIPT SMALL J\n1D4C0         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SCRIPT SMALL K\n1D4C1         ; mapped                 ; 006C          # 4.0  MATHEMATICAL SCRIPT SMALL L\n1D4C2         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SCRIPT SMALL M\n1D4C3         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SCRIPT SMALL N\n1D4C4         ; disallowed                             # NA   <reserved-1D4C4>\n1D4C5         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SCRIPT SMALL P\n1D4C6         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SCRIPT SMALL Q\n1D4C7         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SCRIPT SMALL R\n1D4C8         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SCRIPT SMALL S\n1D4C9         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SCRIPT SMALL T\n1D4CA         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SCRIPT SMALL U\n1D4CB         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SCRIPT SMALL V\n1D4CC         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SCRIPT SMALL W\n1D4CD         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SCRIPT SMALL X\n1D4CE         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SCRIPT SMALL Y\n1D4CF         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SCRIPT SMALL Z\n1D4D0         ; mapped                 ; 0061          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL A\n1D4D1         ; mapped                 ; 0062          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL B\n1D4D2         ; mapped                 ; 0063          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL C\n1D4D3         ; mapped                 ; 0064          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL D\n1D4D4         ; mapped                 ; 0065          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL E\n1D4D5         ; mapped                 ; 0066          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL F\n1D4D6         ; mapped                 ; 0067          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL G\n1D4D7         ; mapped                 ; 0068          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL H\n1D4D8         ; mapped                 ; 0069          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL I\n1D4D9         ; mapped                 ; 006A          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL J\n1D4DA         ; mapped                 ; 006B          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL K\n1D4DB         ; mapped                 ; 006C          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL L\n1D4DC         ; mapped                 ; 006D          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL M\n1D4DD         ; mapped                 ; 006E          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL N\n1D4DE         ; mapped                 ; 006F          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL O\n1D4DF         ; mapped                 ; 0070          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL P\n1D4E0         ; mapped                 ; 0071          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL Q\n1D4E1         ; mapped                 ; 0072          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL R\n1D4E2         ; mapped                 ; 0073          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL S\n1D4E3         ; mapped                 ; 0074          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL T\n1D4E4         ; mapped                 ; 0075          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL U\n1D4E5         ; mapped                 ; 0076          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL V\n1D4E6         ; mapped                 ; 0077          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL W\n1D4E7         ; mapped                 ; 0078          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL X\n1D4E8         ; mapped                 ; 0079          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL Y\n1D4E9         ; mapped                 ; 007A          # 3.1  MATHEMATICAL BOLD SCRIPT CAPITAL Z\n1D4EA         ; mapped                 ; 0061          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL A\n1D4EB         ; mapped                 ; 0062          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL B\n1D4EC         ; mapped                 ; 0063          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL C\n1D4ED         ; mapped                 ; 0064          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL D\n1D4EE         ; mapped                 ; 0065          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL E\n1D4EF         ; mapped                 ; 0066          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL F\n1D4F0         ; mapped                 ; 0067          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL G\n1D4F1         ; mapped                 ; 0068          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL H\n1D4F2         ; mapped                 ; 0069          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL I\n1D4F3         ; mapped                 ; 006A          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL J\n1D4F4         ; mapped                 ; 006B          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL K\n1D4F5         ; mapped                 ; 006C          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL L\n1D4F6         ; mapped                 ; 006D          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL M\n1D4F7         ; mapped                 ; 006E          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL N\n1D4F8         ; mapped                 ; 006F          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL O\n1D4F9         ; mapped                 ; 0070          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL P\n1D4FA         ; mapped                 ; 0071          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL Q\n1D4FB         ; mapped                 ; 0072          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL R\n1D4FC         ; mapped                 ; 0073          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL S\n1D4FD         ; mapped                 ; 0074          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL T\n1D4FE         ; mapped                 ; 0075          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL U\n1D4FF         ; mapped                 ; 0076          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL V\n1D500         ; mapped                 ; 0077          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL W\n1D501         ; mapped                 ; 0078          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL X\n1D502         ; mapped                 ; 0079          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL Y\n1D503         ; mapped                 ; 007A          # 3.1  MATHEMATICAL BOLD SCRIPT SMALL Z\n1D504         ; mapped                 ; 0061          # 3.1  MATHEMATICAL FRAKTUR CAPITAL A\n1D505         ; mapped                 ; 0062          # 3.1  MATHEMATICAL FRAKTUR CAPITAL B\n1D506         ; disallowed                             # NA   <reserved-1D506>\n1D507         ; mapped                 ; 0064          # 3.1  MATHEMATICAL FRAKTUR CAPITAL D\n1D508         ; mapped                 ; 0065          # 3.1  MATHEMATICAL FRAKTUR CAPITAL E\n1D509         ; mapped                 ; 0066          # 3.1  MATHEMATICAL FRAKTUR CAPITAL F\n1D50A         ; mapped                 ; 0067          # 3.1  MATHEMATICAL FRAKTUR CAPITAL G\n1D50B..1D50C  ; disallowed                             # NA   <reserved-1D50B>..<reserved-1D50C>\n1D50D         ; mapped                 ; 006A          # 3.1  MATHEMATICAL FRAKTUR CAPITAL J\n1D50E         ; mapped                 ; 006B          # 3.1  MATHEMATICAL FRAKTUR CAPITAL K\n1D50F         ; mapped                 ; 006C          # 3.1  MATHEMATICAL FRAKTUR CAPITAL L\n1D510         ; mapped                 ; 006D          # 3.1  MATHEMATICAL FRAKTUR CAPITAL M\n1D511         ; mapped                 ; 006E          # 3.1  MATHEMATICAL FRAKTUR CAPITAL N\n1D512         ; mapped                 ; 006F          # 3.1  MATHEMATICAL FRAKTUR CAPITAL O\n1D513         ; mapped                 ; 0070          # 3.1  MATHEMATICAL FRAKTUR CAPITAL P\n1D514         ; mapped                 ; 0071          # 3.1  MATHEMATICAL FRAKTUR CAPITAL Q\n1D515         ; disallowed                             # NA   <reserved-1D515>\n1D516         ; mapped                 ; 0073          # 3.1  MATHEMATICAL FRAKTUR CAPITAL S\n1D517         ; mapped                 ; 0074          # 3.1  MATHEMATICAL FRAKTUR CAPITAL T\n1D518         ; mapped                 ; 0075          # 3.1  MATHEMATICAL FRAKTUR CAPITAL U\n1D519         ; mapped                 ; 0076          # 3.1  MATHEMATICAL FRAKTUR CAPITAL V\n1D51A         ; mapped                 ; 0077          # 3.1  MATHEMATICAL FRAKTUR CAPITAL W\n1D51B         ; mapped                 ; 0078          # 3.1  MATHEMATICAL FRAKTUR CAPITAL X\n1D51C         ; mapped                 ; 0079          # 3.1  MATHEMATICAL FRAKTUR CAPITAL Y\n1D51D         ; disallowed                             # NA   <reserved-1D51D>\n1D51E         ; mapped                 ; 0061          # 3.1  MATHEMATICAL FRAKTUR SMALL A\n1D51F         ; mapped                 ; 0062          # 3.1  MATHEMATICAL FRAKTUR SMALL B\n1D520         ; mapped                 ; 0063          # 3.1  MATHEMATICAL FRAKTUR SMALL C\n1D521         ; mapped                 ; 0064          # 3.1  MATHEMATICAL FRAKTUR SMALL D\n1D522         ; mapped                 ; 0065          # 3.1  MATHEMATICAL FRAKTUR SMALL E\n1D523         ; mapped                 ; 0066          # 3.1  MATHEMATICAL FRAKTUR SMALL F\n1D524         ; mapped                 ; 0067          # 3.1  MATHEMATICAL FRAKTUR SMALL G\n1D525         ; mapped                 ; 0068          # 3.1  MATHEMATICAL FRAKTUR SMALL H\n1D526         ; mapped                 ; 0069          # 3.1  MATHEMATICAL FRAKTUR SMALL I\n1D527         ; mapped                 ; 006A          # 3.1  MATHEMATICAL FRAKTUR SMALL J\n1D528         ; mapped                 ; 006B          # 3.1  MATHEMATICAL FRAKTUR SMALL K\n1D529         ; mapped                 ; 006C          # 3.1  MATHEMATICAL FRAKTUR SMALL L\n1D52A         ; mapped                 ; 006D          # 3.1  MATHEMATICAL FRAKTUR SMALL M\n1D52B         ; mapped                 ; 006E          # 3.1  MATHEMATICAL FRAKTUR SMALL N\n1D52C         ; mapped                 ; 006F          # 3.1  MATHEMATICAL FRAKTUR SMALL O\n1D52D         ; mapped                 ; 0070          # 3.1  MATHEMATICAL FRAKTUR SMALL P\n1D52E         ; mapped                 ; 0071          # 3.1  MATHEMATICAL FRAKTUR SMALL Q\n1D52F         ; mapped                 ; 0072          # 3.1  MATHEMATICAL FRAKTUR SMALL R\n1D530         ; mapped                 ; 0073          # 3.1  MATHEMATICAL FRAKTUR SMALL S\n1D531         ; mapped                 ; 0074          # 3.1  MATHEMATICAL FRAKTUR SMALL T\n1D532         ; mapped                 ; 0075          # 3.1  MATHEMATICAL FRAKTUR SMALL U\n1D533         ; mapped                 ; 0076          # 3.1  MATHEMATICAL FRAKTUR SMALL V\n1D534         ; mapped                 ; 0077          # 3.1  MATHEMATICAL FRAKTUR SMALL W\n1D535         ; mapped                 ; 0078          # 3.1  MATHEMATICAL FRAKTUR SMALL X\n1D536         ; mapped                 ; 0079          # 3.1  MATHEMATICAL FRAKTUR SMALL Y\n1D537         ; mapped                 ; 007A          # 3.1  MATHEMATICAL FRAKTUR SMALL Z\n1D538         ; mapped                 ; 0061          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL A\n1D539         ; mapped                 ; 0062          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL B\n1D53A         ; disallowed                             # NA   <reserved-1D53A>\n1D53B         ; mapped                 ; 0064          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL D\n1D53C         ; mapped                 ; 0065          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL E\n1D53D         ; mapped                 ; 0066          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL F\n1D53E         ; mapped                 ; 0067          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL G\n1D53F         ; disallowed                             # NA   <reserved-1D53F>\n1D540         ; mapped                 ; 0069          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL I\n1D541         ; mapped                 ; 006A          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL J\n1D542         ; mapped                 ; 006B          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL K\n1D543         ; mapped                 ; 006C          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL L\n1D544         ; mapped                 ; 006D          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL M\n1D545         ; disallowed                             # NA   <reserved-1D545>\n1D546         ; mapped                 ; 006F          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL O\n1D547..1D549  ; disallowed                             # NA   <reserved-1D547>..<reserved-1D549>\n1D54A         ; mapped                 ; 0073          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL S\n1D54B         ; mapped                 ; 0074          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL T\n1D54C         ; mapped                 ; 0075          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL U\n1D54D         ; mapped                 ; 0076          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL V\n1D54E         ; mapped                 ; 0077          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL W\n1D54F         ; mapped                 ; 0078          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL X\n1D550         ; mapped                 ; 0079          # 3.1  MATHEMATICAL DOUBLE-STRUCK CAPITAL Y\n1D551         ; disallowed                             # NA   <reserved-1D551>\n1D552         ; mapped                 ; 0061          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL A\n1D553         ; mapped                 ; 0062          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL B\n1D554         ; mapped                 ; 0063          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL C\n1D555         ; mapped                 ; 0064          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL D\n1D556         ; mapped                 ; 0065          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL E\n1D557         ; mapped                 ; 0066          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL F\n1D558         ; mapped                 ; 0067          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL G\n1D559         ; mapped                 ; 0068          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL H\n1D55A         ; mapped                 ; 0069          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL I\n1D55B         ; mapped                 ; 006A          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL J\n1D55C         ; mapped                 ; 006B          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL K\n1D55D         ; mapped                 ; 006C          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL L\n1D55E         ; mapped                 ; 006D          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL M\n1D55F         ; mapped                 ; 006E          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL N\n1D560         ; mapped                 ; 006F          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL O\n1D561         ; mapped                 ; 0070          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL P\n1D562         ; mapped                 ; 0071          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL Q\n1D563         ; mapped                 ; 0072          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL R\n1D564         ; mapped                 ; 0073          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL S\n1D565         ; mapped                 ; 0074          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL T\n1D566         ; mapped                 ; 0075          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL U\n1D567         ; mapped                 ; 0076          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL V\n1D568         ; mapped                 ; 0077          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL W\n1D569         ; mapped                 ; 0078          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL X\n1D56A         ; mapped                 ; 0079          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL Y\n1D56B         ; mapped                 ; 007A          # 3.1  MATHEMATICAL DOUBLE-STRUCK SMALL Z\n1D56C         ; mapped                 ; 0061          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL A\n1D56D         ; mapped                 ; 0062          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL B\n1D56E         ; mapped                 ; 0063          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL C\n1D56F         ; mapped                 ; 0064          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL D\n1D570         ; mapped                 ; 0065          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL E\n1D571         ; mapped                 ; 0066          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL F\n1D572         ; mapped                 ; 0067          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL G\n1D573         ; mapped                 ; 0068          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL H\n1D574         ; mapped                 ; 0069          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL I\n1D575         ; mapped                 ; 006A          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL J\n1D576         ; mapped                 ; 006B          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL K\n1D577         ; mapped                 ; 006C          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL L\n1D578         ; mapped                 ; 006D          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL M\n1D579         ; mapped                 ; 006E          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL N\n1D57A         ; mapped                 ; 006F          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL O\n1D57B         ; mapped                 ; 0070          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL P\n1D57C         ; mapped                 ; 0071          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL Q\n1D57D         ; mapped                 ; 0072          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL R\n1D57E         ; mapped                 ; 0073          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL S\n1D57F         ; mapped                 ; 0074          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL T\n1D580         ; mapped                 ; 0075          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL U\n1D581         ; mapped                 ; 0076          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL V\n1D582         ; mapped                 ; 0077          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL W\n1D583         ; mapped                 ; 0078          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL X\n1D584         ; mapped                 ; 0079          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL Y\n1D585         ; mapped                 ; 007A          # 3.1  MATHEMATICAL BOLD FRAKTUR CAPITAL Z\n1D586         ; mapped                 ; 0061          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL A\n1D587         ; mapped                 ; 0062          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL B\n1D588         ; mapped                 ; 0063          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL C\n1D589         ; mapped                 ; 0064          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL D\n1D58A         ; mapped                 ; 0065          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL E\n1D58B         ; mapped                 ; 0066          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL F\n1D58C         ; mapped                 ; 0067          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL G\n1D58D         ; mapped                 ; 0068          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL H\n1D58E         ; mapped                 ; 0069          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL I\n1D58F         ; mapped                 ; 006A          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL J\n1D590         ; mapped                 ; 006B          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL K\n1D591         ; mapped                 ; 006C          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL L\n1D592         ; mapped                 ; 006D          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL M\n1D593         ; mapped                 ; 006E          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL N\n1D594         ; mapped                 ; 006F          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL O\n1D595         ; mapped                 ; 0070          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL P\n1D596         ; mapped                 ; 0071          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL Q\n1D597         ; mapped                 ; 0072          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL R\n1D598         ; mapped                 ; 0073          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL S\n1D599         ; mapped                 ; 0074          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL T\n1D59A         ; mapped                 ; 0075          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL U\n1D59B         ; mapped                 ; 0076          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL V\n1D59C         ; mapped                 ; 0077          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL W\n1D59D         ; mapped                 ; 0078          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL X\n1D59E         ; mapped                 ; 0079          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL Y\n1D59F         ; mapped                 ; 007A          # 3.1  MATHEMATICAL BOLD FRAKTUR SMALL Z\n1D5A0         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL A\n1D5A1         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL B\n1D5A2         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL C\n1D5A3         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL D\n1D5A4         ; mapped                 ; 0065          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL E\n1D5A5         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL F\n1D5A6         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL G\n1D5A7         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL H\n1D5A8         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL I\n1D5A9         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL J\n1D5AA         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL K\n1D5AB         ; mapped                 ; 006C          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL L\n1D5AC         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL M\n1D5AD         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL N\n1D5AE         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL O\n1D5AF         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL P\n1D5B0         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL Q\n1D5B1         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL R\n1D5B2         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL S\n1D5B3         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL T\n1D5B4         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL U\n1D5B5         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL V\n1D5B6         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL W\n1D5B7         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL X\n1D5B8         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL Y\n1D5B9         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SANS-SERIF CAPITAL Z\n1D5BA         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SANS-SERIF SMALL A\n1D5BB         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SANS-SERIF SMALL B\n1D5BC         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SANS-SERIF SMALL C\n1D5BD         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SANS-SERIF SMALL D\n1D5BE         ; mapped                 ; 0065          # 3.1  MATHEMATICAL SANS-SERIF SMALL E\n1D5BF         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SANS-SERIF SMALL F\n1D5C0         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SANS-SERIF SMALL G\n1D5C1         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SANS-SERIF SMALL H\n1D5C2         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SANS-SERIF SMALL I\n1D5C3         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SANS-SERIF SMALL J\n1D5C4         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SANS-SERIF SMALL K\n1D5C5         ; mapped                 ; 006C          # 3.1  MATHEMATICAL SANS-SERIF SMALL L\n1D5C6         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SANS-SERIF SMALL M\n1D5C7         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SANS-SERIF SMALL N\n1D5C8         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SANS-SERIF SMALL O\n1D5C9         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SANS-SERIF SMALL P\n1D5CA         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SANS-SERIF SMALL Q\n1D5CB         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SANS-SERIF SMALL R\n1D5CC         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SANS-SERIF SMALL S\n1D5CD         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SANS-SERIF SMALL T\n1D5CE         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SANS-SERIF SMALL U\n1D5CF         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SANS-SERIF SMALL V\n1D5D0         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SANS-SERIF SMALL W\n1D5D1         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SANS-SERIF SMALL X\n1D5D2         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SANS-SERIF SMALL Y\n1D5D3         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SANS-SERIF SMALL Z\n1D5D4         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL A\n1D5D5         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL B\n1D5D6         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL C\n1D5D7         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL D\n1D5D8         ; mapped                 ; 0065          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL E\n1D5D9         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL F\n1D5DA         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL G\n1D5DB         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL H\n1D5DC         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL I\n1D5DD         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL J\n1D5DE         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL K\n1D5DF         ; mapped                 ; 006C          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL L\n1D5E0         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL M\n1D5E1         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL N\n1D5E2         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL O\n1D5E3         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL P\n1D5E4         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL Q\n1D5E5         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL R\n1D5E6         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL S\n1D5E7         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL T\n1D5E8         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL U\n1D5E9         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL V\n1D5EA         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL W\n1D5EB         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL X\n1D5EC         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL Y\n1D5ED         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL Z\n1D5EE         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL A\n1D5EF         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL B\n1D5F0         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL C\n1D5F1         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL D\n1D5F2         ; mapped                 ; 0065          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL E\n1D5F3         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL F\n1D5F4         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL G\n1D5F5         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL H\n1D5F6         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL I\n1D5F7         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL J\n1D5F8         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL K\n1D5F9         ; mapped                 ; 006C          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL L\n1D5FA         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL M\n1D5FB         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL N\n1D5FC         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL O\n1D5FD         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL P\n1D5FE         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL Q\n1D5FF         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL R\n1D600         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL S\n1D601         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL T\n1D602         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL U\n1D603         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL V\n1D604         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL W\n1D605         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL X\n1D606         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL Y\n1D607         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL Z\n1D608         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL A\n1D609         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL B\n1D60A         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL C\n1D60B         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL D\n1D60C         ; mapped                 ; 0065          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL E\n1D60D         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL F\n1D60E         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL G\n1D60F         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL H\n1D610         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL I\n1D611         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL J\n1D612         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL K\n1D613         ; mapped                 ; 006C          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL L\n1D614         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL M\n1D615         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL N\n1D616         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL O\n1D617         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL P\n1D618         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q\n1D619         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL R\n1D61A         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL S\n1D61B         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL T\n1D61C         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL U\n1D61D         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL V\n1D61E         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL W\n1D61F         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL X\n1D620         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y\n1D621         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z\n1D622         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL A\n1D623         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL B\n1D624         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL C\n1D625         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL D\n1D626         ; mapped                 ; 0065          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL E\n1D627         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL F\n1D628         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL G\n1D629         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL H\n1D62A         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL I\n1D62B         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL J\n1D62C         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL K\n1D62D         ; mapped                 ; 006C          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL L\n1D62E         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL M\n1D62F         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL N\n1D630         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL O\n1D631         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL P\n1D632         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL Q\n1D633         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL R\n1D634         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL S\n1D635         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL T\n1D636         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL U\n1D637         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL V\n1D638         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL W\n1D639         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL X\n1D63A         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL Y\n1D63B         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SANS-SERIF ITALIC SMALL Z\n1D63C         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A\n1D63D         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B\n1D63E         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C\n1D63F         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D\n1D640         ; mapped                 ; 0065          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E\n1D641         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F\n1D642         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G\n1D643         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H\n1D644         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I\n1D645         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J\n1D646         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K\n1D647         ; mapped                 ; 006C          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L\n1D648         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M\n1D649         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N\n1D64A         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O\n1D64B         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P\n1D64C         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q\n1D64D         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R\n1D64E         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S\n1D64F         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T\n1D650         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U\n1D651         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V\n1D652         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W\n1D653         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X\n1D654         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y\n1D655         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z\n1D656         ; mapped                 ; 0061          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A\n1D657         ; mapped                 ; 0062          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B\n1D658         ; mapped                 ; 0063          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C\n1D659         ; mapped                 ; 0064          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D\n1D65A         ; mapped                 ; 0065          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E\n1D65B         ; mapped                 ; 0066          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F\n1D65C         ; mapped                 ; 0067          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G\n1D65D         ; mapped                 ; 0068          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H\n1D65E         ; mapped                 ; 0069          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I\n1D65F         ; mapped                 ; 006A          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J\n1D660         ; mapped                 ; 006B          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K\n1D661         ; mapped                 ; 006C          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L\n1D662         ; mapped                 ; 006D          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M\n1D663         ; mapped                 ; 006E          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N\n1D664         ; mapped                 ; 006F          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O\n1D665         ; mapped                 ; 0070          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P\n1D666         ; mapped                 ; 0071          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q\n1D667         ; mapped                 ; 0072          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R\n1D668         ; mapped                 ; 0073          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S\n1D669         ; mapped                 ; 0074          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T\n1D66A         ; mapped                 ; 0075          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U\n1D66B         ; mapped                 ; 0076          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V\n1D66C         ; mapped                 ; 0077          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W\n1D66D         ; mapped                 ; 0078          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X\n1D66E         ; mapped                 ; 0079          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y\n1D66F         ; mapped                 ; 007A          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z\n1D670         ; mapped                 ; 0061          # 3.1  MATHEMATICAL MONOSPACE CAPITAL A\n1D671         ; mapped                 ; 0062          # 3.1  MATHEMATICAL MONOSPACE CAPITAL B\n1D672         ; mapped                 ; 0063          # 3.1  MATHEMATICAL MONOSPACE CAPITAL C\n1D673         ; mapped                 ; 0064          # 3.1  MATHEMATICAL MONOSPACE CAPITAL D\n1D674         ; mapped                 ; 0065          # 3.1  MATHEMATICAL MONOSPACE CAPITAL E\n1D675         ; mapped                 ; 0066          # 3.1  MATHEMATICAL MONOSPACE CAPITAL F\n1D676         ; mapped                 ; 0067          # 3.1  MATHEMATICAL MONOSPACE CAPITAL G\n1D677         ; mapped                 ; 0068          # 3.1  MATHEMATICAL MONOSPACE CAPITAL H\n1D678         ; mapped                 ; 0069          # 3.1  MATHEMATICAL MONOSPACE CAPITAL I\n1D679         ; mapped                 ; 006A          # 3.1  MATHEMATICAL MONOSPACE CAPITAL J\n1D67A         ; mapped                 ; 006B          # 3.1  MATHEMATICAL MONOSPACE CAPITAL K\n1D67B         ; mapped                 ; 006C          # 3.1  MATHEMATICAL MONOSPACE CAPITAL L\n1D67C         ; mapped                 ; 006D          # 3.1  MATHEMATICAL MONOSPACE CAPITAL M\n1D67D         ; mapped                 ; 006E          # 3.1  MATHEMATICAL MONOSPACE CAPITAL N\n1D67E         ; mapped                 ; 006F          # 3.1  MATHEMATICAL MONOSPACE CAPITAL O\n1D67F         ; mapped                 ; 0070          # 3.1  MATHEMATICAL MONOSPACE CAPITAL P\n1D680         ; mapped                 ; 0071          # 3.1  MATHEMATICAL MONOSPACE CAPITAL Q\n1D681         ; mapped                 ; 0072          # 3.1  MATHEMATICAL MONOSPACE CAPITAL R\n1D682         ; mapped                 ; 0073          # 3.1  MATHEMATICAL MONOSPACE CAPITAL S\n1D683         ; mapped                 ; 0074          # 3.1  MATHEMATICAL MONOSPACE CAPITAL T\n1D684         ; mapped                 ; 0075          # 3.1  MATHEMATICAL MONOSPACE CAPITAL U\n1D685         ; mapped                 ; 0076          # 3.1  MATHEMATICAL MONOSPACE CAPITAL V\n1D686         ; mapped                 ; 0077          # 3.1  MATHEMATICAL MONOSPACE CAPITAL W\n1D687         ; mapped                 ; 0078          # 3.1  MATHEMATICAL MONOSPACE CAPITAL X\n1D688         ; mapped                 ; 0079          # 3.1  MATHEMATICAL MONOSPACE CAPITAL Y\n1D689         ; mapped                 ; 007A          # 3.1  MATHEMATICAL MONOSPACE CAPITAL Z\n1D68A         ; mapped                 ; 0061          # 3.1  MATHEMATICAL MONOSPACE SMALL A\n1D68B         ; mapped                 ; 0062          # 3.1  MATHEMATICAL MONOSPACE SMALL B\n1D68C         ; mapped                 ; 0063          # 3.1  MATHEMATICAL MONOSPACE SMALL C\n1D68D         ; mapped                 ; 0064          # 3.1  MATHEMATICAL MONOSPACE SMALL D\n1D68E         ; mapped                 ; 0065          # 3.1  MATHEMATICAL MONOSPACE SMALL E\n1D68F         ; mapped                 ; 0066          # 3.1  MATHEMATICAL MONOSPACE SMALL F\n1D690         ; mapped                 ; 0067          # 3.1  MATHEMATICAL MONOSPACE SMALL G\n1D691         ; mapped                 ; 0068          # 3.1  MATHEMATICAL MONOSPACE SMALL H\n1D692         ; mapped                 ; 0069          # 3.1  MATHEMATICAL MONOSPACE SMALL I\n1D693         ; mapped                 ; 006A          # 3.1  MATHEMATICAL MONOSPACE SMALL J\n1D694         ; mapped                 ; 006B          # 3.1  MATHEMATICAL MONOSPACE SMALL K\n1D695         ; mapped                 ; 006C          # 3.1  MATHEMATICAL MONOSPACE SMALL L\n1D696         ; mapped                 ; 006D          # 3.1  MATHEMATICAL MONOSPACE SMALL M\n1D697         ; mapped                 ; 006E          # 3.1  MATHEMATICAL MONOSPACE SMALL N\n1D698         ; mapped                 ; 006F          # 3.1  MATHEMATICAL MONOSPACE SMALL O\n1D699         ; mapped                 ; 0070          # 3.1  MATHEMATICAL MONOSPACE SMALL P\n1D69A         ; mapped                 ; 0071          # 3.1  MATHEMATICAL MONOSPACE SMALL Q\n1D69B         ; mapped                 ; 0072          # 3.1  MATHEMATICAL MONOSPACE SMALL R\n1D69C         ; mapped                 ; 0073          # 3.1  MATHEMATICAL MONOSPACE SMALL S\n1D69D         ; mapped                 ; 0074          # 3.1  MATHEMATICAL MONOSPACE SMALL T\n1D69E         ; mapped                 ; 0075          # 3.1  MATHEMATICAL MONOSPACE SMALL U\n1D69F         ; mapped                 ; 0076          # 3.1  MATHEMATICAL MONOSPACE SMALL V\n1D6A0         ; mapped                 ; 0077          # 3.1  MATHEMATICAL MONOSPACE SMALL W\n1D6A1         ; mapped                 ; 0078          # 3.1  MATHEMATICAL MONOSPACE SMALL X\n1D6A2         ; mapped                 ; 0079          # 3.1  MATHEMATICAL MONOSPACE SMALL Y\n1D6A3         ; mapped                 ; 007A          # 3.1  MATHEMATICAL MONOSPACE SMALL Z\n1D6A4         ; mapped                 ; 0131          # 4.1  MATHEMATICAL ITALIC SMALL DOTLESS I\n1D6A5         ; mapped                 ; 0237          # 4.1  MATHEMATICAL ITALIC SMALL DOTLESS J\n1D6A6..1D6A7  ; disallowed                             # NA   <reserved-1D6A6>..<reserved-1D6A7>\n1D6A8         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL BOLD CAPITAL ALPHA\n1D6A9         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL BOLD CAPITAL BETA\n1D6AA         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL BOLD CAPITAL GAMMA\n1D6AB         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL BOLD CAPITAL DELTA\n1D6AC         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL BOLD CAPITAL EPSILON\n1D6AD         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL BOLD CAPITAL ZETA\n1D6AE         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL BOLD CAPITAL ETA\n1D6AF         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL BOLD CAPITAL THETA\n1D6B0         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL BOLD CAPITAL IOTA\n1D6B1         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL BOLD CAPITAL KAPPA\n1D6B2         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL BOLD CAPITAL LAMDA\n1D6B3         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL BOLD CAPITAL MU\n1D6B4         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL BOLD CAPITAL NU\n1D6B5         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL BOLD CAPITAL XI\n1D6B6         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL BOLD CAPITAL OMICRON\n1D6B7         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL BOLD CAPITAL PI\n1D6B8         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL BOLD CAPITAL RHO\n1D6B9         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL BOLD CAPITAL THETA SYMBOL\n1D6BA         ; mapped                 ; 03C3          # 3.1  MATHEMATICAL BOLD CAPITAL SIGMA\n1D6BB         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL BOLD CAPITAL TAU\n1D6BC         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL BOLD CAPITAL UPSILON\n1D6BD         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL BOLD CAPITAL PHI\n1D6BE         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL BOLD CAPITAL CHI\n1D6BF         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL BOLD CAPITAL PSI\n1D6C0         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL BOLD CAPITAL OMEGA\n1D6C1         ; mapped                 ; 2207          # 3.1  MATHEMATICAL BOLD NABLA\n1D6C2         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL BOLD SMALL ALPHA\n1D6C3         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL BOLD SMALL BETA\n1D6C4         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL BOLD SMALL GAMMA\n1D6C5         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL BOLD SMALL DELTA\n1D6C6         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL BOLD SMALL EPSILON\n1D6C7         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL BOLD SMALL ZETA\n1D6C8         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL BOLD SMALL ETA\n1D6C9         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL BOLD SMALL THETA\n1D6CA         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL BOLD SMALL IOTA\n1D6CB         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL BOLD SMALL KAPPA\n1D6CC         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL BOLD SMALL LAMDA\n1D6CD         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL BOLD SMALL MU\n1D6CE         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL BOLD SMALL NU\n1D6CF         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL BOLD SMALL XI\n1D6D0         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL BOLD SMALL OMICRON\n1D6D1         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL BOLD SMALL PI\n1D6D2         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL BOLD SMALL RHO\n1D6D3..1D6D4  ; mapped                 ; 03C3          # 3.1  MATHEMATICAL BOLD SMALL FINAL SIGMA..MATHEMATICAL BOLD SMALL SIGMA\n1D6D5         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL BOLD SMALL TAU\n1D6D6         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL BOLD SMALL UPSILON\n1D6D7         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL BOLD SMALL PHI\n1D6D8         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL BOLD SMALL CHI\n1D6D9         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL BOLD SMALL PSI\n1D6DA         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL BOLD SMALL OMEGA\n1D6DB         ; mapped                 ; 2202          # 3.1  MATHEMATICAL BOLD PARTIAL DIFFERENTIAL\n1D6DC         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL BOLD EPSILON SYMBOL\n1D6DD         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL BOLD THETA SYMBOL\n1D6DE         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL BOLD KAPPA SYMBOL\n1D6DF         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL BOLD PHI SYMBOL\n1D6E0         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL BOLD RHO SYMBOL\n1D6E1         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL BOLD PI SYMBOL\n1D6E2         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL ITALIC CAPITAL ALPHA\n1D6E3         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL ITALIC CAPITAL BETA\n1D6E4         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL ITALIC CAPITAL GAMMA\n1D6E5         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL ITALIC CAPITAL DELTA\n1D6E6         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL ITALIC CAPITAL EPSILON\n1D6E7         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL ITALIC CAPITAL ZETA\n1D6E8         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL ITALIC CAPITAL ETA\n1D6E9         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL ITALIC CAPITAL THETA\n1D6EA         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL ITALIC CAPITAL IOTA\n1D6EB         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL ITALIC CAPITAL KAPPA\n1D6EC         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL ITALIC CAPITAL LAMDA\n1D6ED         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL ITALIC CAPITAL MU\n1D6EE         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL ITALIC CAPITAL NU\n1D6EF         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL ITALIC CAPITAL XI\n1D6F0         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL ITALIC CAPITAL OMICRON\n1D6F1         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL ITALIC CAPITAL PI\n1D6F2         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL ITALIC CAPITAL RHO\n1D6F3         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL ITALIC CAPITAL THETA SYMBOL\n1D6F4         ; mapped                 ; 03C3          # 3.1  MATHEMATICAL ITALIC CAPITAL SIGMA\n1D6F5         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL ITALIC CAPITAL TAU\n1D6F6         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL ITALIC CAPITAL UPSILON\n1D6F7         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL ITALIC CAPITAL PHI\n1D6F8         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL ITALIC CAPITAL CHI\n1D6F9         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL ITALIC CAPITAL PSI\n1D6FA         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL ITALIC CAPITAL OMEGA\n1D6FB         ; mapped                 ; 2207          # 3.1  MATHEMATICAL ITALIC NABLA\n1D6FC         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL ITALIC SMALL ALPHA\n1D6FD         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL ITALIC SMALL BETA\n1D6FE         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL ITALIC SMALL GAMMA\n1D6FF         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL ITALIC SMALL DELTA\n1D700         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL ITALIC SMALL EPSILON\n1D701         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL ITALIC SMALL ZETA\n1D702         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL ITALIC SMALL ETA\n1D703         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL ITALIC SMALL THETA\n1D704         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL ITALIC SMALL IOTA\n1D705         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL ITALIC SMALL KAPPA\n1D706         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL ITALIC SMALL LAMDA\n1D707         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL ITALIC SMALL MU\n1D708         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL ITALIC SMALL NU\n1D709         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL ITALIC SMALL XI\n1D70A         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL ITALIC SMALL OMICRON\n1D70B         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL ITALIC SMALL PI\n1D70C         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL ITALIC SMALL RHO\n1D70D..1D70E  ; mapped                 ; 03C3          # 3.1  MATHEMATICAL ITALIC SMALL FINAL SIGMA..MATHEMATICAL ITALIC SMALL SIGMA\n1D70F         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL ITALIC SMALL TAU\n1D710         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL ITALIC SMALL UPSILON\n1D711         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL ITALIC SMALL PHI\n1D712         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL ITALIC SMALL CHI\n1D713         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL ITALIC SMALL PSI\n1D714         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL ITALIC SMALL OMEGA\n1D715         ; mapped                 ; 2202          # 3.1  MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL\n1D716         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL ITALIC EPSILON SYMBOL\n1D717         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL ITALIC THETA SYMBOL\n1D718         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL ITALIC KAPPA SYMBOL\n1D719         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL ITALIC PHI SYMBOL\n1D71A         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL ITALIC RHO SYMBOL\n1D71B         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL ITALIC PI SYMBOL\n1D71C         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL ALPHA\n1D71D         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL BETA\n1D71E         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL GAMMA\n1D71F         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL DELTA\n1D720         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL EPSILON\n1D721         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL ZETA\n1D722         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL ETA\n1D723         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL THETA\n1D724         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL IOTA\n1D725         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL KAPPA\n1D726         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL LAMDA\n1D727         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL MU\n1D728         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL NU\n1D729         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL XI\n1D72A         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL OMICRON\n1D72B         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL PI\n1D72C         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL RHO\n1D72D         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL\n1D72E         ; mapped                 ; 03C3          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL SIGMA\n1D72F         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL TAU\n1D730         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL UPSILON\n1D731         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL PHI\n1D732         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL CHI\n1D733         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL PSI\n1D734         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL BOLD ITALIC CAPITAL OMEGA\n1D735         ; mapped                 ; 2207          # 3.1  MATHEMATICAL BOLD ITALIC NABLA\n1D736         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL BOLD ITALIC SMALL ALPHA\n1D737         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL BOLD ITALIC SMALL BETA\n1D738         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL BOLD ITALIC SMALL GAMMA\n1D739         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL BOLD ITALIC SMALL DELTA\n1D73A         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL BOLD ITALIC SMALL EPSILON\n1D73B         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL BOLD ITALIC SMALL ZETA\n1D73C         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL BOLD ITALIC SMALL ETA\n1D73D         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL BOLD ITALIC SMALL THETA\n1D73E         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL BOLD ITALIC SMALL IOTA\n1D73F         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL BOLD ITALIC SMALL KAPPA\n1D740         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL BOLD ITALIC SMALL LAMDA\n1D741         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL BOLD ITALIC SMALL MU\n1D742         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL BOLD ITALIC SMALL NU\n1D743         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL BOLD ITALIC SMALL XI\n1D744         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL BOLD ITALIC SMALL OMICRON\n1D745         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL BOLD ITALIC SMALL PI\n1D746         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL BOLD ITALIC SMALL RHO\n1D747..1D748  ; mapped                 ; 03C3          # 3.1  MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA..MATHEMATICAL BOLD ITALIC SMALL SIGMA\n1D749         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL BOLD ITALIC SMALL TAU\n1D74A         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL BOLD ITALIC SMALL UPSILON\n1D74B         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL BOLD ITALIC SMALL PHI\n1D74C         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL BOLD ITALIC SMALL CHI\n1D74D         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL BOLD ITALIC SMALL PSI\n1D74E         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL BOLD ITALIC SMALL OMEGA\n1D74F         ; mapped                 ; 2202          # 3.1  MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL\n1D750         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL BOLD ITALIC EPSILON SYMBOL\n1D751         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL BOLD ITALIC THETA SYMBOL\n1D752         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL BOLD ITALIC KAPPA SYMBOL\n1D753         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL BOLD ITALIC PHI SYMBOL\n1D754         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL BOLD ITALIC RHO SYMBOL\n1D755         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL BOLD ITALIC PI SYMBOL\n1D756         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA\n1D757         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA\n1D758         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA\n1D759         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA\n1D75A         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON\n1D75B         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA\n1D75C         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA\n1D75D         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA\n1D75E         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA\n1D75F         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA\n1D760         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA\n1D761         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL MU\n1D762         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL NU\n1D763         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL XI\n1D764         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON\n1D765         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL PI\n1D766         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO\n1D767         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL\n1D768         ; mapped                 ; 03C3          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA\n1D769         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU\n1D76A         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON\n1D76B         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI\n1D76C         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI\n1D76D         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI\n1D76E         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA\n1D76F         ; mapped                 ; 2207          # 3.1  MATHEMATICAL SANS-SERIF BOLD NABLA\n1D770         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA\n1D771         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL BETA\n1D772         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA\n1D773         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL DELTA\n1D774         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON\n1D775         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL ZETA\n1D776         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL ETA\n1D777         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL THETA\n1D778         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL IOTA\n1D779         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA\n1D77A         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA\n1D77B         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL MU\n1D77C         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL NU\n1D77D         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL XI\n1D77E         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON\n1D77F         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL PI\n1D780         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL RHO\n1D781..1D782  ; mapped                 ; 03C3          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA..MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA\n1D783         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL TAU\n1D784         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON\n1D785         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL PHI\n1D786         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL CHI\n1D787         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL PSI\n1D788         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA\n1D789         ; mapped                 ; 2202          # 3.1  MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL\n1D78A         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL\n1D78B         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL\n1D78C         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL\n1D78D         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL\n1D78E         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL\n1D78F         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL SANS-SERIF BOLD PI SYMBOL\n1D790         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA\n1D791         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA\n1D792         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA\n1D793         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA\n1D794         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON\n1D795         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA\n1D796         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA\n1D797         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA\n1D798         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA\n1D799         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA\n1D79A         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA\n1D79B         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU\n1D79C         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU\n1D79D         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI\n1D79E         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON\n1D79F         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI\n1D7A0         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO\n1D7A1         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL\n1D7A2         ; mapped                 ; 03C3          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA\n1D7A3         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU\n1D7A4         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON\n1D7A5         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI\n1D7A6         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI\n1D7A7         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI\n1D7A8         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA\n1D7A9         ; mapped                 ; 2207          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA\n1D7AA         ; mapped                 ; 03B1          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA\n1D7AB         ; mapped                 ; 03B2          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA\n1D7AC         ; mapped                 ; 03B3          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA\n1D7AD         ; mapped                 ; 03B4          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA\n1D7AE         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON\n1D7AF         ; mapped                 ; 03B6          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA\n1D7B0         ; mapped                 ; 03B7          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA\n1D7B1         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA\n1D7B2         ; mapped                 ; 03B9          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA\n1D7B3         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA\n1D7B4         ; mapped                 ; 03BB          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA\n1D7B5         ; mapped                 ; 03BC          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU\n1D7B6         ; mapped                 ; 03BD          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU\n1D7B7         ; mapped                 ; 03BE          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI\n1D7B8         ; mapped                 ; 03BF          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON\n1D7B9         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI\n1D7BA         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO\n1D7BB..1D7BC  ; mapped                 ; 03C3          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA\n1D7BD         ; mapped                 ; 03C4          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU\n1D7BE         ; mapped                 ; 03C5          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON\n1D7BF         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI\n1D7C0         ; mapped                 ; 03C7          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI\n1D7C1         ; mapped                 ; 03C8          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI\n1D7C2         ; mapped                 ; 03C9          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA\n1D7C3         ; mapped                 ; 2202          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL\n1D7C4         ; mapped                 ; 03B5          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL\n1D7C5         ; mapped                 ; 03B8          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL\n1D7C6         ; mapped                 ; 03BA          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL\n1D7C7         ; mapped                 ; 03C6          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL\n1D7C8         ; mapped                 ; 03C1          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL\n1D7C9         ; mapped                 ; 03C0          # 3.1  MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL\n1D7CA..1D7CB  ; mapped                 ; 03DD          # 5.0  MATHEMATICAL BOLD CAPITAL DIGAMMA..MATHEMATICAL BOLD SMALL DIGAMMA\n1D7CC..1D7CD  ; disallowed                             # NA   <reserved-1D7CC>..<reserved-1D7CD>\n1D7CE         ; mapped                 ; 0030          # 3.1  MATHEMATICAL BOLD DIGIT ZERO\n1D7CF         ; mapped                 ; 0031          # 3.1  MATHEMATICAL BOLD DIGIT ONE\n1D7D0         ; mapped                 ; 0032          # 3.1  MATHEMATICAL BOLD DIGIT TWO\n1D7D1         ; mapped                 ; 0033          # 3.1  MATHEMATICAL BOLD DIGIT THREE\n1D7D2         ; mapped                 ; 0034          # 3.1  MATHEMATICAL BOLD DIGIT FOUR\n1D7D3         ; mapped                 ; 0035          # 3.1  MATHEMATICAL BOLD DIGIT FIVE\n1D7D4         ; mapped                 ; 0036          # 3.1  MATHEMATICAL BOLD DIGIT SIX\n1D7D5         ; mapped                 ; 0037          # 3.1  MATHEMATICAL BOLD DIGIT SEVEN\n1D7D6         ; mapped                 ; 0038          # 3.1  MATHEMATICAL BOLD DIGIT EIGHT\n1D7D7         ; mapped                 ; 0039          # 3.1  MATHEMATICAL BOLD DIGIT NINE\n1D7D8         ; mapped                 ; 0030          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO\n1D7D9         ; mapped                 ; 0031          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT ONE\n1D7DA         ; mapped                 ; 0032          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT TWO\n1D7DB         ; mapped                 ; 0033          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT THREE\n1D7DC         ; mapped                 ; 0034          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR\n1D7DD         ; mapped                 ; 0035          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE\n1D7DE         ; mapped                 ; 0036          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT SIX\n1D7DF         ; mapped                 ; 0037          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN\n1D7E0         ; mapped                 ; 0038          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT\n1D7E1         ; mapped                 ; 0039          # 3.1  MATHEMATICAL DOUBLE-STRUCK DIGIT NINE\n1D7E2         ; mapped                 ; 0030          # 3.1  MATHEMATICAL SANS-SERIF DIGIT ZERO\n1D7E3         ; mapped                 ; 0031          # 3.1  MATHEMATICAL SANS-SERIF DIGIT ONE\n1D7E4         ; mapped                 ; 0032          # 3.1  MATHEMATICAL SANS-SERIF DIGIT TWO\n1D7E5         ; mapped                 ; 0033          # 3.1  MATHEMATICAL SANS-SERIF DIGIT THREE\n1D7E6         ; mapped                 ; 0034          # 3.1  MATHEMATICAL SANS-SERIF DIGIT FOUR\n1D7E7         ; mapped                 ; 0035          # 3.1  MATHEMATICAL SANS-SERIF DIGIT FIVE\n1D7E8         ; mapped                 ; 0036          # 3.1  MATHEMATICAL SANS-SERIF DIGIT SIX\n1D7E9         ; mapped                 ; 0037          # 3.1  MATHEMATICAL SANS-SERIF DIGIT SEVEN\n1D7EA         ; mapped                 ; 0038          # 3.1  MATHEMATICAL SANS-SERIF DIGIT EIGHT\n1D7EB         ; mapped                 ; 0039          # 3.1  MATHEMATICAL SANS-SERIF DIGIT NINE\n1D7EC         ; mapped                 ; 0030          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO\n1D7ED         ; mapped                 ; 0031          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT ONE\n1D7EE         ; mapped                 ; 0032          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT TWO\n1D7EF         ; mapped                 ; 0033          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT THREE\n1D7F0         ; mapped                 ; 0034          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR\n1D7F1         ; mapped                 ; 0035          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE\n1D7F2         ; mapped                 ; 0036          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT SIX\n1D7F3         ; mapped                 ; 0037          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN\n1D7F4         ; mapped                 ; 0038          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT\n1D7F5         ; mapped                 ; 0039          # 3.1  MATHEMATICAL SANS-SERIF BOLD DIGIT NINE\n1D7F6         ; mapped                 ; 0030          # 3.1  MATHEMATICAL MONOSPACE DIGIT ZERO\n1D7F7         ; mapped                 ; 0031          # 3.1  MATHEMATICAL MONOSPACE DIGIT ONE\n1D7F8         ; mapped                 ; 0032          # 3.1  MATHEMATICAL MONOSPACE DIGIT TWO\n1D7F9         ; mapped                 ; 0033          # 3.1  MATHEMATICAL MONOSPACE DIGIT THREE\n1D7FA         ; mapped                 ; 0034          # 3.1  MATHEMATICAL MONOSPACE DIGIT FOUR\n1D7FB         ; mapped                 ; 0035          # 3.1  MATHEMATICAL MONOSPACE DIGIT FIVE\n1D7FC         ; mapped                 ; 0036          # 3.1  MATHEMATICAL MONOSPACE DIGIT SIX\n1D7FD         ; mapped                 ; 0037          # 3.1  MATHEMATICAL MONOSPACE DIGIT SEVEN\n1D7FE         ; mapped                 ; 0038          # 3.1  MATHEMATICAL MONOSPACE DIGIT EIGHT\n1D7FF         ; mapped                 ; 0039          # 3.1  MATHEMATICAL MONOSPACE DIGIT NINE\n1D800..1D9FF  ; valid                  ;      ; NV8    # 8.0  SIGNWRITING HAND-FIST INDEX..SIGNWRITING HEAD\n1DA00..1DA36  ; valid                                  # 8.0  SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN\n1DA37..1DA3A  ; valid                  ;      ; NV8    # 8.0  SIGNWRITING AIR BLOW SMALL ROTATIONS..SIGNWRITING BREATH EXHALE\n1DA3B..1DA6C  ; valid                                  # 8.0  SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT\n1DA6D..1DA74  ; valid                  ;      ; NV8    # 8.0  SIGNWRITING SHOULDER HIP SPINE..SIGNWRITING TORSO-FLOORPLANE TWISTING\n1DA75         ; valid                                  # 8.0  SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS\n1DA76..1DA83  ; valid                  ;      ; NV8    # 8.0  SIGNWRITING LIMB COMBINATION..SIGNWRITING LOCATION DEPTH\n1DA84         ; valid                                  # 8.0  SIGNWRITING LOCATION HEAD NECK\n1DA85..1DA8B  ; valid                  ;      ; NV8    # 8.0  SIGNWRITING LOCATION TORSO..SIGNWRITING PARENTHESIS\n1DA8C..1DA9A  ; disallowed                             # NA   <reserved-1DA8C>..<reserved-1DA9A>\n1DA9B..1DA9F  ; valid                                  # 8.0  SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6\n1DAA0         ; disallowed                             # NA   <reserved-1DAA0>\n1DAA1..1DAAF  ; valid                                  # 8.0  SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16\n1DAB0..1DEFF  ; disallowed                             # NA   <reserved-1DAB0>..<reserved-1DEFF>\n1DF00..1DF1E  ; valid                                  # 14.0 LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER S WITH CURL\n1DF1F..1DF24  ; disallowed                             # NA   <reserved-1DF1F>..<reserved-1DF24>\n1DF25..1DF2A  ; valid                                  # 15.0 LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK\n1DF2B..1DFFF  ; disallowed                             # NA   <reserved-1DF2B>..<reserved-1DFFF>\n1E000..1E006  ; valid                                  # 9.0  COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE\n1E007         ; disallowed                             # NA   <reserved-1E007>\n1E008..1E018  ; valid                                  # 9.0  COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU\n1E019..1E01A  ; disallowed                             # NA   <reserved-1E019>..<reserved-1E01A>\n1E01B..1E021  ; valid                                  # 9.0  COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI\n1E022         ; disallowed                             # NA   <reserved-1E022>\n1E023..1E024  ; valid                                  # 9.0  COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS\n1E025         ; disallowed                             # NA   <reserved-1E025>\n1E026..1E02A  ; valid                                  # 9.0  COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA\n1E02B..1E02F  ; disallowed                             # NA   <reserved-1E02B>..<reserved-1E02F>\n1E030         ; mapped                 ; 0430          # 15.0 MODIFIER LETTER CYRILLIC SMALL A\n1E031         ; mapped                 ; 0431          # 15.0 MODIFIER LETTER CYRILLIC SMALL BE\n1E032         ; mapped                 ; 0432          # 15.0 MODIFIER LETTER CYRILLIC SMALL VE\n1E033         ; mapped                 ; 0433          # 15.0 MODIFIER LETTER CYRILLIC SMALL GHE\n1E034         ; mapped                 ; 0434          # 15.0 MODIFIER LETTER CYRILLIC SMALL DE\n1E035         ; mapped                 ; 0435          # 15.0 MODIFIER LETTER CYRILLIC SMALL IE\n1E036         ; mapped                 ; 0436          # 15.0 MODIFIER LETTER CYRILLIC SMALL ZHE\n1E037         ; mapped                 ; 0437          # 15.0 MODIFIER LETTER CYRILLIC SMALL ZE\n1E038         ; mapped                 ; 0438          # 15.0 MODIFIER LETTER CYRILLIC SMALL I\n1E039         ; mapped                 ; 043A          # 15.0 MODIFIER LETTER CYRILLIC SMALL KA\n1E03A         ; mapped                 ; 043B          # 15.0 MODIFIER LETTER CYRILLIC SMALL EL\n1E03B         ; mapped                 ; 043C          # 15.0 MODIFIER LETTER CYRILLIC SMALL EM\n1E03C         ; mapped                 ; 043E          # 15.0 MODIFIER LETTER CYRILLIC SMALL O\n1E03D         ; mapped                 ; 043F          # 15.0 MODIFIER LETTER CYRILLIC SMALL PE\n1E03E         ; mapped                 ; 0440          # 15.0 MODIFIER LETTER CYRILLIC SMALL ER\n1E03F         ; mapped                 ; 0441          # 15.0 MODIFIER LETTER CYRILLIC SMALL ES\n1E040         ; mapped                 ; 0442          # 15.0 MODIFIER LETTER CYRILLIC SMALL TE\n1E041         ; mapped                 ; 0443          # 15.0 MODIFIER LETTER CYRILLIC SMALL U\n1E042         ; mapped                 ; 0444          # 15.0 MODIFIER LETTER CYRILLIC SMALL EF\n1E043         ; mapped                 ; 0445          # 15.0 MODIFIER LETTER CYRILLIC SMALL HA\n1E044         ; mapped                 ; 0446          # 15.0 MODIFIER LETTER CYRILLIC SMALL TSE\n1E045         ; mapped                 ; 0447          # 15.0 MODIFIER LETTER CYRILLIC SMALL CHE\n1E046         ; mapped                 ; 0448          # 15.0 MODIFIER LETTER CYRILLIC SMALL SHA\n1E047         ; mapped                 ; 044B          # 15.0 MODIFIER LETTER CYRILLIC SMALL YERU\n1E048         ; mapped                 ; 044D          # 15.0 MODIFIER LETTER CYRILLIC SMALL E\n1E049         ; mapped                 ; 044E          # 15.0 MODIFIER LETTER CYRILLIC SMALL YU\n1E04A         ; mapped                 ; A689          # 15.0 MODIFIER LETTER CYRILLIC SMALL DZZE\n1E04B         ; mapped                 ; 04D9          # 15.0 MODIFIER LETTER CYRILLIC SMALL SCHWA\n1E04C         ; mapped                 ; 0456          # 15.0 MODIFIER LETTER CYRILLIC SMALL BYELORUSSIAN-UKRAINIAN I\n1E04D         ; mapped                 ; 0458          # 15.0 MODIFIER LETTER CYRILLIC SMALL JE\n1E04E         ; mapped                 ; 04E9          # 15.0 MODIFIER LETTER CYRILLIC SMALL BARRED O\n1E04F         ; mapped                 ; 04AF          # 15.0 MODIFIER LETTER CYRILLIC SMALL STRAIGHT U\n1E050         ; mapped                 ; 04CF          # 15.0 MODIFIER LETTER CYRILLIC SMALL PALOCHKA\n1E051         ; mapped                 ; 0430          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER A\n1E052         ; mapped                 ; 0431          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER BE\n1E053         ; mapped                 ; 0432          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER VE\n1E054         ; mapped                 ; 0433          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER GHE\n1E055         ; mapped                 ; 0434          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER DE\n1E056         ; mapped                 ; 0435          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER IE\n1E057         ; mapped                 ; 0436          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER ZHE\n1E058         ; mapped                 ; 0437          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER ZE\n1E059         ; mapped                 ; 0438          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER I\n1E05A         ; mapped                 ; 043A          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER KA\n1E05B         ; mapped                 ; 043B          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER EL\n1E05C         ; mapped                 ; 043E          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER O\n1E05D         ; mapped                 ; 043F          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER PE\n1E05E         ; mapped                 ; 0441          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER ES\n1E05F         ; mapped                 ; 0443          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER U\n1E060         ; mapped                 ; 0444          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER EF\n1E061         ; mapped                 ; 0445          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER HA\n1E062         ; mapped                 ; 0446          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER TSE\n1E063         ; mapped                 ; 0447          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER CHE\n1E064         ; mapped                 ; 0448          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER SHA\n1E065         ; mapped                 ; 044A          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER HARD SIGN\n1E066         ; mapped                 ; 044B          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER YERU\n1E067         ; mapped                 ; 0491          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER GHE WITH UPTURN\n1E068         ; mapped                 ; 0456          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER BYELORUSSIAN-UKRAINIAN I\n1E069         ; mapped                 ; 0455          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER DZE\n1E06A         ; mapped                 ; 045F          # 15.0 CYRILLIC SUBSCRIPT SMALL LETTER DZHE\n1E06B         ; mapped                 ; 04AB          # 15.0 MODIFIER LETTER CYRILLIC SMALL ES WITH DESCENDER\n1E06C         ; mapped                 ; A651          # 15.0 MODIFIER LETTER CYRILLIC SMALL YERU WITH BACK YER\n1E06D         ; mapped                 ; 04B1          # 15.0 MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE\n1E06E..1E08E  ; disallowed                             # NA   <reserved-1E06E>..<reserved-1E08E>\n1E08F         ; valid                                  # 15.0 COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I\n1E090..1E0FF  ; disallowed                             # NA   <reserved-1E090>..<reserved-1E0FF>\n1E100..1E12C  ; valid                                  # 12.0 NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W\n1E12D..1E12F  ; disallowed                             # NA   <reserved-1E12D>..<reserved-1E12F>\n1E130..1E13D  ; valid                                  # 12.0 NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER\n1E13E..1E13F  ; disallowed                             # NA   <reserved-1E13E>..<reserved-1E13F>\n1E140..1E149  ; valid                                  # 12.0 NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE\n1E14A..1E14D  ; disallowed                             # NA   <reserved-1E14A>..<reserved-1E14D>\n1E14E         ; valid                                  # 12.0 NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ\n1E14F         ; valid                  ;      ; NV8    # 12.0 NYIAKENG PUACHUE HMONG CIRCLED CA\n1E150..1E28F  ; disallowed                             # NA   <reserved-1E150>..<reserved-1E28F>\n1E290..1E2AE  ; valid                                  # 14.0 TOTO LETTER PA..TOTO SIGN RISING TONE\n1E2AF..1E2BF  ; disallowed                             # NA   <reserved-1E2AF>..<reserved-1E2BF>\n1E2C0..1E2F9  ; valid                                  # 12.0 WANCHO LETTER AA..WANCHO DIGIT NINE\n1E2FA..1E2FE  ; disallowed                             # NA   <reserved-1E2FA>..<reserved-1E2FE>\n1E2FF         ; valid                  ;      ; NV8    # 12.0 WANCHO NGUN SIGN\n1E300..1E4CF  ; disallowed                             # NA   <reserved-1E300>..<reserved-1E4CF>\n1E4D0..1E4F9  ; valid                                  # 15.0 NAG MUNDARI LETTER O..NAG MUNDARI DIGIT NINE\n1E4FA..1E7DF  ; disallowed                             # NA   <reserved-1E4FA>..<reserved-1E7DF>\n1E7E0..1E7E6  ; valid                                  # 14.0 ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO\n1E7E7         ; disallowed                             # NA   <reserved-1E7E7>\n1E7E8..1E7EB  ; valid                                  # 14.0 ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE\n1E7EC         ; disallowed                             # NA   <reserved-1E7EC>\n1E7ED..1E7EE  ; valid                                  # 14.0 ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE\n1E7EF         ; disallowed                             # NA   <reserved-1E7EF>\n1E7F0..1E7FE  ; valid                                  # 14.0 ETHIOPIC SYLLABLE GURAGE QWI..ETHIOPIC SYLLABLE GURAGE PWEE\n1E7FF         ; disallowed                             # NA   <reserved-1E7FF>\n1E800..1E8C4  ; valid                                  # 7.0  MENDE KIKAKUI SYLLABLE M001 KI..MENDE KIKAKUI SYLLABLE M060 NYON\n1E8C5..1E8C6  ; disallowed                             # NA   <reserved-1E8C5>..<reserved-1E8C6>\n1E8C7..1E8CF  ; valid                  ;      ; NV8    # 7.0  MENDE KIKAKUI DIGIT ONE..MENDE KIKAKUI DIGIT NINE\n1E8D0..1E8D6  ; valid                                  # 7.0  MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS\n1E8D7..1E8FF  ; disallowed                             # NA   <reserved-1E8D7>..<reserved-1E8FF>\n1E900         ; mapped                 ; 1E922         # 9.0  ADLAM CAPITAL LETTER ALIF\n1E901         ; mapped                 ; 1E923         # 9.0  ADLAM CAPITAL LETTER DAALI\n1E902         ; mapped                 ; 1E924         # 9.0  ADLAM CAPITAL LETTER LAAM\n1E903         ; mapped                 ; 1E925         # 9.0  ADLAM CAPITAL LETTER MIIM\n1E904         ; mapped                 ; 1E926         # 9.0  ADLAM CAPITAL LETTER BA\n1E905         ; mapped                 ; 1E927         # 9.0  ADLAM CAPITAL LETTER SINNYIIYHE\n1E906         ; mapped                 ; 1E928         # 9.0  ADLAM CAPITAL LETTER PE\n1E907         ; mapped                 ; 1E929         # 9.0  ADLAM CAPITAL LETTER BHE\n1E908         ; mapped                 ; 1E92A         # 9.0  ADLAM CAPITAL LETTER RA\n1E909         ; mapped                 ; 1E92B         # 9.0  ADLAM CAPITAL LETTER E\n1E90A         ; mapped                 ; 1E92C         # 9.0  ADLAM CAPITAL LETTER FA\n1E90B         ; mapped                 ; 1E92D         # 9.0  ADLAM CAPITAL LETTER I\n1E90C         ; mapped                 ; 1E92E         # 9.0  ADLAM CAPITAL LETTER O\n1E90D         ; mapped                 ; 1E92F         # 9.0  ADLAM CAPITAL LETTER DHA\n1E90E         ; mapped                 ; 1E930         # 9.0  ADLAM CAPITAL LETTER YHE\n1E90F         ; mapped                 ; 1E931         # 9.0  ADLAM CAPITAL LETTER WAW\n1E910         ; mapped                 ; 1E932         # 9.0  ADLAM CAPITAL LETTER NUN\n1E911         ; mapped                 ; 1E933         # 9.0  ADLAM CAPITAL LETTER KAF\n1E912         ; mapped                 ; 1E934         # 9.0  ADLAM CAPITAL LETTER YA\n1E913         ; mapped                 ; 1E935         # 9.0  ADLAM CAPITAL LETTER U\n1E914         ; mapped                 ; 1E936         # 9.0  ADLAM CAPITAL LETTER JIIM\n1E915         ; mapped                 ; 1E937         # 9.0  ADLAM CAPITAL LETTER CHI\n1E916         ; mapped                 ; 1E938         # 9.0  ADLAM CAPITAL LETTER HA\n1E917         ; mapped                 ; 1E939         # 9.0  ADLAM CAPITAL LETTER QAAF\n1E918         ; mapped                 ; 1E93A         # 9.0  ADLAM CAPITAL LETTER GA\n1E919         ; mapped                 ; 1E93B         # 9.0  ADLAM CAPITAL LETTER NYA\n1E91A         ; mapped                 ; 1E93C         # 9.0  ADLAM CAPITAL LETTER TU\n1E91B         ; mapped                 ; 1E93D         # 9.0  ADLAM CAPITAL LETTER NHA\n1E91C         ; mapped                 ; 1E93E         # 9.0  ADLAM CAPITAL LETTER VA\n1E91D         ; mapped                 ; 1E93F         # 9.0  ADLAM CAPITAL LETTER KHA\n1E91E         ; mapped                 ; 1E940         # 9.0  ADLAM CAPITAL LETTER GBE\n1E91F         ; mapped                 ; 1E941         # 9.0  ADLAM CAPITAL LETTER ZAL\n1E920         ; mapped                 ; 1E942         # 9.0  ADLAM CAPITAL LETTER KPO\n1E921         ; mapped                 ; 1E943         # 9.0  ADLAM CAPITAL LETTER SHA\n1E922..1E94A  ; valid                                  # 9.0  ADLAM SMALL LETTER ALIF..ADLAM NUKTA\n1E94B         ; valid                                  # 12.0 ADLAM NASALIZATION MARK\n1E94C..1E94F  ; disallowed                             # NA   <reserved-1E94C>..<reserved-1E94F>\n1E950..1E959  ; valid                                  # 9.0  ADLAM DIGIT ZERO..ADLAM DIGIT NINE\n1E95A..1E95D  ; disallowed                             # NA   <reserved-1E95A>..<reserved-1E95D>\n1E95E..1E95F  ; valid                  ;      ; NV8    # 9.0  ADLAM INITIAL EXCLAMATION MARK..ADLAM INITIAL QUESTION MARK\n1E960..1EC70  ; disallowed                             # NA   <reserved-1E960>..<reserved-1EC70>\n1EC71..1ECB4  ; valid                  ;      ; NV8    # 11.0 INDIC SIYAQ NUMBER ONE..INDIC SIYAQ ALTERNATE LAKH MARK\n1ECB5..1ED00  ; disallowed                             # NA   <reserved-1ECB5>..<reserved-1ED00>\n1ED01..1ED3D  ; valid                  ;      ; NV8    # 12.0 OTTOMAN SIYAQ NUMBER ONE..OTTOMAN SIYAQ FRACTION ONE SIXTH\n1ED3E..1EDFF  ; disallowed                             # NA   <reserved-1ED3E>..<reserved-1EDFF>\n1EE00         ; mapped                 ; 0627          # 6.1  ARABIC MATHEMATICAL ALEF\n1EE01         ; mapped                 ; 0628          # 6.1  ARABIC MATHEMATICAL BEH\n1EE02         ; mapped                 ; 062C          # 6.1  ARABIC MATHEMATICAL JEEM\n1EE03         ; mapped                 ; 062F          # 6.1  ARABIC MATHEMATICAL DAL\n1EE04         ; disallowed                             # NA   <reserved-1EE04>\n1EE05         ; mapped                 ; 0648          # 6.1  ARABIC MATHEMATICAL WAW\n1EE06         ; mapped                 ; 0632          # 6.1  ARABIC MATHEMATICAL ZAIN\n1EE07         ; mapped                 ; 062D          # 6.1  ARABIC MATHEMATICAL HAH\n1EE08         ; mapped                 ; 0637          # 6.1  ARABIC MATHEMATICAL TAH\n1EE09         ; mapped                 ; 064A          # 6.1  ARABIC MATHEMATICAL YEH\n1EE0A         ; mapped                 ; 0643          # 6.1  ARABIC MATHEMATICAL KAF\n1EE0B         ; mapped                 ; 0644          # 6.1  ARABIC MATHEMATICAL LAM\n1EE0C         ; mapped                 ; 0645          # 6.1  ARABIC MATHEMATICAL MEEM\n1EE0D         ; mapped                 ; 0646          # 6.1  ARABIC MATHEMATICAL NOON\n1EE0E         ; mapped                 ; 0633          # 6.1  ARABIC MATHEMATICAL SEEN\n1EE0F         ; mapped                 ; 0639          # 6.1  ARABIC MATHEMATICAL AIN\n1EE10         ; mapped                 ; 0641          # 6.1  ARABIC MATHEMATICAL FEH\n1EE11         ; mapped                 ; 0635          # 6.1  ARABIC MATHEMATICAL SAD\n1EE12         ; mapped                 ; 0642          # 6.1  ARABIC MATHEMATICAL QAF\n1EE13         ; mapped                 ; 0631          # 6.1  ARABIC MATHEMATICAL REH\n1EE14         ; mapped                 ; 0634          # 6.1  ARABIC MATHEMATICAL SHEEN\n1EE15         ; mapped                 ; 062A          # 6.1  ARABIC MATHEMATICAL TEH\n1EE16         ; mapped                 ; 062B          # 6.1  ARABIC MATHEMATICAL THEH\n1EE17         ; mapped                 ; 062E          # 6.1  ARABIC MATHEMATICAL KHAH\n1EE18         ; mapped                 ; 0630          # 6.1  ARABIC MATHEMATICAL THAL\n1EE19         ; mapped                 ; 0636          # 6.1  ARABIC MATHEMATICAL DAD\n1EE1A         ; mapped                 ; 0638          # 6.1  ARABIC MATHEMATICAL ZAH\n1EE1B         ; mapped                 ; 063A          # 6.1  ARABIC MATHEMATICAL GHAIN\n1EE1C         ; mapped                 ; 066E          # 6.1  ARABIC MATHEMATICAL DOTLESS BEH\n1EE1D         ; mapped                 ; 06BA          # 6.1  ARABIC MATHEMATICAL DOTLESS NOON\n1EE1E         ; mapped                 ; 06A1          # 6.1  ARABIC MATHEMATICAL DOTLESS FEH\n1EE1F         ; mapped                 ; 066F          # 6.1  ARABIC MATHEMATICAL DOTLESS QAF\n1EE20         ; disallowed                             # NA   <reserved-1EE20>\n1EE21         ; mapped                 ; 0628          # 6.1  ARABIC MATHEMATICAL INITIAL BEH\n1EE22         ; mapped                 ; 062C          # 6.1  ARABIC MATHEMATICAL INITIAL JEEM\n1EE23         ; disallowed                             # NA   <reserved-1EE23>\n1EE24         ; mapped                 ; 0647          # 6.1  ARABIC MATHEMATICAL INITIAL HEH\n1EE25..1EE26  ; disallowed                             # NA   <reserved-1EE25>..<reserved-1EE26>\n1EE27         ; mapped                 ; 062D          # 6.1  ARABIC MATHEMATICAL INITIAL HAH\n1EE28         ; disallowed                             # NA   <reserved-1EE28>\n1EE29         ; mapped                 ; 064A          # 6.1  ARABIC MATHEMATICAL INITIAL YEH\n1EE2A         ; mapped                 ; 0643          # 6.1  ARABIC MATHEMATICAL INITIAL KAF\n1EE2B         ; mapped                 ; 0644          # 6.1  ARABIC MATHEMATICAL INITIAL LAM\n1EE2C         ; mapped                 ; 0645          # 6.1  ARABIC MATHEMATICAL INITIAL MEEM\n1EE2D         ; mapped                 ; 0646          # 6.1  ARABIC MATHEMATICAL INITIAL NOON\n1EE2E         ; mapped                 ; 0633          # 6.1  ARABIC MATHEMATICAL INITIAL SEEN\n1EE2F         ; mapped                 ; 0639          # 6.1  ARABIC MATHEMATICAL INITIAL AIN\n1EE30         ; mapped                 ; 0641          # 6.1  ARABIC MATHEMATICAL INITIAL FEH\n1EE31         ; mapped                 ; 0635          # 6.1  ARABIC MATHEMATICAL INITIAL SAD\n1EE32         ; mapped                 ; 0642          # 6.1  ARABIC MATHEMATICAL INITIAL QAF\n1EE33         ; disallowed                             # NA   <reserved-1EE33>\n1EE34         ; mapped                 ; 0634          # 6.1  ARABIC MATHEMATICAL INITIAL SHEEN\n1EE35         ; mapped                 ; 062A          # 6.1  ARABIC MATHEMATICAL INITIAL TEH\n1EE36         ; mapped                 ; 062B          # 6.1  ARABIC MATHEMATICAL INITIAL THEH\n1EE37         ; mapped                 ; 062E          # 6.1  ARABIC MATHEMATICAL INITIAL KHAH\n1EE38         ; disallowed                             # NA   <reserved-1EE38>\n1EE39         ; mapped                 ; 0636          # 6.1  ARABIC MATHEMATICAL INITIAL DAD\n1EE3A         ; disallowed                             # NA   <reserved-1EE3A>\n1EE3B         ; mapped                 ; 063A          # 6.1  ARABIC MATHEMATICAL INITIAL GHAIN\n1EE3C..1EE41  ; disallowed                             # NA   <reserved-1EE3C>..<reserved-1EE41>\n1EE42         ; mapped                 ; 062C          # 6.1  ARABIC MATHEMATICAL TAILED JEEM\n1EE43..1EE46  ; disallowed                             # NA   <reserved-1EE43>..<reserved-1EE46>\n1EE47         ; mapped                 ; 062D          # 6.1  ARABIC MATHEMATICAL TAILED HAH\n1EE48         ; disallowed                             # NA   <reserved-1EE48>\n1EE49         ; mapped                 ; 064A          # 6.1  ARABIC MATHEMATICAL TAILED YEH\n1EE4A         ; disallowed                             # NA   <reserved-1EE4A>\n1EE4B         ; mapped                 ; 0644          # 6.1  ARABIC MATHEMATICAL TAILED LAM\n1EE4C         ; disallowed                             # NA   <reserved-1EE4C>\n1EE4D         ; mapped                 ; 0646          # 6.1  ARABIC MATHEMATICAL TAILED NOON\n1EE4E         ; mapped                 ; 0633          # 6.1  ARABIC MATHEMATICAL TAILED SEEN\n1EE4F         ; mapped                 ; 0639          # 6.1  ARABIC MATHEMATICAL TAILED AIN\n1EE50         ; disallowed                             # NA   <reserved-1EE50>\n1EE51         ; mapped                 ; 0635          # 6.1  ARABIC MATHEMATICAL TAILED SAD\n1EE52         ; mapped                 ; 0642          # 6.1  ARABIC MATHEMATICAL TAILED QAF\n1EE53         ; disallowed                             # NA   <reserved-1EE53>\n1EE54         ; mapped                 ; 0634          # 6.1  ARABIC MATHEMATICAL TAILED SHEEN\n1EE55..1EE56  ; disallowed                             # NA   <reserved-1EE55>..<reserved-1EE56>\n1EE57         ; mapped                 ; 062E          # 6.1  ARABIC MATHEMATICAL TAILED KHAH\n1EE58         ; disallowed                             # NA   <reserved-1EE58>\n1EE59         ; mapped                 ; 0636          # 6.1  ARABIC MATHEMATICAL TAILED DAD\n1EE5A         ; disallowed                             # NA   <reserved-1EE5A>\n1EE5B         ; mapped                 ; 063A          # 6.1  ARABIC MATHEMATICAL TAILED GHAIN\n1EE5C         ; disallowed                             # NA   <reserved-1EE5C>\n1EE5D         ; mapped                 ; 06BA          # 6.1  ARABIC MATHEMATICAL TAILED DOTLESS NOON\n1EE5E         ; disallowed                             # NA   <reserved-1EE5E>\n1EE5F         ; mapped                 ; 066F          # 6.1  ARABIC MATHEMATICAL TAILED DOTLESS QAF\n1EE60         ; disallowed                             # NA   <reserved-1EE60>\n1EE61         ; mapped                 ; 0628          # 6.1  ARABIC MATHEMATICAL STRETCHED BEH\n1EE62         ; mapped                 ; 062C          # 6.1  ARABIC MATHEMATICAL STRETCHED JEEM\n1EE63         ; disallowed                             # NA   <reserved-1EE63>\n1EE64         ; mapped                 ; 0647          # 6.1  ARABIC MATHEMATICAL STRETCHED HEH\n1EE65..1EE66  ; disallowed                             # NA   <reserved-1EE65>..<reserved-1EE66>\n1EE67         ; mapped                 ; 062D          # 6.1  ARABIC MATHEMATICAL STRETCHED HAH\n1EE68         ; mapped                 ; 0637          # 6.1  ARABIC MATHEMATICAL STRETCHED TAH\n1EE69         ; mapped                 ; 064A          # 6.1  ARABIC MATHEMATICAL STRETCHED YEH\n1EE6A         ; mapped                 ; 0643          # 6.1  ARABIC MATHEMATICAL STRETCHED KAF\n1EE6B         ; disallowed                             # NA   <reserved-1EE6B>\n1EE6C         ; mapped                 ; 0645          # 6.1  ARABIC MATHEMATICAL STRETCHED MEEM\n1EE6D         ; mapped                 ; 0646          # 6.1  ARABIC MATHEMATICAL STRETCHED NOON\n1EE6E         ; mapped                 ; 0633          # 6.1  ARABIC MATHEMATICAL STRETCHED SEEN\n1EE6F         ; mapped                 ; 0639          # 6.1  ARABIC MATHEMATICAL STRETCHED AIN\n1EE70         ; mapped                 ; 0641          # 6.1  ARABIC MATHEMATICAL STRETCHED FEH\n1EE71         ; mapped                 ; 0635          # 6.1  ARABIC MATHEMATICAL STRETCHED SAD\n1EE72         ; mapped                 ; 0642          # 6.1  ARABIC MATHEMATICAL STRETCHED QAF\n1EE73         ; disallowed                             # NA   <reserved-1EE73>\n1EE74         ; mapped                 ; 0634          # 6.1  ARABIC MATHEMATICAL STRETCHED SHEEN\n1EE75         ; mapped                 ; 062A          # 6.1  ARABIC MATHEMATICAL STRETCHED TEH\n1EE76         ; mapped                 ; 062B          # 6.1  ARABIC MATHEMATICAL STRETCHED THEH\n1EE77         ; mapped                 ; 062E          # 6.1  ARABIC MATHEMATICAL STRETCHED KHAH\n1EE78         ; disallowed                             # NA   <reserved-1EE78>\n1EE79         ; mapped                 ; 0636          # 6.1  ARABIC MATHEMATICAL STRETCHED DAD\n1EE7A         ; mapped                 ; 0638          # 6.1  ARABIC MATHEMATICAL STRETCHED ZAH\n1EE7B         ; mapped                 ; 063A          # 6.1  ARABIC MATHEMATICAL STRETCHED GHAIN\n1EE7C         ; mapped                 ; 066E          # 6.1  ARABIC MATHEMATICAL STRETCHED DOTLESS BEH\n1EE7D         ; disallowed                             # NA   <reserved-1EE7D>\n1EE7E         ; mapped                 ; 06A1          # 6.1  ARABIC MATHEMATICAL STRETCHED DOTLESS FEH\n1EE7F         ; disallowed                             # NA   <reserved-1EE7F>\n1EE80         ; mapped                 ; 0627          # 6.1  ARABIC MATHEMATICAL LOOPED ALEF\n1EE81         ; mapped                 ; 0628          # 6.1  ARABIC MATHEMATICAL LOOPED BEH\n1EE82         ; mapped                 ; 062C          # 6.1  ARABIC MATHEMATICAL LOOPED JEEM\n1EE83         ; mapped                 ; 062F          # 6.1  ARABIC MATHEMATICAL LOOPED DAL\n1EE84         ; mapped                 ; 0647          # 6.1  ARABIC MATHEMATICAL LOOPED HEH\n1EE85         ; mapped                 ; 0648          # 6.1  ARABIC MATHEMATICAL LOOPED WAW\n1EE86         ; mapped                 ; 0632          # 6.1  ARABIC MATHEMATICAL LOOPED ZAIN\n1EE87         ; mapped                 ; 062D          # 6.1  ARABIC MATHEMATICAL LOOPED HAH\n1EE88         ; mapped                 ; 0637          # 6.1  ARABIC MATHEMATICAL LOOPED TAH\n1EE89         ; mapped                 ; 064A          # 6.1  ARABIC MATHEMATICAL LOOPED YEH\n1EE8A         ; disallowed                             # NA   <reserved-1EE8A>\n1EE8B         ; mapped                 ; 0644          # 6.1  ARABIC MATHEMATICAL LOOPED LAM\n1EE8C         ; mapped                 ; 0645          # 6.1  ARABIC MATHEMATICAL LOOPED MEEM\n1EE8D         ; mapped                 ; 0646          # 6.1  ARABIC MATHEMATICAL LOOPED NOON\n1EE8E         ; mapped                 ; 0633          # 6.1  ARABIC MATHEMATICAL LOOPED SEEN\n1EE8F         ; mapped                 ; 0639          # 6.1  ARABIC MATHEMATICAL LOOPED AIN\n1EE90         ; mapped                 ; 0641          # 6.1  ARABIC MATHEMATICAL LOOPED FEH\n1EE91         ; mapped                 ; 0635          # 6.1  ARABIC MATHEMATICAL LOOPED SAD\n1EE92         ; mapped                 ; 0642          # 6.1  ARABIC MATHEMATICAL LOOPED QAF\n1EE93         ; mapped                 ; 0631          # 6.1  ARABIC MATHEMATICAL LOOPED REH\n1EE94         ; mapped                 ; 0634          # 6.1  ARABIC MATHEMATICAL LOOPED SHEEN\n1EE95         ; mapped                 ; 062A          # 6.1  ARABIC MATHEMATICAL LOOPED TEH\n1EE96         ; mapped                 ; 062B          # 6.1  ARABIC MATHEMATICAL LOOPED THEH\n1EE97         ; mapped                 ; 062E          # 6.1  ARABIC MATHEMATICAL LOOPED KHAH\n1EE98         ; mapped                 ; 0630          # 6.1  ARABIC MATHEMATICAL LOOPED THAL\n1EE99         ; mapped                 ; 0636          # 6.1  ARABIC MATHEMATICAL LOOPED DAD\n1EE9A         ; mapped                 ; 0638          # 6.1  ARABIC MATHEMATICAL LOOPED ZAH\n1EE9B         ; mapped                 ; 063A          # 6.1  ARABIC MATHEMATICAL LOOPED GHAIN\n1EE9C..1EEA0  ; disallowed                             # NA   <reserved-1EE9C>..<reserved-1EEA0>\n1EEA1         ; mapped                 ; 0628          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK BEH\n1EEA2         ; mapped                 ; 062C          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK JEEM\n1EEA3         ; mapped                 ; 062F          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK DAL\n1EEA4         ; disallowed                             # NA   <reserved-1EEA4>\n1EEA5         ; mapped                 ; 0648          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK WAW\n1EEA6         ; mapped                 ; 0632          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK ZAIN\n1EEA7         ; mapped                 ; 062D          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK HAH\n1EEA8         ; mapped                 ; 0637          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK TAH\n1EEA9         ; mapped                 ; 064A          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK YEH\n1EEAA         ; disallowed                             # NA   <reserved-1EEAA>\n1EEAB         ; mapped                 ; 0644          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK LAM\n1EEAC         ; mapped                 ; 0645          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK MEEM\n1EEAD         ; mapped                 ; 0646          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK NOON\n1EEAE         ; mapped                 ; 0633          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK SEEN\n1EEAF         ; mapped                 ; 0639          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK AIN\n1EEB0         ; mapped                 ; 0641          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK FEH\n1EEB1         ; mapped                 ; 0635          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK SAD\n1EEB2         ; mapped                 ; 0642          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK QAF\n1EEB3         ; mapped                 ; 0631          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK REH\n1EEB4         ; mapped                 ; 0634          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK SHEEN\n1EEB5         ; mapped                 ; 062A          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK TEH\n1EEB6         ; mapped                 ; 062B          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK THEH\n1EEB7         ; mapped                 ; 062E          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK KHAH\n1EEB8         ; mapped                 ; 0630          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK THAL\n1EEB9         ; mapped                 ; 0636          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK DAD\n1EEBA         ; mapped                 ; 0638          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK ZAH\n1EEBB         ; mapped                 ; 063A          # 6.1  ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN\n1EEBC..1EEEF  ; disallowed                             # NA   <reserved-1EEBC>..<reserved-1EEEF>\n1EEF0..1EEF1  ; valid                  ;      ; NV8    # 6.1  ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL\n1EEF2..1EFFF  ; disallowed                             # NA   <reserved-1EEF2>..<reserved-1EFFF>\n1F000..1F02B  ; valid                  ;      ; NV8    # 5.1  MAHJONG TILE EAST WIND..MAHJONG TILE BACK\n1F02C..1F02F  ; disallowed                             # NA   <reserved-1F02C>..<reserved-1F02F>\n1F030..1F093  ; valid                  ;      ; NV8    # 5.1  DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06\n1F094..1F09F  ; disallowed                             # NA   <reserved-1F094>..<reserved-1F09F>\n1F0A0..1F0AE  ; valid                  ;      ; NV8    # 6.0  PLAYING CARD BACK..PLAYING CARD KING OF SPADES\n1F0AF..1F0B0  ; disallowed                             # NA   <reserved-1F0AF>..<reserved-1F0B0>\n1F0B1..1F0BE  ; valid                  ;      ; NV8    # 6.0  PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS\n1F0BF         ; valid                  ;      ; NV8    # 7.0  PLAYING CARD RED JOKER\n1F0C0         ; disallowed                             # NA   <reserved-1F0C0>\n1F0C1..1F0CF  ; valid                  ;      ; NV8    # 6.0  PLAYING CARD ACE OF DIAMONDS..PLAYING CARD BLACK JOKER\n1F0D0         ; disallowed                             # NA   <reserved-1F0D0>\n1F0D1..1F0DF  ; valid                  ;      ; NV8    # 6.0  PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER\n1F0E0..1F0F5  ; valid                  ;      ; NV8    # 7.0  PLAYING CARD FOOL..PLAYING CARD TRUMP-21\n1F0F6..1F0FF  ; disallowed                             # NA   <reserved-1F0F6>..<reserved-1F0FF>\n1F100         ; disallowed                             # 5.2  DIGIT ZERO FULL STOP\n1F101         ; disallowed_STD3_mapped ; 0030 002C     # 5.2  DIGIT ZERO COMMA\n1F102         ; disallowed_STD3_mapped ; 0031 002C     # 5.2  DIGIT ONE COMMA\n1F103         ; disallowed_STD3_mapped ; 0032 002C     # 5.2  DIGIT TWO COMMA\n1F104         ; disallowed_STD3_mapped ; 0033 002C     # 5.2  DIGIT THREE COMMA\n1F105         ; disallowed_STD3_mapped ; 0034 002C     # 5.2  DIGIT FOUR COMMA\n1F106         ; disallowed_STD3_mapped ; 0035 002C     # 5.2  DIGIT FIVE COMMA\n1F107         ; disallowed_STD3_mapped ; 0036 002C     # 5.2  DIGIT SIX COMMA\n1F108         ; disallowed_STD3_mapped ; 0037 002C     # 5.2  DIGIT SEVEN COMMA\n1F109         ; disallowed_STD3_mapped ; 0038 002C     # 5.2  DIGIT EIGHT COMMA\n1F10A         ; disallowed_STD3_mapped ; 0039 002C     # 5.2  DIGIT NINE COMMA\n1F10B..1F10C  ; valid                  ;      ; NV8    # 7.0  DINGBAT CIRCLED SANS-SERIF DIGIT ZERO..DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO\n1F10D..1F10F  ; valid                  ;      ; NV8    # 13.0 CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH\n1F110         ; disallowed_STD3_mapped ; 0028 0061 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER A\n1F111         ; disallowed_STD3_mapped ; 0028 0062 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER B\n1F112         ; disallowed_STD3_mapped ; 0028 0063 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER C\n1F113         ; disallowed_STD3_mapped ; 0028 0064 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER D\n1F114         ; disallowed_STD3_mapped ; 0028 0065 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER E\n1F115         ; disallowed_STD3_mapped ; 0028 0066 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER F\n1F116         ; disallowed_STD3_mapped ; 0028 0067 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER G\n1F117         ; disallowed_STD3_mapped ; 0028 0068 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER H\n1F118         ; disallowed_STD3_mapped ; 0028 0069 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER I\n1F119         ; disallowed_STD3_mapped ; 0028 006A 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER J\n1F11A         ; disallowed_STD3_mapped ; 0028 006B 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER K\n1F11B         ; disallowed_STD3_mapped ; 0028 006C 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER L\n1F11C         ; disallowed_STD3_mapped ; 0028 006D 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER M\n1F11D         ; disallowed_STD3_mapped ; 0028 006E 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER N\n1F11E         ; disallowed_STD3_mapped ; 0028 006F 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER O\n1F11F         ; disallowed_STD3_mapped ; 0028 0070 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER P\n1F120         ; disallowed_STD3_mapped ; 0028 0071 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER Q\n1F121         ; disallowed_STD3_mapped ; 0028 0072 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER R\n1F122         ; disallowed_STD3_mapped ; 0028 0073 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER S\n1F123         ; disallowed_STD3_mapped ; 0028 0074 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER T\n1F124         ; disallowed_STD3_mapped ; 0028 0075 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER U\n1F125         ; disallowed_STD3_mapped ; 0028 0076 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER V\n1F126         ; disallowed_STD3_mapped ; 0028 0077 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER W\n1F127         ; disallowed_STD3_mapped ; 0028 0078 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER X\n1F128         ; disallowed_STD3_mapped ; 0028 0079 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER Y\n1F129         ; disallowed_STD3_mapped ; 0028 007A 0029 #5.2  PARENTHESIZED LATIN CAPITAL LETTER Z\n1F12A         ; mapped                 ; 3014 0073 3015 #5.2  TORTOISE SHELL BRACKETED LATIN CAPITAL LETTER S\n1F12B         ; mapped                 ; 0063          # 5.2  CIRCLED ITALIC LATIN CAPITAL LETTER C\n1F12C         ; mapped                 ; 0072          # 5.2  CIRCLED ITALIC LATIN CAPITAL LETTER R\n1F12D         ; mapped                 ; 0063 0064     # 5.2  CIRCLED CD\n1F12E         ; mapped                 ; 0077 007A     # 5.2  CIRCLED WZ\n1F12F         ; valid                  ;      ; NV8    # 11.0 COPYLEFT SYMBOL\n1F130         ; mapped                 ; 0061          # 6.0  SQUARED LATIN CAPITAL LETTER A\n1F131         ; mapped                 ; 0062          # 5.2  SQUARED LATIN CAPITAL LETTER B\n1F132         ; mapped                 ; 0063          # 6.0  SQUARED LATIN CAPITAL LETTER C\n1F133         ; mapped                 ; 0064          # 6.0  SQUARED LATIN CAPITAL LETTER D\n1F134         ; mapped                 ; 0065          # 6.0  SQUARED LATIN CAPITAL LETTER E\n1F135         ; mapped                 ; 0066          # 6.0  SQUARED LATIN CAPITAL LETTER F\n1F136         ; mapped                 ; 0067          # 6.0  SQUARED LATIN CAPITAL LETTER G\n1F137         ; mapped                 ; 0068          # 6.0  SQUARED LATIN CAPITAL LETTER H\n1F138         ; mapped                 ; 0069          # 6.0  SQUARED LATIN CAPITAL LETTER I\n1F139         ; mapped                 ; 006A          # 6.0  SQUARED LATIN CAPITAL LETTER J\n1F13A         ; mapped                 ; 006B          # 6.0  SQUARED LATIN CAPITAL LETTER K\n1F13B         ; mapped                 ; 006C          # 6.0  SQUARED LATIN CAPITAL LETTER L\n1F13C         ; mapped                 ; 006D          # 6.0  SQUARED LATIN CAPITAL LETTER M\n1F13D         ; mapped                 ; 006E          # 5.2  SQUARED LATIN CAPITAL LETTER N\n1F13E         ; mapped                 ; 006F          # 6.0  SQUARED LATIN CAPITAL LETTER O\n1F13F         ; mapped                 ; 0070          # 5.2  SQUARED LATIN CAPITAL LETTER P\n1F140         ; mapped                 ; 0071          # 6.0  SQUARED LATIN CAPITAL LETTER Q\n1F141         ; mapped                 ; 0072          # 6.0  SQUARED LATIN CAPITAL LETTER R\n1F142         ; mapped                 ; 0073          # 5.2  SQUARED LATIN CAPITAL LETTER S\n1F143         ; mapped                 ; 0074          # 6.0  SQUARED LATIN CAPITAL LETTER T\n1F144         ; mapped                 ; 0075          # 6.0  SQUARED LATIN CAPITAL LETTER U\n1F145         ; mapped                 ; 0076          # 6.0  SQUARED LATIN CAPITAL LETTER V\n1F146         ; mapped                 ; 0077          # 5.2  SQUARED LATIN CAPITAL LETTER W\n1F147         ; mapped                 ; 0078          # 6.0  SQUARED LATIN CAPITAL LETTER X\n1F148         ; mapped                 ; 0079          # 6.0  SQUARED LATIN CAPITAL LETTER Y\n1F149         ; mapped                 ; 007A          # 6.0  SQUARED LATIN CAPITAL LETTER Z\n1F14A         ; mapped                 ; 0068 0076     # 5.2  SQUARED HV\n1F14B         ; mapped                 ; 006D 0076     # 5.2  SQUARED MV\n1F14C         ; mapped                 ; 0073 0064     # 5.2  SQUARED SD\n1F14D         ; mapped                 ; 0073 0073     # 5.2  SQUARED SS\n1F14E         ; mapped                 ; 0070 0070 0076 #5.2  SQUARED PPV\n1F14F         ; mapped                 ; 0077 0063     # 6.0  SQUARED WC\n1F150..1F156  ; valid                  ;      ; NV8    # 6.0  NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER G\n1F157         ; valid                  ;      ; NV8    # 5.2  NEGATIVE CIRCLED LATIN CAPITAL LETTER H\n1F158..1F15E  ; valid                  ;      ; NV8    # 6.0  NEGATIVE CIRCLED LATIN CAPITAL LETTER I..NEGATIVE CIRCLED LATIN CAPITAL LETTER O\n1F15F         ; valid                  ;      ; NV8    # 5.2  NEGATIVE CIRCLED LATIN CAPITAL LETTER P\n1F160..1F169  ; valid                  ;      ; NV8    # 6.0  NEGATIVE CIRCLED LATIN CAPITAL LETTER Q..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z\n1F16A         ; mapped                 ; 006D 0063     # 6.1  RAISED MC SIGN\n1F16B         ; mapped                 ; 006D 0064     # 6.1  RAISED MD SIGN\n1F16C         ; mapped                 ; 006D 0072     # 12.0 RAISED MR SIGN\n1F16D..1F16F  ; valid                  ;      ; NV8    # 13.0 CIRCLED CC..CIRCLED HUMAN FIGURE\n1F170..1F178  ; valid                  ;      ; NV8    # 6.0  NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER I\n1F179         ; valid                  ;      ; NV8    # 5.2  NEGATIVE SQUARED LATIN CAPITAL LETTER J\n1F17A         ; valid                  ;      ; NV8    # 6.0  NEGATIVE SQUARED LATIN CAPITAL LETTER K\n1F17B..1F17C  ; valid                  ;      ; NV8    # 5.2  NEGATIVE SQUARED LATIN CAPITAL LETTER L..NEGATIVE SQUARED LATIN CAPITAL LETTER M\n1F17D..1F17E  ; valid                  ;      ; NV8    # 6.0  NEGATIVE SQUARED LATIN CAPITAL LETTER N..NEGATIVE SQUARED LATIN CAPITAL LETTER O\n1F17F         ; valid                  ;      ; NV8    # 5.2  NEGATIVE SQUARED LATIN CAPITAL LETTER P\n1F180..1F189  ; valid                  ;      ; NV8    # 6.0  NEGATIVE SQUARED LATIN CAPITAL LETTER Q..NEGATIVE SQUARED LATIN CAPITAL LETTER Z\n1F18A..1F18D  ; valid                  ;      ; NV8    # 5.2  CROSSED NEGATIVE SQUARED LATIN CAPITAL LETTER P..NEGATIVE SQUARED SA\n1F18E..1F18F  ; valid                  ;      ; NV8    # 6.0  NEGATIVE SQUARED AB..NEGATIVE SQUARED WC\n1F190         ; mapped                 ; 0064 006A     # 5.2  SQUARE DJ\n1F191..1F19A  ; valid                  ;      ; NV8    # 6.0  SQUARED CL..SQUARED VS\n1F19B..1F1AC  ; valid                  ;      ; NV8    # 9.0  SQUARED THREE D..SQUARED VOD\n1F1AD         ; valid                  ;      ; NV8    # 13.0 MASK WORK SYMBOL\n1F1AE..1F1E5  ; disallowed                             # NA   <reserved-1F1AE>..<reserved-1F1E5>\n1F1E6..1F1FF  ; valid                  ;      ; NV8    # 6.0  REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z\n1F200         ; mapped                 ; 307B 304B     # 5.2  SQUARE HIRAGANA HOKA\n1F201         ; mapped                 ; 30B3 30B3     # 6.0  SQUARED KATAKANA KOKO\n1F202         ; mapped                 ; 30B5          # 6.0  SQUARED KATAKANA SA\n1F203..1F20F  ; disallowed                             # NA   <reserved-1F203>..<reserved-1F20F>\n1F210         ; mapped                 ; 624B          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-624B\n1F211         ; mapped                 ; 5B57          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-5B57\n1F212         ; mapped                 ; 53CC          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-53CC\n1F213         ; mapped                 ; 30C7          # 5.2  SQUARED KATAKANA DE\n1F214         ; mapped                 ; 4E8C          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-4E8C\n1F215         ; mapped                 ; 591A          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-591A\n1F216         ; mapped                 ; 89E3          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-89E3\n1F217         ; mapped                 ; 5929          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-5929\n1F218         ; mapped                 ; 4EA4          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-4EA4\n1F219         ; mapped                 ; 6620          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-6620\n1F21A         ; mapped                 ; 7121          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-7121\n1F21B         ; mapped                 ; 6599          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-6599\n1F21C         ; mapped                 ; 524D          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-524D\n1F21D         ; mapped                 ; 5F8C          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-5F8C\n1F21E         ; mapped                 ; 518D          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-518D\n1F21F         ; mapped                 ; 65B0          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-65B0\n1F220         ; mapped                 ; 521D          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-521D\n1F221         ; mapped                 ; 7D42          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-7D42\n1F222         ; mapped                 ; 751F          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-751F\n1F223         ; mapped                 ; 8CA9          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-8CA9\n1F224         ; mapped                 ; 58F0          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-58F0\n1F225         ; mapped                 ; 5439          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-5439\n1F226         ; mapped                 ; 6F14          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-6F14\n1F227         ; mapped                 ; 6295          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-6295\n1F228         ; mapped                 ; 6355          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-6355\n1F229         ; mapped                 ; 4E00          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-4E00\n1F22A         ; mapped                 ; 4E09          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-4E09\n1F22B         ; mapped                 ; 904A          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-904A\n1F22C         ; mapped                 ; 5DE6          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-5DE6\n1F22D         ; mapped                 ; 4E2D          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-4E2D\n1F22E         ; mapped                 ; 53F3          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-53F3\n1F22F         ; mapped                 ; 6307          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-6307\n1F230         ; mapped                 ; 8D70          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-8D70\n1F231         ; mapped                 ; 6253          # 5.2  SQUARED CJK UNIFIED IDEOGRAPH-6253\n1F232         ; mapped                 ; 7981          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-7981\n1F233         ; mapped                 ; 7A7A          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-7A7A\n1F234         ; mapped                 ; 5408          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-5408\n1F235         ; mapped                 ; 6E80          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-6E80\n1F236         ; mapped                 ; 6709          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-6709\n1F237         ; mapped                 ; 6708          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-6708\n1F238         ; mapped                 ; 7533          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-7533\n1F239         ; mapped                 ; 5272          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-5272\n1F23A         ; mapped                 ; 55B6          # 6.0  SQUARED CJK UNIFIED IDEOGRAPH-55B6\n1F23B         ; mapped                 ; 914D          # 9.0  SQUARED CJK UNIFIED IDEOGRAPH-914D\n1F23C..1F23F  ; disallowed                             # NA   <reserved-1F23C>..<reserved-1F23F>\n1F240         ; mapped                 ; 3014 672C 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C\n1F241         ; mapped                 ; 3014 4E09 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-4E09\n1F242         ; mapped                 ; 3014 4E8C 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-4E8C\n1F243         ; mapped                 ; 3014 5B89 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-5B89\n1F244         ; mapped                 ; 3014 70B9 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-70B9\n1F245         ; mapped                 ; 3014 6253 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6253\n1F246         ; mapped                 ; 3014 76D7 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-76D7\n1F247         ; mapped                 ; 3014 52DD 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-52DD\n1F248         ; mapped                 ; 3014 6557 3015 #5.2  TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557\n1F249..1F24F  ; disallowed                             # NA   <reserved-1F249>..<reserved-1F24F>\n1F250         ; mapped                 ; 5F97          # 6.0  CIRCLED IDEOGRAPH ADVANTAGE\n1F251         ; mapped                 ; 53EF          # 6.0  CIRCLED IDEOGRAPH ACCEPT\n1F252..1F25F  ; disallowed                             # NA   <reserved-1F252>..<reserved-1F25F>\n1F260..1F265  ; valid                  ;      ; NV8    # 10.0 ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI\n1F266..1F2FF  ; disallowed                             # NA   <reserved-1F266>..<reserved-1F2FF>\n1F300..1F320  ; valid                  ;      ; NV8    # 6.0  CYCLONE..SHOOTING STAR\n1F321..1F32C  ; valid                  ;      ; NV8    # 7.0  THERMOMETER..WIND BLOWING FACE\n1F32D..1F32F  ; valid                  ;      ; NV8    # 8.0  HOT DOG..BURRITO\n1F330..1F335  ; valid                  ;      ; NV8    # 6.0  CHESTNUT..CACTUS\n1F336         ; valid                  ;      ; NV8    # 7.0  HOT PEPPER\n1F337..1F37C  ; valid                  ;      ; NV8    # 6.0  TULIP..BABY BOTTLE\n1F37D         ; valid                  ;      ; NV8    # 7.0  FORK AND KNIFE WITH PLATE\n1F37E..1F37F  ; valid                  ;      ; NV8    # 8.0  BOTTLE WITH POPPING CORK..POPCORN\n1F380..1F393  ; valid                  ;      ; NV8    # 6.0  RIBBON..GRADUATION CAP\n1F394..1F39F  ; valid                  ;      ; NV8    # 7.0  HEART WITH TIP ON THE LEFT..ADMISSION TICKETS\n1F3A0..1F3C4  ; valid                  ;      ; NV8    # 6.0  CAROUSEL HORSE..SURFER\n1F3C5         ; valid                  ;      ; NV8    # 7.0  SPORTS MEDAL\n1F3C6..1F3CA  ; valid                  ;      ; NV8    # 6.0  TROPHY..SWIMMER\n1F3CB..1F3CE  ; valid                  ;      ; NV8    # 7.0  WEIGHT LIFTER..RACING CAR\n1F3CF..1F3D3  ; valid                  ;      ; NV8    # 8.0  CRICKET BAT AND BALL..TABLE TENNIS PADDLE AND BALL\n1F3D4..1F3DF  ; valid                  ;      ; NV8    # 7.0  SNOW CAPPED MOUNTAIN..STADIUM\n1F3E0..1F3F0  ; valid                  ;      ; NV8    # 6.0  HOUSE BUILDING..EUROPEAN CASTLE\n1F3F1..1F3F7  ; valid                  ;      ; NV8    # 7.0  WHITE PENNANT..LABEL\n1F3F8..1F3FF  ; valid                  ;      ; NV8    # 8.0  BADMINTON RACQUET AND SHUTTLECOCK..EMOJI MODIFIER FITZPATRICK TYPE-6\n1F400..1F43E  ; valid                  ;      ; NV8    # 6.0  RAT..PAW PRINTS\n1F43F         ; valid                  ;      ; NV8    # 7.0  CHIPMUNK\n1F440         ; valid                  ;      ; NV8    # 6.0  EYES\n1F441         ; valid                  ;      ; NV8    # 7.0  EYE\n1F442..1F4F7  ; valid                  ;      ; NV8    # 6.0  EAR..CAMERA\n1F4F8         ; valid                  ;      ; NV8    # 7.0  CAMERA WITH FLASH\n1F4F9..1F4FC  ; valid                  ;      ; NV8    # 6.0  VIDEO CAMERA..VIDEOCASSETTE\n1F4FD..1F4FE  ; valid                  ;      ; NV8    # 7.0  FILM PROJECTOR..PORTABLE STEREO\n1F4FF         ; valid                  ;      ; NV8    # 8.0  PRAYER BEADS\n1F500..1F53D  ; valid                  ;      ; NV8    # 6.0  TWISTED RIGHTWARDS ARROWS..DOWN-POINTING SMALL RED TRIANGLE\n1F53E..1F53F  ; valid                  ;      ; NV8    # 7.0  LOWER RIGHT SHADOWED WHITE CIRCLE..UPPER RIGHT SHADOWED WHITE CIRCLE\n1F540..1F543  ; valid                  ;      ; NV8    # 6.1  CIRCLED CROSS POMMEE..NOTCHED LEFT SEMICIRCLE WITH THREE DOTS\n1F544..1F54A  ; valid                  ;      ; NV8    # 7.0  NOTCHED RIGHT SEMICIRCLE WITH THREE DOTS..DOVE OF PEACE\n1F54B..1F54F  ; valid                  ;      ; NV8    # 8.0  KAABA..BOWL OF HYGIEIA\n1F550..1F567  ; valid                  ;      ; NV8    # 6.0  CLOCK FACE ONE OCLOCK..CLOCK FACE TWELVE-THIRTY\n1F568..1F579  ; valid                  ;      ; NV8    # 7.0  RIGHT SPEAKER..JOYSTICK\n1F57A         ; valid                  ;      ; NV8    # 9.0  MAN DANCING\n1F57B..1F5A3  ; valid                  ;      ; NV8    # 7.0  LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX\n1F5A4         ; valid                  ;      ; NV8    # 9.0  BLACK HEART\n1F5A5..1F5FA  ; valid                  ;      ; NV8    # 7.0  DESKTOP COMPUTER..WORLD MAP\n1F5FB..1F5FF  ; valid                  ;      ; NV8    # 6.0  MOUNT FUJI..MOYAI\n1F600         ; valid                  ;      ; NV8    # 6.1  GRINNING FACE\n1F601..1F610  ; valid                  ;      ; NV8    # 6.0  GRINNING FACE WITH SMILING EYES..NEUTRAL FACE\n1F611         ; valid                  ;      ; NV8    # 6.1  EXPRESSIONLESS FACE\n1F612..1F614  ; valid                  ;      ; NV8    # 6.0  UNAMUSED FACE..PENSIVE FACE\n1F615         ; valid                  ;      ; NV8    # 6.1  CONFUSED FACE\n1F616         ; valid                  ;      ; NV8    # 6.0  CONFOUNDED FACE\n1F617         ; valid                  ;      ; NV8    # 6.1  KISSING FACE\n1F618         ; valid                  ;      ; NV8    # 6.0  FACE THROWING A KISS\n1F619         ; valid                  ;      ; NV8    # 6.1  KISSING FACE WITH SMILING EYES\n1F61A         ; valid                  ;      ; NV8    # 6.0  KISSING FACE WITH CLOSED EYES\n1F61B         ; valid                  ;      ; NV8    # 6.1  FACE WITH STUCK-OUT TONGUE\n1F61C..1F61E  ; valid                  ;      ; NV8    # 6.0  FACE WITH STUCK-OUT TONGUE AND WINKING EYE..DISAPPOINTED FACE\n1F61F         ; valid                  ;      ; NV8    # 6.1  WORRIED FACE\n1F620..1F625  ; valid                  ;      ; NV8    # 6.0  ANGRY FACE..DISAPPOINTED BUT RELIEVED FACE\n1F626..1F627  ; valid                  ;      ; NV8    # 6.1  FROWNING FACE WITH OPEN MOUTH..ANGUISHED FACE\n1F628..1F62B  ; valid                  ;      ; NV8    # 6.0  FEARFUL FACE..TIRED FACE\n1F62C         ; valid                  ;      ; NV8    # 6.1  GRIMACING FACE\n1F62D         ; valid                  ;      ; NV8    # 6.0  LOUDLY CRYING FACE\n1F62E..1F62F  ; valid                  ;      ; NV8    # 6.1  FACE WITH OPEN MOUTH..HUSHED FACE\n1F630..1F633  ; valid                  ;      ; NV8    # 6.0  FACE WITH OPEN MOUTH AND COLD SWEAT..FLUSHED FACE\n1F634         ; valid                  ;      ; NV8    # 6.1  SLEEPING FACE\n1F635..1F640  ; valid                  ;      ; NV8    # 6.0  DIZZY FACE..WEARY CAT FACE\n1F641..1F642  ; valid                  ;      ; NV8    # 7.0  SLIGHTLY FROWNING FACE..SLIGHTLY SMILING FACE\n1F643..1F644  ; valid                  ;      ; NV8    # 8.0  UPSIDE-DOWN FACE..FACE WITH ROLLING EYES\n1F645..1F64F  ; valid                  ;      ; NV8    # 6.0  FACE WITH NO GOOD GESTURE..PERSON WITH FOLDED HANDS\n1F650..1F67F  ; valid                  ;      ; NV8    # 7.0  NORTH WEST POINTING LEAF..REVERSE CHECKER BOARD\n1F680..1F6C5  ; valid                  ;      ; NV8    # 6.0  ROCKET..LEFT LUGGAGE\n1F6C6..1F6CF  ; valid                  ;      ; NV8    # 7.0  TRIANGLE WITH ROUNDED CORNERS..BED\n1F6D0         ; valid                  ;      ; NV8    # 8.0  PLACE OF WORSHIP\n1F6D1..1F6D2  ; valid                  ;      ; NV8    # 9.0  OCTAGONAL SIGN..SHOPPING TROLLEY\n1F6D3..1F6D4  ; valid                  ;      ; NV8    # 10.0 STUPA..PAGODA\n1F6D5         ; valid                  ;      ; NV8    # 12.0 HINDU TEMPLE\n1F6D6..1F6D7  ; valid                  ;      ; NV8    # 13.0 HUT..ELEVATOR\n1F6D8..1F6DB  ; disallowed                             # NA   <reserved-1F6D8>..<reserved-1F6DB>\n1F6DC         ; valid                  ;      ; NV8    # 15.0 WIRELESS\n1F6DD..1F6DF  ; valid                  ;      ; NV8    # 14.0 PLAYGROUND SLIDE..RING BUOY\n1F6E0..1F6EC  ; valid                  ;      ; NV8    # 7.0  HAMMER AND WRENCH..AIRPLANE ARRIVING\n1F6ED..1F6EF  ; disallowed                             # NA   <reserved-1F6ED>..<reserved-1F6EF>\n1F6F0..1F6F3  ; valid                  ;      ; NV8    # 7.0  SATELLITE..PASSENGER SHIP\n1F6F4..1F6F6  ; valid                  ;      ; NV8    # 9.0  SCOOTER..CANOE\n1F6F7..1F6F8  ; valid                  ;      ; NV8    # 10.0 SLED..FLYING SAUCER\n1F6F9         ; valid                  ;      ; NV8    # 11.0 SKATEBOARD\n1F6FA         ; valid                  ;      ; NV8    # 12.0 AUTO RICKSHAW\n1F6FB..1F6FC  ; valid                  ;      ; NV8    # 13.0 PICKUP TRUCK..ROLLER SKATE\n1F6FD..1F6FF  ; disallowed                             # NA   <reserved-1F6FD>..<reserved-1F6FF>\n1F700..1F773  ; valid                  ;      ; NV8    # 6.0  ALCHEMICAL SYMBOL FOR QUINTESSENCE..ALCHEMICAL SYMBOL FOR HALF OUNCE\n1F774..1F776  ; valid                  ;      ; NV8    # 15.0 LOT OF FORTUNE..LUNAR ECLIPSE\n1F777..1F77A  ; disallowed                             # NA   <reserved-1F777>..<reserved-1F77A>\n1F77B..1F77F  ; valid                  ;      ; NV8    # 15.0 HAUMEA..ORCUS\n1F780..1F7D4  ; valid                  ;      ; NV8    # 7.0  BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..HEAVY TWELVE POINTED PINWHEEL STAR\n1F7D5..1F7D8  ; valid                  ;      ; NV8    # 11.0 CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE\n1F7D9         ; valid                  ;      ; NV8    # 15.0 NINE POINTED WHITE STAR\n1F7DA..1F7DF  ; disallowed                             # NA   <reserved-1F7DA>..<reserved-1F7DF>\n1F7E0..1F7EB  ; valid                  ;      ; NV8    # 12.0 LARGE ORANGE CIRCLE..LARGE BROWN SQUARE\n1F7EC..1F7EF  ; disallowed                             # NA   <reserved-1F7EC>..<reserved-1F7EF>\n1F7F0         ; valid                  ;      ; NV8    # 14.0 HEAVY EQUALS SIGN\n1F7F1..1F7FF  ; disallowed                             # NA   <reserved-1F7F1>..<reserved-1F7FF>\n1F800..1F80B  ; valid                  ;      ; NV8    # 7.0  LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD\n1F80C..1F80F  ; disallowed                             # NA   <reserved-1F80C>..<reserved-1F80F>\n1F810..1F847  ; valid                  ;      ; NV8    # 7.0  LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW\n1F848..1F84F  ; disallowed                             # NA   <reserved-1F848>..<reserved-1F84F>\n1F850..1F859  ; valid                  ;      ; NV8    # 7.0  LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW\n1F85A..1F85F  ; disallowed                             # NA   <reserved-1F85A>..<reserved-1F85F>\n1F860..1F887  ; valid                  ;      ; NV8    # 7.0  WIDE-HEADED LEFTWARDS LIGHT BARB ARROW..WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW\n1F888..1F88F  ; disallowed                             # NA   <reserved-1F888>..<reserved-1F88F>\n1F890..1F8AD  ; valid                  ;      ; NV8    # 7.0  LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS\n1F8AE..1F8AF  ; disallowed                             # NA   <reserved-1F8AE>..<reserved-1F8AF>\n1F8B0..1F8B1  ; valid                  ;      ; NV8    # 13.0 ARROW POINTING UPWARDS THEN NORTH WEST..ARROW POINTING RIGHTWARDS THEN CURVING SOUTH WEST\n1F8B2..1F8FF  ; disallowed                             # NA   <reserved-1F8B2>..<reserved-1F8FF>\n1F900..1F90B  ; valid                  ;      ; NV8    # 10.0 CIRCLED CROSS FORMEE WITH FOUR DOTS..DOWNWARD FACING NOTCHED HOOK WITH DOT\n1F90C         ; valid                  ;      ; NV8    # 13.0 PINCHED FINGERS\n1F90D..1F90F  ; valid                  ;      ; NV8    # 12.0 WHITE HEART..PINCHING HAND\n1F910..1F918  ; valid                  ;      ; NV8    # 8.0  ZIPPER-MOUTH FACE..SIGN OF THE HORNS\n1F919..1F91E  ; valid                  ;      ; NV8    # 9.0  CALL ME HAND..HAND WITH INDEX AND MIDDLE FINGERS CROSSED\n1F91F         ; valid                  ;      ; NV8    # 10.0 I LOVE YOU HAND SIGN\n1F920..1F927  ; valid                  ;      ; NV8    # 9.0  FACE WITH COWBOY HAT..SNEEZING FACE\n1F928..1F92F  ; valid                  ;      ; NV8    # 10.0 FACE WITH ONE EYEBROW RAISED..SHOCKED FACE WITH EXPLODING HEAD\n1F930         ; valid                  ;      ; NV8    # 9.0  PREGNANT WOMAN\n1F931..1F932  ; valid                  ;      ; NV8    # 10.0 BREAST-FEEDING..PALMS UP TOGETHER\n1F933..1F93E  ; valid                  ;      ; NV8    # 9.0  SELFIE..HANDBALL\n1F93F         ; valid                  ;      ; NV8    # 12.0 DIVING MASK\n1F940..1F94B  ; valid                  ;      ; NV8    # 9.0  WILTED FLOWER..MARTIAL ARTS UNIFORM\n1F94C         ; valid                  ;      ; NV8    # 10.0 CURLING STONE\n1F94D..1F94F  ; valid                  ;      ; NV8    # 11.0 LACROSSE STICK AND BALL..FLYING DISC\n1F950..1F95E  ; valid                  ;      ; NV8    # 9.0  CROISSANT..PANCAKES\n1F95F..1F96B  ; valid                  ;      ; NV8    # 10.0 DUMPLING..CANNED FOOD\n1F96C..1F970  ; valid                  ;      ; NV8    # 11.0 LEAFY GREEN..SMILING FACE WITH SMILING EYES AND THREE HEARTS\n1F971         ; valid                  ;      ; NV8    # 12.0 YAWNING FACE\n1F972         ; valid                  ;      ; NV8    # 13.0 SMILING FACE WITH TEAR\n1F973..1F976  ; valid                  ;      ; NV8    # 11.0 FACE WITH PARTY HORN AND PARTY HAT..FREEZING FACE\n1F977..1F978  ; valid                  ;      ; NV8    # 13.0 NINJA..DISGUISED FACE\n1F979         ; valid                  ;      ; NV8    # 14.0 FACE HOLDING BACK TEARS\n1F97A         ; valid                  ;      ; NV8    # 11.0 FACE WITH PLEADING EYES\n1F97B         ; valid                  ;      ; NV8    # 12.0 SARI\n1F97C..1F97F  ; valid                  ;      ; NV8    # 11.0 LAB COAT..FLAT SHOE\n1F980..1F984  ; valid                  ;      ; NV8    # 8.0  CRAB..UNICORN FACE\n1F985..1F991  ; valid                  ;      ; NV8    # 9.0  EAGLE..SQUID\n1F992..1F997  ; valid                  ;      ; NV8    # 10.0 GIRAFFE FACE..CRICKET\n1F998..1F9A2  ; valid                  ;      ; NV8    # 11.0 KANGAROO..SWAN\n1F9A3..1F9A4  ; valid                  ;      ; NV8    # 13.0 MAMMOTH..DODO\n1F9A5..1F9AA  ; valid                  ;      ; NV8    # 12.0 SLOTH..OYSTER\n1F9AB..1F9AD  ; valid                  ;      ; NV8    # 13.0 BEAVER..SEAL\n1F9AE..1F9AF  ; valid                  ;      ; NV8    # 12.0 GUIDE DOG..PROBING CANE\n1F9B0..1F9B9  ; valid                  ;      ; NV8    # 11.0 EMOJI COMPONENT RED HAIR..SUPERVILLAIN\n1F9BA..1F9BF  ; valid                  ;      ; NV8    # 12.0 SAFETY VEST..MECHANICAL LEG\n1F9C0         ; valid                  ;      ; NV8    # 8.0  CHEESE WEDGE\n1F9C1..1F9C2  ; valid                  ;      ; NV8    # 11.0 CUPCAKE..SALT SHAKER\n1F9C3..1F9CA  ; valid                  ;      ; NV8    # 12.0 BEVERAGE BOX..ICE CUBE\n1F9CB         ; valid                  ;      ; NV8    # 13.0 BUBBLE TEA\n1F9CC         ; valid                  ;      ; NV8    # 14.0 TROLL\n1F9CD..1F9CF  ; valid                  ;      ; NV8    # 12.0 STANDING PERSON..DEAF PERSON\n1F9D0..1F9E6  ; valid                  ;      ; NV8    # 10.0 FACE WITH MONOCLE..SOCKS\n1F9E7..1F9FF  ; valid                  ;      ; NV8    # 11.0 RED GIFT ENVELOPE..NAZAR AMULET\n1FA00..1FA53  ; valid                  ;      ; NV8    # 12.0 NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP\n1FA54..1FA5F  ; disallowed                             # NA   <reserved-1FA54>..<reserved-1FA5F>\n1FA60..1FA6D  ; valid                  ;      ; NV8    # 11.0 XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER\n1FA6E..1FA6F  ; disallowed                             # NA   <reserved-1FA6E>..<reserved-1FA6F>\n1FA70..1FA73  ; valid                  ;      ; NV8    # 12.0 BALLET SHOES..SHORTS\n1FA74         ; valid                  ;      ; NV8    # 13.0 THONG SANDAL\n1FA75..1FA77  ; valid                  ;      ; NV8    # 15.0 LIGHT BLUE HEART..PINK HEART\n1FA78..1FA7A  ; valid                  ;      ; NV8    # 12.0 DROP OF BLOOD..STETHOSCOPE\n1FA7B..1FA7C  ; valid                  ;      ; NV8    # 14.0 X-RAY..CRUTCH\n1FA7D..1FA7F  ; disallowed                             # NA   <reserved-1FA7D>..<reserved-1FA7F>\n1FA80..1FA82  ; valid                  ;      ; NV8    # 12.0 YO-YO..PARACHUTE\n1FA83..1FA86  ; valid                  ;      ; NV8    # 13.0 BOOMERANG..NESTING DOLLS\n1FA87..1FA88  ; valid                  ;      ; NV8    # 15.0 MARACAS..FLUTE\n1FA89..1FA8F  ; disallowed                             # NA   <reserved-1FA89>..<reserved-1FA8F>\n1FA90..1FA95  ; valid                  ;      ; NV8    # 12.0 RINGED PLANET..BANJO\n1FA96..1FAA8  ; valid                  ;      ; NV8    # 13.0 MILITARY HELMET..ROCK\n1FAA9..1FAAC  ; valid                  ;      ; NV8    # 14.0 MIRROR BALL..HAMSA\n1FAAD..1FAAF  ; valid                  ;      ; NV8    # 15.0 FOLDING HAND FAN..KHANDA\n1FAB0..1FAB6  ; valid                  ;      ; NV8    # 13.0 FLY..FEATHER\n1FAB7..1FABA  ; valid                  ;      ; NV8    # 14.0 LOTUS..NEST WITH EGGS\n1FABB..1FABD  ; valid                  ;      ; NV8    # 15.0 HYACINTH..WING\n1FABE         ; disallowed                             # NA   <reserved-1FABE>\n1FABF         ; valid                  ;      ; NV8    # 15.0 GOOSE\n1FAC0..1FAC2  ; valid                  ;      ; NV8    # 13.0 ANATOMICAL HEART..PEOPLE HUGGING\n1FAC3..1FAC5  ; valid                  ;      ; NV8    # 14.0 PREGNANT MAN..PERSON WITH CROWN\n1FAC6..1FACD  ; disallowed                             # NA   <reserved-1FAC6>..<reserved-1FACD>\n1FACE..1FACF  ; valid                  ;      ; NV8    # 15.0 MOOSE..DONKEY\n1FAD0..1FAD6  ; valid                  ;      ; NV8    # 13.0 BLUEBERRIES..TEAPOT\n1FAD7..1FAD9  ; valid                  ;      ; NV8    # 14.0 POURING LIQUID..JAR\n1FADA..1FADB  ; valid                  ;      ; NV8    # 15.0 GINGER ROOT..PEA POD\n1FADC..1FADF  ; disallowed                             # NA   <reserved-1FADC>..<reserved-1FADF>\n1FAE0..1FAE7  ; valid                  ;      ; NV8    # 14.0 MELTING FACE..BUBBLES\n1FAE8         ; valid                  ;      ; NV8    # 15.0 SHAKING FACE\n1FAE9..1FAEF  ; disallowed                             # NA   <reserved-1FAE9>..<reserved-1FAEF>\n1FAF0..1FAF6  ; valid                  ;      ; NV8    # 14.0 HAND WITH INDEX FINGER AND THUMB CROSSED..HEART HANDS\n1FAF7..1FAF8  ; valid                  ;      ; NV8    # 15.0 LEFTWARDS PUSHING HAND..RIGHTWARDS PUSHING HAND\n1FAF9..1FAFF  ; disallowed                             # NA   <reserved-1FAF9>..<reserved-1FAFF>\n1FB00..1FB92  ; valid                  ;      ; NV8    # 13.0 BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK\n1FB93         ; disallowed                             # NA   <reserved-1FB93>\n1FB94..1FBCA  ; valid                  ;      ; NV8    # 13.0 LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..WHITE UP-POINTING CHEVRON\n1FBCB..1FBEF  ; disallowed                             # NA   <reserved-1FBCB>..<reserved-1FBEF>\n1FBF0         ; mapped                 ; 0030          # 13.0 SEGMENTED DIGIT ZERO\n1FBF1         ; mapped                 ; 0031          # 13.0 SEGMENTED DIGIT ONE\n1FBF2         ; mapped                 ; 0032          # 13.0 SEGMENTED DIGIT TWO\n1FBF3         ; mapped                 ; 0033          # 13.0 SEGMENTED DIGIT THREE\n1FBF4         ; mapped                 ; 0034          # 13.0 SEGMENTED DIGIT FOUR\n1FBF5         ; mapped                 ; 0035          # 13.0 SEGMENTED DIGIT FIVE\n1FBF6         ; mapped                 ; 0036          # 13.0 SEGMENTED DIGIT SIX\n1FBF7         ; mapped                 ; 0037          # 13.0 SEGMENTED DIGIT SEVEN\n1FBF8         ; mapped                 ; 0038          # 13.0 SEGMENTED DIGIT EIGHT\n1FBF9         ; mapped                 ; 0039          # 13.0 SEGMENTED DIGIT NINE\n1FBFA..1FFFD  ; disallowed                             # NA   <reserved-1FBFA>..<reserved-1FFFD>\n1FFFE..1FFFF  ; disallowed                             # 2.0  <noncharacter-1FFFE>..<noncharacter-1FFFF>\n20000..2A6D6  ; valid                                  # 3.1  CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6\n2A6D7..2A6DD  ; valid                                  # 13.0 CJK UNIFIED IDEOGRAPH-2A6D7..CJK UNIFIED IDEOGRAPH-2A6DD\n2A6DE..2A6DF  ; valid                                  # 14.0 CJK UNIFIED IDEOGRAPH-2A6DE..CJK UNIFIED IDEOGRAPH-2A6DF\n2A6E0..2A6FF  ; disallowed                             # NA   <reserved-2A6E0>..<reserved-2A6FF>\n2A700..2B734  ; valid                                  # 5.2  CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B734\n2B735..2B738  ; valid                                  # 14.0 CJK UNIFIED IDEOGRAPH-2B735..CJK UNIFIED IDEOGRAPH-2B738\n2B739         ; valid                                  # 15.0 CJK UNIFIED IDEOGRAPH-2B739\n2B73A..2B73F  ; disallowed                             # NA   <reserved-2B73A>..<reserved-2B73F>\n2B740..2B81D  ; valid                                  # 6.0  CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D\n2B81E..2B81F  ; disallowed                             # NA   <reserved-2B81E>..<reserved-2B81F>\n2B820..2CEA1  ; valid                                  # 8.0  CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1\n2CEA2..2CEAF  ; disallowed                             # NA   <reserved-2CEA2>..<reserved-2CEAF>\n2CEB0..2EBE0  ; valid                                  # 10.0 CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0\n2EBE1..2EBEF  ; disallowed                             # NA   <reserved-2EBE1>..<reserved-2EBEF>\n2EBF0..2EE5D  ; valid                                  # 15.1 CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D\n2EE5E..2F7FF  ; disallowed                             # NA   <reserved-2EE5E>..<reserved-2F7FF>\n2F800         ; mapped                 ; 4E3D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F800\n2F801         ; mapped                 ; 4E38          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F801\n2F802         ; mapped                 ; 4E41          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F802\n2F803         ; mapped                 ; 20122         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F803\n2F804         ; mapped                 ; 4F60          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F804\n2F805         ; mapped                 ; 4FAE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F805\n2F806         ; mapped                 ; 4FBB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F806\n2F807         ; mapped                 ; 5002          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F807\n2F808         ; mapped                 ; 507A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F808\n2F809         ; mapped                 ; 5099          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F809\n2F80A         ; mapped                 ; 50E7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F80A\n2F80B         ; mapped                 ; 50CF          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F80B\n2F80C         ; mapped                 ; 349E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F80C\n2F80D         ; mapped                 ; 2063A         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F80D\n2F80E         ; mapped                 ; 514D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F80E\n2F80F         ; mapped                 ; 5154          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F80F\n2F810         ; mapped                 ; 5164          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F810\n2F811         ; mapped                 ; 5177          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F811\n2F812         ; mapped                 ; 2051C         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F812\n2F813         ; mapped                 ; 34B9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F813\n2F814         ; mapped                 ; 5167          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F814\n2F815         ; mapped                 ; 518D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F815\n2F816         ; mapped                 ; 2054B         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F816\n2F817         ; mapped                 ; 5197          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F817\n2F818         ; mapped                 ; 51A4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F818\n2F819         ; mapped                 ; 4ECC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F819\n2F81A         ; mapped                 ; 51AC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F81A\n2F81B         ; mapped                 ; 51B5          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F81B\n2F81C         ; mapped                 ; 291DF         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F81C\n2F81D         ; mapped                 ; 51F5          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F81D\n2F81E         ; mapped                 ; 5203          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F81E\n2F81F         ; mapped                 ; 34DF          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F81F\n2F820         ; mapped                 ; 523B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F820\n2F821         ; mapped                 ; 5246          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F821\n2F822         ; mapped                 ; 5272          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F822\n2F823         ; mapped                 ; 5277          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F823\n2F824         ; mapped                 ; 3515          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F824\n2F825         ; mapped                 ; 52C7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F825\n2F826         ; mapped                 ; 52C9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F826\n2F827         ; mapped                 ; 52E4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F827\n2F828         ; mapped                 ; 52FA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F828\n2F829         ; mapped                 ; 5305          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F829\n2F82A         ; mapped                 ; 5306          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F82A\n2F82B         ; mapped                 ; 5317          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F82B\n2F82C         ; mapped                 ; 5349          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F82C\n2F82D         ; mapped                 ; 5351          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F82D\n2F82E         ; mapped                 ; 535A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F82E\n2F82F         ; mapped                 ; 5373          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F82F\n2F830         ; mapped                 ; 537D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F830\n2F831..2F833  ; mapped                 ; 537F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F831..CJK COMPATIBILITY IDEOGRAPH-2F833\n2F834         ; mapped                 ; 20A2C         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F834\n2F835         ; mapped                 ; 7070          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F835\n2F836         ; mapped                 ; 53CA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F836\n2F837         ; mapped                 ; 53DF          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F837\n2F838         ; mapped                 ; 20B63         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F838\n2F839         ; mapped                 ; 53EB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F839\n2F83A         ; mapped                 ; 53F1          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F83A\n2F83B         ; mapped                 ; 5406          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F83B\n2F83C         ; mapped                 ; 549E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F83C\n2F83D         ; mapped                 ; 5438          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F83D\n2F83E         ; mapped                 ; 5448          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F83E\n2F83F         ; mapped                 ; 5468          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F83F\n2F840         ; mapped                 ; 54A2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F840\n2F841         ; mapped                 ; 54F6          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F841\n2F842         ; mapped                 ; 5510          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F842\n2F843         ; mapped                 ; 5553          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F843\n2F844         ; mapped                 ; 5563          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F844\n2F845..2F846  ; mapped                 ; 5584          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F845..CJK COMPATIBILITY IDEOGRAPH-2F846\n2F847         ; mapped                 ; 5599          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F847\n2F848         ; mapped                 ; 55AB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F848\n2F849         ; mapped                 ; 55B3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F849\n2F84A         ; mapped                 ; 55C2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F84A\n2F84B         ; mapped                 ; 5716          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F84B\n2F84C         ; mapped                 ; 5606          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F84C\n2F84D         ; mapped                 ; 5717          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F84D\n2F84E         ; mapped                 ; 5651          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F84E\n2F84F         ; mapped                 ; 5674          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F84F\n2F850         ; mapped                 ; 5207          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F850\n2F851         ; mapped                 ; 58EE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F851\n2F852         ; mapped                 ; 57CE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F852\n2F853         ; mapped                 ; 57F4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F853\n2F854         ; mapped                 ; 580D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F854\n2F855         ; mapped                 ; 578B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F855\n2F856         ; mapped                 ; 5832          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F856\n2F857         ; mapped                 ; 5831          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F857\n2F858         ; mapped                 ; 58AC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F858\n2F859         ; mapped                 ; 214E4         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F859\n2F85A         ; mapped                 ; 58F2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F85A\n2F85B         ; mapped                 ; 58F7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F85B\n2F85C         ; mapped                 ; 5906          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F85C\n2F85D         ; mapped                 ; 591A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F85D\n2F85E         ; mapped                 ; 5922          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F85E\n2F85F         ; mapped                 ; 5962          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F85F\n2F860         ; mapped                 ; 216A8         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F860\n2F861         ; mapped                 ; 216EA         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F861\n2F862         ; mapped                 ; 59EC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F862\n2F863         ; mapped                 ; 5A1B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F863\n2F864         ; mapped                 ; 5A27          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F864\n2F865         ; mapped                 ; 59D8          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F865\n2F866         ; mapped                 ; 5A66          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F866\n2F867         ; mapped                 ; 36EE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F867\n2F868         ; disallowed                             # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F868\n2F869         ; mapped                 ; 5B08          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F869\n2F86A..2F86B  ; mapped                 ; 5B3E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F86A..CJK COMPATIBILITY IDEOGRAPH-2F86B\n2F86C         ; mapped                 ; 219C8         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F86C\n2F86D         ; mapped                 ; 5BC3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F86D\n2F86E         ; mapped                 ; 5BD8          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F86E\n2F86F         ; mapped                 ; 5BE7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F86F\n2F870         ; mapped                 ; 5BF3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F870\n2F871         ; mapped                 ; 21B18         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F871\n2F872         ; mapped                 ; 5BFF          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F872\n2F873         ; mapped                 ; 5C06          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F873\n2F874         ; disallowed                             # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F874\n2F875         ; mapped                 ; 5C22          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F875\n2F876         ; mapped                 ; 3781          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F876\n2F877         ; mapped                 ; 5C60          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F877\n2F878         ; mapped                 ; 5C6E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F878\n2F879         ; mapped                 ; 5CC0          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F879\n2F87A         ; mapped                 ; 5C8D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F87A\n2F87B         ; mapped                 ; 21DE4         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F87B\n2F87C         ; mapped                 ; 5D43          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F87C\n2F87D         ; mapped                 ; 21DE6         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F87D\n2F87E         ; mapped                 ; 5D6E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F87E\n2F87F         ; mapped                 ; 5D6B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F87F\n2F880         ; mapped                 ; 5D7C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F880\n2F881         ; mapped                 ; 5DE1          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F881\n2F882         ; mapped                 ; 5DE2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F882\n2F883         ; mapped                 ; 382F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F883\n2F884         ; mapped                 ; 5DFD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F884\n2F885         ; mapped                 ; 5E28          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F885\n2F886         ; mapped                 ; 5E3D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F886\n2F887         ; mapped                 ; 5E69          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F887\n2F888         ; mapped                 ; 3862          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F888\n2F889         ; mapped                 ; 22183         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F889\n2F88A         ; mapped                 ; 387C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F88A\n2F88B         ; mapped                 ; 5EB0          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F88B\n2F88C         ; mapped                 ; 5EB3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F88C\n2F88D         ; mapped                 ; 5EB6          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F88D\n2F88E         ; mapped                 ; 5ECA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F88E\n2F88F         ; mapped                 ; 2A392         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F88F\n2F890         ; mapped                 ; 5EFE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F890\n2F891..2F892  ; mapped                 ; 22331         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F891..CJK COMPATIBILITY IDEOGRAPH-2F892\n2F893         ; mapped                 ; 8201          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F893\n2F894..2F895  ; mapped                 ; 5F22          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F894..CJK COMPATIBILITY IDEOGRAPH-2F895\n2F896         ; mapped                 ; 38C7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F896\n2F897         ; mapped                 ; 232B8         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F897\n2F898         ; mapped                 ; 261DA         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F898\n2F899         ; mapped                 ; 5F62          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F899\n2F89A         ; mapped                 ; 5F6B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F89A\n2F89B         ; mapped                 ; 38E3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F89B\n2F89C         ; mapped                 ; 5F9A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F89C\n2F89D         ; mapped                 ; 5FCD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F89D\n2F89E         ; mapped                 ; 5FD7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F89E\n2F89F         ; mapped                 ; 5FF9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F89F\n2F8A0         ; mapped                 ; 6081          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A0\n2F8A1         ; mapped                 ; 393A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A1\n2F8A2         ; mapped                 ; 391C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A2\n2F8A3         ; mapped                 ; 6094          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A3\n2F8A4         ; mapped                 ; 226D4         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A4\n2F8A5         ; mapped                 ; 60C7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A5\n2F8A6         ; mapped                 ; 6148          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A6\n2F8A7         ; mapped                 ; 614C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A7\n2F8A8         ; mapped                 ; 614E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A8\n2F8A9         ; mapped                 ; 614C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8A9\n2F8AA         ; mapped                 ; 617A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8AA\n2F8AB         ; mapped                 ; 618E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8AB\n2F8AC         ; mapped                 ; 61B2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8AC\n2F8AD         ; mapped                 ; 61A4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8AD\n2F8AE         ; mapped                 ; 61AF          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8AE\n2F8AF         ; mapped                 ; 61DE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8AF\n2F8B0         ; mapped                 ; 61F2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B0\n2F8B1         ; mapped                 ; 61F6          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B1\n2F8B2         ; mapped                 ; 6210          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B2\n2F8B3         ; mapped                 ; 621B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B3\n2F8B4         ; mapped                 ; 625D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B4\n2F8B5         ; mapped                 ; 62B1          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B5\n2F8B6         ; mapped                 ; 62D4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B6\n2F8B7         ; mapped                 ; 6350          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B7\n2F8B8         ; mapped                 ; 22B0C         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B8\n2F8B9         ; mapped                 ; 633D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8B9\n2F8BA         ; mapped                 ; 62FC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8BA\n2F8BB         ; mapped                 ; 6368          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8BB\n2F8BC         ; mapped                 ; 6383          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8BC\n2F8BD         ; mapped                 ; 63E4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8BD\n2F8BE         ; mapped                 ; 22BF1         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8BE\n2F8BF         ; mapped                 ; 6422          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8BF\n2F8C0         ; mapped                 ; 63C5          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C0\n2F8C1         ; mapped                 ; 63A9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C1\n2F8C2         ; mapped                 ; 3A2E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C2\n2F8C3         ; mapped                 ; 6469          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C3\n2F8C4         ; mapped                 ; 647E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C4\n2F8C5         ; mapped                 ; 649D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C5\n2F8C6         ; mapped                 ; 6477          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C6\n2F8C7         ; mapped                 ; 3A6C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C7\n2F8C8         ; mapped                 ; 654F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C8\n2F8C9         ; mapped                 ; 656C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8C9\n2F8CA         ; mapped                 ; 2300A         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8CA\n2F8CB         ; mapped                 ; 65E3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8CB\n2F8CC         ; mapped                 ; 66F8          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8CC\n2F8CD         ; mapped                 ; 6649          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8CD\n2F8CE         ; mapped                 ; 3B19          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8CE\n2F8CF         ; mapped                 ; 6691          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8CF\n2F8D0         ; mapped                 ; 3B08          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D0\n2F8D1         ; mapped                 ; 3AE4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D1\n2F8D2         ; mapped                 ; 5192          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D2\n2F8D3         ; mapped                 ; 5195          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D3\n2F8D4         ; mapped                 ; 6700          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D4\n2F8D5         ; mapped                 ; 669C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D5\n2F8D6         ; mapped                 ; 80AD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D6\n2F8D7         ; mapped                 ; 43D9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D7\n2F8D8         ; mapped                 ; 6717          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D8\n2F8D9         ; mapped                 ; 671B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8D9\n2F8DA         ; mapped                 ; 6721          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8DA\n2F8DB         ; mapped                 ; 675E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8DB\n2F8DC         ; mapped                 ; 6753          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8DC\n2F8DD         ; mapped                 ; 233C3         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8DD\n2F8DE         ; mapped                 ; 3B49          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8DE\n2F8DF         ; mapped                 ; 67FA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8DF\n2F8E0         ; mapped                 ; 6785          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E0\n2F8E1         ; mapped                 ; 6852          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E1\n2F8E2         ; mapped                 ; 6885          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E2\n2F8E3         ; mapped                 ; 2346D         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E3\n2F8E4         ; mapped                 ; 688E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E4\n2F8E5         ; mapped                 ; 681F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E5\n2F8E6         ; mapped                 ; 6914          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E6\n2F8E7         ; mapped                 ; 3B9D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E7\n2F8E8         ; mapped                 ; 6942          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E8\n2F8E9         ; mapped                 ; 69A3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8E9\n2F8EA         ; mapped                 ; 69EA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8EA\n2F8EB         ; mapped                 ; 6AA8          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8EB\n2F8EC         ; mapped                 ; 236A3         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8EC\n2F8ED         ; mapped                 ; 6ADB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8ED\n2F8EE         ; mapped                 ; 3C18          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8EE\n2F8EF         ; mapped                 ; 6B21          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8EF\n2F8F0         ; mapped                 ; 238A7         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F0\n2F8F1         ; mapped                 ; 6B54          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F1\n2F8F2         ; mapped                 ; 3C4E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F2\n2F8F3         ; mapped                 ; 6B72          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F3\n2F8F4         ; mapped                 ; 6B9F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F4\n2F8F5         ; mapped                 ; 6BBA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F5\n2F8F6         ; mapped                 ; 6BBB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F6\n2F8F7         ; mapped                 ; 23A8D         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F7\n2F8F8         ; mapped                 ; 21D0B         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F8\n2F8F9         ; mapped                 ; 23AFA         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8F9\n2F8FA         ; mapped                 ; 6C4E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8FA\n2F8FB         ; mapped                 ; 23CBC         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8FB\n2F8FC         ; mapped                 ; 6CBF          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8FC\n2F8FD         ; mapped                 ; 6CCD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8FD\n2F8FE         ; mapped                 ; 6C67          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8FE\n2F8FF         ; mapped                 ; 6D16          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F8FF\n2F900         ; mapped                 ; 6D3E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F900\n2F901         ; mapped                 ; 6D77          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F901\n2F902         ; mapped                 ; 6D41          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F902\n2F903         ; mapped                 ; 6D69          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F903\n2F904         ; mapped                 ; 6D78          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F904\n2F905         ; mapped                 ; 6D85          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F905\n2F906         ; mapped                 ; 23D1E         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F906\n2F907         ; mapped                 ; 6D34          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F907\n2F908         ; mapped                 ; 6E2F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F908\n2F909         ; mapped                 ; 6E6E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F909\n2F90A         ; mapped                 ; 3D33          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F90A\n2F90B         ; mapped                 ; 6ECB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F90B\n2F90C         ; mapped                 ; 6EC7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F90C\n2F90D         ; mapped                 ; 23ED1         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F90D\n2F90E         ; mapped                 ; 6DF9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F90E\n2F90F         ; mapped                 ; 6F6E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F90F\n2F910         ; mapped                 ; 23F5E         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F910\n2F911         ; mapped                 ; 23F8E         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F911\n2F912         ; mapped                 ; 6FC6          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F912\n2F913         ; mapped                 ; 7039          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F913\n2F914         ; mapped                 ; 701E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F914\n2F915         ; mapped                 ; 701B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F915\n2F916         ; mapped                 ; 3D96          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F916\n2F917         ; mapped                 ; 704A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F917\n2F918         ; mapped                 ; 707D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F918\n2F919         ; mapped                 ; 7077          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F919\n2F91A         ; mapped                 ; 70AD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F91A\n2F91B         ; mapped                 ; 20525         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F91B\n2F91C         ; mapped                 ; 7145          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F91C\n2F91D         ; mapped                 ; 24263         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F91D\n2F91E         ; mapped                 ; 719C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F91E\n2F91F         ; disallowed                             # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F91F\n2F920         ; mapped                 ; 7228          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F920\n2F921         ; mapped                 ; 7235          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F921\n2F922         ; mapped                 ; 7250          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F922\n2F923         ; mapped                 ; 24608         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F923\n2F924         ; mapped                 ; 7280          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F924\n2F925         ; mapped                 ; 7295          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F925\n2F926         ; mapped                 ; 24735         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F926\n2F927         ; mapped                 ; 24814         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F927\n2F928         ; mapped                 ; 737A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F928\n2F929         ; mapped                 ; 738B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F929\n2F92A         ; mapped                 ; 3EAC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F92A\n2F92B         ; mapped                 ; 73A5          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F92B\n2F92C..2F92D  ; mapped                 ; 3EB8          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F92C..CJK COMPATIBILITY IDEOGRAPH-2F92D\n2F92E         ; mapped                 ; 7447          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F92E\n2F92F         ; mapped                 ; 745C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F92F\n2F930         ; mapped                 ; 7471          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F930\n2F931         ; mapped                 ; 7485          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F931\n2F932         ; mapped                 ; 74CA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F932\n2F933         ; mapped                 ; 3F1B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F933\n2F934         ; mapped                 ; 7524          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F934\n2F935         ; mapped                 ; 24C36         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F935\n2F936         ; mapped                 ; 753E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F936\n2F937         ; mapped                 ; 24C92         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F937\n2F938         ; mapped                 ; 7570          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F938\n2F939         ; mapped                 ; 2219F         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F939\n2F93A         ; mapped                 ; 7610          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F93A\n2F93B         ; mapped                 ; 24FA1         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F93B\n2F93C         ; mapped                 ; 24FB8         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F93C\n2F93D         ; mapped                 ; 25044         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F93D\n2F93E         ; mapped                 ; 3FFC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F93E\n2F93F         ; mapped                 ; 4008          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F93F\n2F940         ; mapped                 ; 76F4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F940\n2F941         ; mapped                 ; 250F3         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F941\n2F942         ; mapped                 ; 250F2         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F942\n2F943         ; mapped                 ; 25119         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F943\n2F944         ; mapped                 ; 25133         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F944\n2F945         ; mapped                 ; 771E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F945\n2F946..2F947  ; mapped                 ; 771F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F946..CJK COMPATIBILITY IDEOGRAPH-2F947\n2F948         ; mapped                 ; 774A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F948\n2F949         ; mapped                 ; 4039          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F949\n2F94A         ; mapped                 ; 778B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F94A\n2F94B         ; mapped                 ; 4046          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F94B\n2F94C         ; mapped                 ; 4096          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F94C\n2F94D         ; mapped                 ; 2541D         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F94D\n2F94E         ; mapped                 ; 784E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F94E\n2F94F         ; mapped                 ; 788C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F94F\n2F950         ; mapped                 ; 78CC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F950\n2F951         ; mapped                 ; 40E3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F951\n2F952         ; mapped                 ; 25626         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F952\n2F953         ; mapped                 ; 7956          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F953\n2F954         ; mapped                 ; 2569A         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F954\n2F955         ; mapped                 ; 256C5         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F955\n2F956         ; mapped                 ; 798F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F956\n2F957         ; mapped                 ; 79EB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F957\n2F958         ; mapped                 ; 412F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F958\n2F959         ; mapped                 ; 7A40          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F959\n2F95A         ; mapped                 ; 7A4A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F95A\n2F95B         ; mapped                 ; 7A4F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F95B\n2F95C         ; mapped                 ; 2597C         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F95C\n2F95D..2F95E  ; mapped                 ; 25AA7         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F95D..CJK COMPATIBILITY IDEOGRAPH-2F95E\n2F95F         ; disallowed                             # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F95F\n2F960         ; mapped                 ; 4202          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F960\n2F961         ; mapped                 ; 25BAB         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F961\n2F962         ; mapped                 ; 7BC6          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F962\n2F963         ; mapped                 ; 7BC9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F963\n2F964         ; mapped                 ; 4227          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F964\n2F965         ; mapped                 ; 25C80         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F965\n2F966         ; mapped                 ; 7CD2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F966\n2F967         ; mapped                 ; 42A0          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F967\n2F968         ; mapped                 ; 7CE8          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F968\n2F969         ; mapped                 ; 7CE3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F969\n2F96A         ; mapped                 ; 7D00          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F96A\n2F96B         ; mapped                 ; 25F86         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F96B\n2F96C         ; mapped                 ; 7D63          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F96C\n2F96D         ; mapped                 ; 4301          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F96D\n2F96E         ; mapped                 ; 7DC7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F96E\n2F96F         ; mapped                 ; 7E02          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F96F\n2F970         ; mapped                 ; 7E45          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F970\n2F971         ; mapped                 ; 4334          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F971\n2F972         ; mapped                 ; 26228         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F972\n2F973         ; mapped                 ; 26247         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F973\n2F974         ; mapped                 ; 4359          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F974\n2F975         ; mapped                 ; 262D9         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F975\n2F976         ; mapped                 ; 7F7A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F976\n2F977         ; mapped                 ; 2633E         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F977\n2F978         ; mapped                 ; 7F95          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F978\n2F979         ; mapped                 ; 7FFA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F979\n2F97A         ; mapped                 ; 8005          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F97A\n2F97B         ; mapped                 ; 264DA         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F97B\n2F97C         ; mapped                 ; 26523         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F97C\n2F97D         ; mapped                 ; 8060          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F97D\n2F97E         ; mapped                 ; 265A8         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F97E\n2F97F         ; mapped                 ; 8070          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F97F\n2F980         ; mapped                 ; 2335F         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F980\n2F981         ; mapped                 ; 43D5          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F981\n2F982         ; mapped                 ; 80B2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F982\n2F983         ; mapped                 ; 8103          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F983\n2F984         ; mapped                 ; 440B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F984\n2F985         ; mapped                 ; 813E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F985\n2F986         ; mapped                 ; 5AB5          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F986\n2F987         ; mapped                 ; 267A7         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F987\n2F988         ; mapped                 ; 267B5         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F988\n2F989         ; mapped                 ; 23393         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F989\n2F98A         ; mapped                 ; 2339C         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F98A\n2F98B         ; mapped                 ; 8201          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F98B\n2F98C         ; mapped                 ; 8204          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F98C\n2F98D         ; mapped                 ; 8F9E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F98D\n2F98E         ; mapped                 ; 446B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F98E\n2F98F         ; mapped                 ; 8291          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F98F\n2F990         ; mapped                 ; 828B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F990\n2F991         ; mapped                 ; 829D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F991\n2F992         ; mapped                 ; 52B3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F992\n2F993         ; mapped                 ; 82B1          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F993\n2F994         ; mapped                 ; 82B3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F994\n2F995         ; mapped                 ; 82BD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F995\n2F996         ; mapped                 ; 82E6          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F996\n2F997         ; mapped                 ; 26B3C         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F997\n2F998         ; mapped                 ; 82E5          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F998\n2F999         ; mapped                 ; 831D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F999\n2F99A         ; mapped                 ; 8363          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F99A\n2F99B         ; mapped                 ; 83AD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F99B\n2F99C         ; mapped                 ; 8323          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F99C\n2F99D         ; mapped                 ; 83BD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F99D\n2F99E         ; mapped                 ; 83E7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F99E\n2F99F         ; mapped                 ; 8457          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F99F\n2F9A0         ; mapped                 ; 8353          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A0\n2F9A1         ; mapped                 ; 83CA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A1\n2F9A2         ; mapped                 ; 83CC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A2\n2F9A3         ; mapped                 ; 83DC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A3\n2F9A4         ; mapped                 ; 26C36         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A4\n2F9A5         ; mapped                 ; 26D6B         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A5\n2F9A6         ; mapped                 ; 26CD5         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A6\n2F9A7         ; mapped                 ; 452B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A7\n2F9A8         ; mapped                 ; 84F1          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A8\n2F9A9         ; mapped                 ; 84F3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9A9\n2F9AA         ; mapped                 ; 8516          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9AA\n2F9AB         ; mapped                 ; 273CA         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9AB\n2F9AC         ; mapped                 ; 8564          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9AC\n2F9AD         ; mapped                 ; 26F2C         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9AD\n2F9AE         ; mapped                 ; 455D          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9AE\n2F9AF         ; mapped                 ; 4561          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9AF\n2F9B0         ; mapped                 ; 26FB1         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B0\n2F9B1         ; mapped                 ; 270D2         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B1\n2F9B2         ; mapped                 ; 456B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B2\n2F9B3         ; mapped                 ; 8650          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B3\n2F9B4         ; mapped                 ; 865C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B4\n2F9B5         ; mapped                 ; 8667          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B5\n2F9B6         ; mapped                 ; 8669          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B6\n2F9B7         ; mapped                 ; 86A9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B7\n2F9B8         ; mapped                 ; 8688          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B8\n2F9B9         ; mapped                 ; 870E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9B9\n2F9BA         ; mapped                 ; 86E2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9BA\n2F9BB         ; mapped                 ; 8779          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9BB\n2F9BC         ; mapped                 ; 8728          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9BC\n2F9BD         ; mapped                 ; 876B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9BD\n2F9BE         ; mapped                 ; 8786          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9BE\n2F9BF         ; disallowed                             # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9BF\n2F9C0         ; mapped                 ; 87E1          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C0\n2F9C1         ; mapped                 ; 8801          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C1\n2F9C2         ; mapped                 ; 45F9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C2\n2F9C3         ; mapped                 ; 8860          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C3\n2F9C4         ; mapped                 ; 8863          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C4\n2F9C5         ; mapped                 ; 27667         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C5\n2F9C6         ; mapped                 ; 88D7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C6\n2F9C7         ; mapped                 ; 88DE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C7\n2F9C8         ; mapped                 ; 4635          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C8\n2F9C9         ; mapped                 ; 88FA          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9C9\n2F9CA         ; mapped                 ; 34BB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9CA\n2F9CB         ; mapped                 ; 278AE         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9CB\n2F9CC         ; mapped                 ; 27966         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9CC\n2F9CD         ; mapped                 ; 46BE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9CD\n2F9CE         ; mapped                 ; 46C7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9CE\n2F9CF         ; mapped                 ; 8AA0          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9CF\n2F9D0         ; mapped                 ; 8AED          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D0\n2F9D1         ; mapped                 ; 8B8A          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D1\n2F9D2         ; mapped                 ; 8C55          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D2\n2F9D3         ; mapped                 ; 27CA8         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D3\n2F9D4         ; mapped                 ; 8CAB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D4\n2F9D5         ; mapped                 ; 8CC1          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D5\n2F9D6         ; mapped                 ; 8D1B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D6\n2F9D7         ; mapped                 ; 8D77          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D7\n2F9D8         ; mapped                 ; 27F2F         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D8\n2F9D9         ; mapped                 ; 20804         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9D9\n2F9DA         ; mapped                 ; 8DCB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9DA\n2F9DB         ; mapped                 ; 8DBC          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9DB\n2F9DC         ; mapped                 ; 8DF0          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9DC\n2F9DD         ; mapped                 ; 208DE         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9DD\n2F9DE         ; mapped                 ; 8ED4          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9DE\n2F9DF         ; mapped                 ; 8F38          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9DF\n2F9E0         ; mapped                 ; 285D2         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E0\n2F9E1         ; mapped                 ; 285ED         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E1\n2F9E2         ; mapped                 ; 9094          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E2\n2F9E3         ; mapped                 ; 90F1          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E3\n2F9E4         ; mapped                 ; 9111          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E4\n2F9E5         ; mapped                 ; 2872E         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E5\n2F9E6         ; mapped                 ; 911B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E6\n2F9E7         ; mapped                 ; 9238          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E7\n2F9E8         ; mapped                 ; 92D7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E8\n2F9E9         ; mapped                 ; 92D8          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9E9\n2F9EA         ; mapped                 ; 927C          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9EA\n2F9EB         ; mapped                 ; 93F9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9EB\n2F9EC         ; mapped                 ; 9415          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9EC\n2F9ED         ; mapped                 ; 28BFA         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9ED\n2F9EE         ; mapped                 ; 958B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9EE\n2F9EF         ; mapped                 ; 4995          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9EF\n2F9F0         ; mapped                 ; 95B7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F0\n2F9F1         ; mapped                 ; 28D77         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F1\n2F9F2         ; mapped                 ; 49E6          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F2\n2F9F3         ; mapped                 ; 96C3          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F3\n2F9F4         ; mapped                 ; 5DB2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F4\n2F9F5         ; mapped                 ; 9723          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F5\n2F9F6         ; mapped                 ; 29145         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F6\n2F9F7         ; mapped                 ; 2921A         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F7\n2F9F8         ; mapped                 ; 4A6E          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F8\n2F9F9         ; mapped                 ; 4A76          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9F9\n2F9FA         ; mapped                 ; 97E0          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9FA\n2F9FB         ; mapped                 ; 2940A         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9FB\n2F9FC         ; mapped                 ; 4AB2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9FC\n2F9FD         ; mapped                 ; 29496         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9FD\n2F9FE..2F9FF  ; mapped                 ; 980B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2F9FE..CJK COMPATIBILITY IDEOGRAPH-2F9FF\n2FA00         ; mapped                 ; 9829          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA00\n2FA01         ; mapped                 ; 295B6         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA01\n2FA02         ; mapped                 ; 98E2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA02\n2FA03         ; mapped                 ; 4B33          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA03\n2FA04         ; mapped                 ; 9929          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA04\n2FA05         ; mapped                 ; 99A7          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA05\n2FA06         ; mapped                 ; 99C2          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA06\n2FA07         ; mapped                 ; 99FE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA07\n2FA08         ; mapped                 ; 4BCE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA08\n2FA09         ; mapped                 ; 29B30         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA09\n2FA0A         ; mapped                 ; 9B12          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA0A\n2FA0B         ; mapped                 ; 9C40          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA0B\n2FA0C         ; mapped                 ; 9CFD          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA0C\n2FA0D         ; mapped                 ; 4CCE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA0D\n2FA0E         ; mapped                 ; 4CED          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA0E\n2FA0F         ; mapped                 ; 9D67          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA0F\n2FA10         ; mapped                 ; 2A0CE         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA10\n2FA11         ; mapped                 ; 4CF8          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA11\n2FA12         ; mapped                 ; 2A105         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA12\n2FA13         ; mapped                 ; 2A20E         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA13\n2FA14         ; mapped                 ; 2A291         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA14\n2FA15         ; mapped                 ; 9EBB          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA15\n2FA16         ; mapped                 ; 4D56          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA16\n2FA17         ; mapped                 ; 9EF9          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA17\n2FA18         ; mapped                 ; 9EFE          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA18\n2FA19         ; mapped                 ; 9F05          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA19\n2FA1A         ; mapped                 ; 9F0F          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA1A\n2FA1B         ; mapped                 ; 9F16          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA1B\n2FA1C         ; mapped                 ; 9F3B          # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA1C\n2FA1D         ; mapped                 ; 2A600         # 3.1  CJK COMPATIBILITY IDEOGRAPH-2FA1D\n2FA1E..2FFFD  ; disallowed                             # NA   <reserved-2FA1E>..<reserved-2FFFD>\n2FFFE..2FFFF  ; disallowed                             # 2.0  <noncharacter-2FFFE>..<noncharacter-2FFFF>\n30000..3134A  ; valid                                  # 13.0 CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A\n3134B..3134F  ; disallowed                             # NA   <reserved-3134B>..<reserved-3134F>\n31350..323AF  ; valid                                  # 15.0 CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF\n323B0..3FFFD  ; disallowed                             # NA   <reserved-323B0>..<reserved-3FFFD>\n3FFFE..3FFFF  ; disallowed                             # 2.0  <noncharacter-3FFFE>..<noncharacter-3FFFF>\n40000..4FFFD  ; disallowed                             # NA   <reserved-40000>..<reserved-4FFFD>\n4FFFE..4FFFF  ; disallowed                             # 2.0  <noncharacter-4FFFE>..<noncharacter-4FFFF>\n50000..5FFFD  ; disallowed                             # NA   <reserved-50000>..<reserved-5FFFD>\n5FFFE..5FFFF  ; disallowed                             # 2.0  <noncharacter-5FFFE>..<noncharacter-5FFFF>\n60000..6FFFD  ; disallowed                             # NA   <reserved-60000>..<reserved-6FFFD>\n6FFFE..6FFFF  ; disallowed                             # 2.0  <noncharacter-6FFFE>..<noncharacter-6FFFF>\n70000..7FFFD  ; disallowed                             # NA   <reserved-70000>..<reserved-7FFFD>\n7FFFE..7FFFF  ; disallowed                             # 2.0  <noncharacter-7FFFE>..<noncharacter-7FFFF>\n80000..8FFFD  ; disallowed                             # NA   <reserved-80000>..<reserved-8FFFD>\n8FFFE..8FFFF  ; disallowed                             # 2.0  <noncharacter-8FFFE>..<noncharacter-8FFFF>\n90000..9FFFD  ; disallowed                             # NA   <reserved-90000>..<reserved-9FFFD>\n9FFFE..9FFFF  ; disallowed                             # 2.0  <noncharacter-9FFFE>..<noncharacter-9FFFF>\nA0000..AFFFD  ; disallowed                             # NA   <reserved-A0000>..<reserved-AFFFD>\nAFFFE..AFFFF  ; disallowed                             # 2.0  <noncharacter-AFFFE>..<noncharacter-AFFFF>\nB0000..BFFFD  ; disallowed                             # NA   <reserved-B0000>..<reserved-BFFFD>\nBFFFE..BFFFF  ; disallowed                             # 2.0  <noncharacter-BFFFE>..<noncharacter-BFFFF>\nC0000..CFFFD  ; disallowed                             # NA   <reserved-C0000>..<reserved-CFFFD>\nCFFFE..CFFFF  ; disallowed                             # 2.0  <noncharacter-CFFFE>..<noncharacter-CFFFF>\nD0000..DFFFD  ; disallowed                             # NA   <reserved-D0000>..<reserved-DFFFD>\nDFFFE..DFFFF  ; disallowed                             # 2.0  <noncharacter-DFFFE>..<noncharacter-DFFFF>\nE0000         ; disallowed                             # NA   <reserved-E0000>\nE0001         ; disallowed                             # 3.1  LANGUAGE TAG\nE0002..E001F  ; disallowed                             # NA   <reserved-E0002>..<reserved-E001F>\nE0020..E007F  ; disallowed                             # 3.1  TAG SPACE..CANCEL TAG\nE0080..E00FF  ; disallowed                             # NA   <reserved-E0080>..<reserved-E00FF>\nE0100..E01EF  ; ignored                                # 4.0  VARIATION SELECTOR-17..VARIATION SELECTOR-256\nE01F0..EFFFD  ; disallowed                             # NA   <reserved-E01F0>..<reserved-EFFFD>\nEFFFE..EFFFF  ; disallowed                             # 2.0  <noncharacter-EFFFE>..<noncharacter-EFFFF>\nF0000..FFFFD  ; disallowed                             # 2.0  <private-use-F0000>..<private-use-FFFFD>\nFFFFE..FFFFF  ; disallowed                             # 2.0  <noncharacter-FFFFE>..<noncharacter-FFFFF>\n100000..10FFFD; disallowed                             # 2.0  <private-use-100000>..<private-use-10FFFD>\n10FFFE..10FFFF; disallowed                             # 2.0  <noncharacter-10FFFE>..<noncharacter-10FFFF>\n\n# Total code points: 1114112\n\n"
  },
  {
    "path": "okhttp-idna-mapping-table/src/test/kotlin/okhttp3/internal/idn/MappingTablesTest.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.idn\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport okhttp3.internal.idn.MappedRange.InlineDelta\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.encodeUtf8\nimport org.junit.jupiter.api.Test\n\nclass MappingTablesTest {\n  @Test fun simplifyCombinesMultipleMappings() {\n    assertThat(\n      mergeAdjacentRanges(\n        listOf(\n          Mapping(0x0232, 0x0232, TYPE_MAPPED, \"a\".encodeUtf8()),\n          Mapping(0x0233, 0x0233, TYPE_VALID, ByteString.EMPTY),\n          Mapping(0x0234, 0x0236, TYPE_VALID, ByteString.EMPTY),\n          Mapping(0x0237, 0x0239, TYPE_VALID, ByteString.EMPTY),\n          Mapping(0x023a, 0x023a, TYPE_MAPPED, \"b\".encodeUtf8()),\n        ),\n      ),\n    ).containsExactly(\n      Mapping(0x0232, 0x0232, TYPE_MAPPED, \"a\".encodeUtf8()),\n      Mapping(0x0233, 0x0239, TYPE_VALID, ByteString.EMPTY),\n      Mapping(0x023a, 0x023a, TYPE_MAPPED, \"b\".encodeUtf8()),\n    )\n  }\n\n  @Test fun simplifyDoesNotCombineWhenMappedTargetsAreDifferent() {\n    assertThat(\n      mergeAdjacentRanges(\n        listOf(\n          Mapping(0x0041, 0x0041, TYPE_MAPPED, \"a\".encodeUtf8()),\n          Mapping(0x0042, 0x0042, TYPE_MAPPED, \"b\".encodeUtf8()),\n        ),\n      ),\n    ).containsExactly(\n      Mapping(0x0041, 0x0041, TYPE_MAPPED, \"a\".encodeUtf8()),\n      Mapping(0x0042, 0x0042, TYPE_MAPPED, \"b\".encodeUtf8()),\n    )\n  }\n\n  @Test fun simplifyCanonicalizesType() {\n    assertThat(\n      mergeAdjacentRanges(\n        listOf(\n          Mapping(0x0000, 0x002c, TYPE_DISALLOWED_STD3_VALID, ByteString.EMPTY),\n        ),\n      ),\n    ).containsExactly(\n      Mapping(0x0000, 0x002c, TYPE_VALID, ByteString.EMPTY),\n    )\n  }\n\n  @Test fun simplifyCombinesCanonicalEquivalent() {\n    assertThat(\n      mergeAdjacentRanges(\n        listOf(\n          Mapping(0x0000, 0x002c, TYPE_DISALLOWED_STD3_VALID, ByteString.EMPTY),\n          Mapping(0x002d, 0x002e, TYPE_VALID, ByteString.EMPTY),\n        ),\n      ),\n    ).containsExactly(\n      Mapping(0x0000, 0x002e, TYPE_VALID, ByteString.EMPTY),\n    )\n  }\n\n  @Test fun withSectionStartsSplits() {\n    assertThat(\n      withoutSectionSpans(\n        listOf(\n          Mapping(0x40000, 0x40180, TYPE_DISALLOWED, ByteString.EMPTY),\n        ),\n      ),\n    ).containsExactly(\n      Mapping(0x40000, 0x4007f, TYPE_DISALLOWED, ByteString.EMPTY),\n      Mapping(0x40080, 0x400ff, TYPE_DISALLOWED, ByteString.EMPTY),\n      Mapping(0x40100, 0x4017f, TYPE_DISALLOWED, ByteString.EMPTY),\n      Mapping(0x40180, 0x40180, TYPE_DISALLOWED, ByteString.EMPTY),\n    )\n  }\n\n  @Test fun withSectionStartAlreadySplit() {\n    assertThat(\n      withoutSectionSpans(\n        listOf(\n          Mapping(0x40000, 0x4007f, TYPE_DISALLOWED, ByteString.EMPTY),\n          Mapping(0x40080, 0x400ff, TYPE_DISALLOWED, ByteString.EMPTY),\n        ),\n      ),\n    ).containsExactly(\n      Mapping(0x40000, 0x4007f, TYPE_DISALLOWED, ByteString.EMPTY),\n      Mapping(0x40080, 0x400ff, TYPE_DISALLOWED, ByteString.EMPTY),\n    )\n  }\n\n  @Test fun mergeAdjacentDeltaMappedRangesWithMultipleDeltas() {\n    assertThat(\n      mergeAdjacentDeltaMappedRanges(\n        mutableListOf(\n          InlineDelta(1, 5),\n          InlineDelta(2, 5),\n          InlineDelta(3, 5),\n          MappedRange.External(4, \"a\".encodeUtf8()),\n        ),\n      ),\n    ).containsExactly(\n      InlineDelta(1, 5),\n      MappedRange.External(4, \"a\".encodeUtf8()),\n    )\n  }\n\n  @Test fun mergeAdjacentDeltaMappedRangesWithDifferentSizedDeltas() {\n    assertThat(\n      mergeAdjacentDeltaMappedRanges(\n        mutableListOf(\n          InlineDelta(1, 5),\n          InlineDelta(2, 5),\n          InlineDelta(3, 1),\n        ),\n      ),\n    ).containsExactly(\n      InlineDelta(1, 5),\n      InlineDelta(3, 1),\n    )\n  }\n\n  @Test fun inlineDeltaOrNullValid() {\n    assertThat(\n      inlineDeltaOrNull(\n        mappingOf(\n          sourceCodePoint0 = 1,\n          sourceCodePoint1 = 1,\n          mappedToCodePoints = listOf(2),\n        ),\n      ),\n    ).isEqualTo(InlineDelta(1, 1))\n\n    assertThat(\n      inlineDeltaOrNull(\n        mappingOf(\n          sourceCodePoint0 = 2,\n          sourceCodePoint1 = 2,\n          mappedToCodePoints = listOf(1),\n        ),\n      ),\n    ).isEqualTo(InlineDelta(2, -1))\n  }\n\n  @Test fun inlineDeltaOrNullMultipleSourceCodePoints() {\n    assertThat(\n      inlineDeltaOrNull(\n        mappingOf(\n          sourceCodePoint0 = 2,\n          sourceCodePoint1 = 3,\n          mappedToCodePoints = listOf(2),\n        ),\n      ),\n    ).isEqualTo(null)\n  }\n\n  @Test fun inlineDeltaOrNullMultipleMappedToCodePoints() {\n    assertThat(\n      inlineDeltaOrNull(\n        mappingOf(\n          sourceCodePoint0 = 1,\n          sourceCodePoint1 = 1,\n          mappedToCodePoints = listOf(2, 3),\n        ),\n      ),\n    ).isEqualTo(null)\n  }\n\n  @Test fun inlineDeltaOrNullMaxCodepointDelta() {\n    assertThat(\n      inlineDeltaOrNull(\n        mappingOf(\n          sourceCodePoint0 = 0,\n          sourceCodePoint1 = 0,\n          mappedToCodePoints = listOf((1 shl 18) - 1),\n        ),\n      ),\n    ).isEqualTo(\n      InlineDelta(\n        rangeStart = 0,\n        codepointDelta = InlineDelta.MAX_VALUE,\n      ),\n    )\n\n    assertThat(\n      inlineDeltaOrNull(\n        mappingOf(\n          sourceCodePoint0 = 0,\n          sourceCodePoint1 = 0,\n          mappedToCodePoints = listOf(1 shl 18),\n        ),\n      ),\n    ).isEqualTo(null)\n  }\n\n  private fun mappingOf(\n    sourceCodePoint0: Int,\n    sourceCodePoint1: Int,\n    mappedToCodePoints: List<Int>,\n  ): Mapping =\n    Mapping(\n      sourceCodePoint0 = sourceCodePoint0,\n      sourceCodePoint1 = sourceCodePoint1,\n      type = TYPE_MAPPED,\n      mappedTo =\n        Buffer()\n          .also {\n            for (cp in mappedToCodePoints) {\n              it.writeUtf8CodePoint(cp)\n            }\n          }.readByteString(),\n    )\n}\n"
  },
  {
    "path": "okhttp-java-net-cookiejar/README.md",
    "content": "OkHttp java.net.CookieHandler\n=============================\n\nThis module integrates OkHttp with `CookieHandler` from `java.net`.\nThis used to be part of `okhttp-urlconnection`\n\n### Download\n\n```kotlin\ntestImplementation(\"com.squareup.okhttp3:okhttp-java-net-cookiehandler:5.3.0\")\n```\n"
  },
  {
    "path": "okhttp-java-net-cookiejar/api/okhttp-java-net-cookiejar.api",
    "content": "public final class okhttp3/java/net/cookiejar/JavaNetCookieJar : okhttp3/CookieJar {\n\tpublic fun <init> (Ljava/net/CookieHandler;)V\n\tpublic fun loadForRequest (Lokhttp3/HttpUrl;)Ljava/util/List;\n\tpublic fun saveFromResponse (Lokhttp3/HttpUrl;Ljava/util/List;)V\n}\n\n"
  },
  {
    "path": "okhttp-java-net-cookiejar/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Export-Package: okhttp3.java.net.cookiejar\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.java.net.cookiejar\",\n)\n\nproject.applyJavaModules(\"okhttp3.java.net.cookiejar\")\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n  compileOnly(libs.animalsniffer.annotations)\n}\n"
  },
  {
    "path": "okhttp-java-net-cookiejar/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.java.net.cookiejar {\n  requires okhttp3;\n  exports okhttp3.java.net.cookiejar;\n}\n"
  },
  {
    "path": "okhttp-java-net-cookiejar/src/main/kotlin/okhttp3/java/net/cookiejar/JavaNetCookieJar.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.java.net.cookiejar\n\nimport java.io.IOException\nimport java.net.CookieHandler\nimport java.net.HttpCookie\nimport java.util.Collections\nimport okhttp3.Cookie\nimport okhttp3.CookieJar\nimport okhttp3.HttpUrl\nimport okhttp3.internal.cookieToString\nimport okhttp3.internal.delimiterOffset\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.Platform.Companion.WARN\nimport okhttp3.internal.trimSubstring\n\n/** A cookie jar that delegates to a [java.net.CookieHandler]. */\nclass JavaNetCookieJar(\n  private val cookieHandler: CookieHandler,\n) : CookieJar {\n  override fun saveFromResponse(\n    url: HttpUrl,\n    cookies: List<Cookie>,\n  ) {\n    val cookieStrings = mutableListOf<String>()\n    for (cookie in cookies) {\n      cookieStrings.add(cookieToString(cookie, true))\n    }\n    val multimap = mapOf(\"Set-Cookie\" to cookieStrings)\n    try {\n      cookieHandler.put(url.toUri(), multimap)\n    } catch (e: IOException) {\n      Platform.get().log(\"Saving cookies failed for \" + url.resolve(\"/...\")!!, WARN, e)\n    }\n  }\n\n  override fun loadForRequest(url: HttpUrl): List<Cookie> {\n    val cookieHeaders =\n      try {\n        // The RI passes all headers. We don't have 'em, so we don't pass 'em!\n        cookieHandler.get(url.toUri(), emptyMap<String, List<String>>())\n      } catch (e: IOException) {\n        Platform.get().log(\"Loading cookies failed for \" + url.resolve(\"/...\")!!, WARN, e)\n        return emptyList()\n      }\n\n    var cookies: MutableList<Cookie>? = null\n    for ((key, value) in cookieHeaders) {\n      if ((\"Cookie\".equals(key, ignoreCase = true) || \"Cookie2\".equals(key, ignoreCase = true)) &&\n        value.isNotEmpty()\n      ) {\n        for (header in value) {\n          if (cookies == null) cookies = mutableListOf()\n          cookies.addAll(decodeHeaderAsJavaNetCookies(url, header))\n        }\n      }\n    }\n\n    return if (cookies != null) {\n      Collections.unmodifiableList(cookies)\n    } else {\n      emptyList()\n    }\n  }\n\n  /**\n   * Convert a request header to OkHttp's cookies via [HttpCookie]. That extra step handles\n   * multiple cookies in a single request header, which [Cookie.parse] doesn't support.\n   */\n  private fun decodeHeaderAsJavaNetCookies(\n    url: HttpUrl,\n    header: String,\n  ): List<Cookie> {\n    val result = mutableListOf<Cookie>()\n    var pos = 0\n    val limit = header.length\n    var pairEnd: Int\n    while (pos < limit) {\n      pairEnd = header.delimiterOffset(\";,\", pos, limit)\n      val equalsSign = header.delimiterOffset('=', pos, pairEnd)\n      val name = header.trimSubstring(pos, equalsSign)\n      if (name.startsWith(\"$\")) {\n        pos = pairEnd + 1\n        continue\n      }\n\n      // We have either name=value or just a name.\n      var value =\n        if (equalsSign < pairEnd) {\n          header.trimSubstring(equalsSign + 1, pairEnd)\n        } else {\n          \"\"\n        }\n\n      // If the value is \"quoted\", drop the quotes.\n      if (value.startsWith(\"\\\"\") && value.endsWith(\"\\\"\") && value.length >= 2) {\n        value = value.substring(1, value.length - 1)\n      }\n\n      // Minimal normalisation so Cookie.Builder doesn't crash on values like \"abc123 \".\n      value = value.trim()\n\n      result.add(\n        Cookie\n          .Builder()\n          .name(name)\n          .value(value)\n          .domain(url.host)\n          .build(),\n      )\n      pos = pairEnd + 1\n    }\n    return result\n  }\n}\n"
  },
  {
    "path": "okhttp-logging-interceptor/Module.md",
    "content": "# Module okhttp-logging-interceptor\n\nAn OkHttp interceptor which logs HTTP request and response data.\n"
  },
  {
    "path": "okhttp-logging-interceptor/README.md",
    "content": "Logging Interceptor\n===================\n\nAn [OkHttp interceptor][interceptors] which logs HTTP request and response data.\n\n```java\nHttpLoggingInterceptor logging = new HttpLoggingInterceptor();\nlogging.setLevel(Level.BASIC);\nOkHttpClient client = new OkHttpClient.Builder()\n  .addInterceptor(logging)\n  .build();\n```\n\nYou can change the log level at any time by calling `setLevel()`.\n\nTo log to a custom location, pass a `Logger` instance to the constructor.\n```java\nHttpLoggingInterceptor logging = new HttpLoggingInterceptor(new Logger() {\n  @Override public void log(String message) {\n    Timber.tag(\"OkHttp\").d(message);\n  }\n});\n```\n\n**Warning**: The logs generated by this interceptor when using the `HEADERS` or `BODY` levels have\nthe potential to leak sensitive information such as \"Authorization\" or \"Cookie\" headers and the\ncontents of request and response bodies. This data should only be logged in a controlled way or in\na non-production environment.\n\nYou can redact headers that may contain sensitive information by calling `redactHeader()`.\n```java\nlogging.redactHeader(\"Authorization\");\nlogging.redactHeader(\"Cookie\");\n```\n\nDownload\n--------\n\n```kotlin\nimplementation(\"com.squareup.okhttp3:logging-interceptor:5.3.0\")\n```\n\n\n[interceptors]: https://square.github.io/okhttp/interceptors/\n"
  },
  {
    "path": "okhttp-logging-interceptor/api/logging-interceptor.api",
    "content": "public final class okhttp3/logging/HttpLoggingInterceptor : okhttp3/Interceptor {\n\tpublic static final field Companion Lokhttp3/logging/HttpLoggingInterceptor$Companion;\n\tpublic final fun -deprecated_level ()Lokhttp3/logging/HttpLoggingInterceptor$Level;\n\tpublic fun <init> ()V\n\tpublic fun <init> (Lokhttp3/logging/HttpLoggingInterceptor$Logger;)V\n\tpublic synthetic fun <init> (Lokhttp3/logging/HttpLoggingInterceptor$Logger;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun getLevel ()Lokhttp3/logging/HttpLoggingInterceptor$Level;\n\tpublic fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response;\n\tpublic final fun level (Lokhttp3/logging/HttpLoggingInterceptor$Level;)V\n\tpublic final fun redactHeader (Ljava/lang/String;)V\n\tpublic final fun redactQueryParams ([Ljava/lang/String;)V\n\tpublic final fun setLevel (Lokhttp3/logging/HttpLoggingInterceptor$Level;)Lokhttp3/logging/HttpLoggingInterceptor;\n}\n\npublic final class okhttp3/logging/HttpLoggingInterceptor$Companion {\n}\n\npublic final class okhttp3/logging/HttpLoggingInterceptor$Level : java/lang/Enum {\n\tpublic static final field BASIC Lokhttp3/logging/HttpLoggingInterceptor$Level;\n\tpublic static final field BODY Lokhttp3/logging/HttpLoggingInterceptor$Level;\n\tpublic static final field HEADERS Lokhttp3/logging/HttpLoggingInterceptor$Level;\n\tpublic static final field NONE Lokhttp3/logging/HttpLoggingInterceptor$Level;\n\tpublic static fun getEntries ()Lkotlin/enums/EnumEntries;\n\tpublic static fun valueOf (Ljava/lang/String;)Lokhttp3/logging/HttpLoggingInterceptor$Level;\n\tpublic static fun values ()[Lokhttp3/logging/HttpLoggingInterceptor$Level;\n}\n\npublic abstract interface class okhttp3/logging/HttpLoggingInterceptor$Logger {\n\tpublic static final field Companion Lokhttp3/logging/HttpLoggingInterceptor$Logger$Companion;\n\tpublic static final field DEFAULT Lokhttp3/logging/HttpLoggingInterceptor$Logger;\n\tpublic abstract fun log (Ljava/lang/String;)V\n}\n\npublic final class okhttp3/logging/HttpLoggingInterceptor$Logger$Companion {\n}\n\npublic final class okhttp3/logging/LoggingEventListener : okhttp3/EventListener {\n\tpublic static final field Companion Lokhttp3/logging/LoggingEventListener$Companion;\n\tpublic synthetic fun <init> (Lokhttp3/logging/HttpLoggingInterceptor$Logger;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic fun cacheConditionalHit (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun cacheHit (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun cacheMiss (Lokhttp3/Call;)V\n\tpublic fun callEnd (Lokhttp3/Call;)V\n\tpublic fun callFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun callStart (Lokhttp3/Call;)V\n\tpublic fun canceled (Lokhttp3/Call;)V\n\tpublic fun connectEnd (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;)V\n\tpublic fun connectFailed (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;Ljava/io/IOException;)V\n\tpublic fun connectStart (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;)V\n\tpublic fun connectionAcquired (Lokhttp3/Call;Lokhttp3/Connection;)V\n\tpublic fun connectionReleased (Lokhttp3/Call;Lokhttp3/Connection;)V\n\tpublic fun dispatcherQueueEnd (Lokhttp3/Call;Lokhttp3/Dispatcher;)V\n\tpublic fun dispatcherQueueStart (Lokhttp3/Call;Lokhttp3/Dispatcher;)V\n\tpublic fun dnsEnd (Lokhttp3/Call;Ljava/lang/String;Ljava/util/List;)V\n\tpublic fun dnsStart (Lokhttp3/Call;Ljava/lang/String;)V\n\tpublic fun proxySelectEnd (Lokhttp3/Call;Lokhttp3/HttpUrl;Ljava/util/List;)V\n\tpublic fun proxySelectStart (Lokhttp3/Call;Lokhttp3/HttpUrl;)V\n\tpublic fun requestBodyEnd (Lokhttp3/Call;J)V\n\tpublic fun requestBodyStart (Lokhttp3/Call;)V\n\tpublic fun requestFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun requestHeadersEnd (Lokhttp3/Call;Lokhttp3/Request;)V\n\tpublic fun requestHeadersStart (Lokhttp3/Call;)V\n\tpublic fun responseBodyEnd (Lokhttp3/Call;J)V\n\tpublic fun responseBodyStart (Lokhttp3/Call;)V\n\tpublic fun responseFailed (Lokhttp3/Call;Ljava/io/IOException;)V\n\tpublic fun responseHeadersEnd (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun responseHeadersStart (Lokhttp3/Call;)V\n\tpublic fun satisfactionFailure (Lokhttp3/Call;Lokhttp3/Response;)V\n\tpublic fun secureConnectEnd (Lokhttp3/Call;Lokhttp3/Handshake;)V\n\tpublic fun secureConnectStart (Lokhttp3/Call;)V\n}\n\npublic final class okhttp3/logging/LoggingEventListener$Companion {\n}\n\npublic class okhttp3/logging/LoggingEventListener$Factory : okhttp3/EventListener$Factory {\n\tpublic fun <init> ()V\n\tpublic fun <init> (Lokhttp3/logging/HttpLoggingInterceptor$Logger;)V\n\tpublic synthetic fun <init> (Lokhttp3/logging/HttpLoggingInterceptor$Logger;ILkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic fun create (Lokhttp3/Call;)Lokhttp3/EventListener;\n}\n\n"
  },
  {
    "path": "okhttp-logging-interceptor/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Export-Package: okhttp3.logging\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.logging\",\n)\n\nproject.applyJavaModules(\"okhttp3.logging\")\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n\n  testImplementation(libs.junit)\n  testImplementation(projects.mockwebserver3)\n  testImplementation(projects.mockwebserver3Junit5)\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(projects.okhttpTls)\n  testImplementation(libs.assertk)\n}\n"
  },
  {
    "path": "okhttp-logging-interceptor/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.logging {\n  requires okhttp3;\n  exports okhttp3.logging;\n}\n"
  },
  {
    "path": "okhttp-logging-interceptor/src/main/kotlin/okhttp3/logging/HttpLoggingInterceptor.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.logging\n\nimport java.io.IOException\nimport java.nio.charset.Charset\nimport java.util.TreeSet\nimport java.util.concurrent.TimeUnit\nimport okhttp3.Headers\nimport okhttp3.HttpUrl\nimport okhttp3.Interceptor\nimport okhttp3.OkHttpClient\nimport okhttp3.Response\nimport okhttp3.internal.UnreadableResponseBody\nimport okhttp3.internal.charsetOrUtf8\nimport okhttp3.internal.http.promisesBody\nimport okhttp3.internal.isProbablyUtf8\nimport okhttp3.internal.platform.Platform\nimport okio.Buffer\nimport okio.GzipSource\n\n/**\n * An OkHttp interceptor which logs request and response information. Can be applied as an\n * [application interceptor][OkHttpClient.interceptors] or as a [OkHttpClient.networkInterceptors].\n *\n * The format of the logs created by this class should not be considered stable and may\n * change slightly between releases. If you need a stable logging format, use your own interceptor.\n */\nclass HttpLoggingInterceptor\n  @JvmOverloads\n  constructor(\n    private val logger: Logger = Logger.DEFAULT,\n  ) : Interceptor {\n    @Volatile private var headersToRedact = emptySet<String>()\n\n    @Volatile private var queryParamsNameToRedact = emptySet<String>()\n\n    @set:JvmName(\"level\")\n    @Volatile\n    var level = Level.NONE\n\n    enum class Level {\n      /** No logs. */\n      NONE,\n\n      /**\n       * Logs request and response lines.\n       *\n       * Example:\n       * ```\n       * --> POST /greeting http/1.1 (3-byte body)\n       *\n       * <-- 200 OK (22ms, 6-byte body)\n       * ```\n       */\n      BASIC,\n\n      /**\n       * Logs request and response lines and their respective headers.\n       *\n       * Example:\n       * ```\n       * --> POST /greeting http/1.1\n       * Host: example.com\n       * Content-Type: plain/text\n       * Content-Length: 3\n       * --> END POST\n       *\n       * <-- 200 OK (22ms)\n       * Content-Type: plain/text\n       * Content-Length: 6\n       * <-- END HTTP\n       * ```\n       */\n      HEADERS,\n\n      /**\n       * Logs request and response lines and their respective headers and bodies (if present).\n       *\n       * Example:\n       * ```\n       * --> POST /greeting http/1.1\n       * Host: example.com\n       * Content-Type: plain/text\n       * Content-Length: 3\n       *\n       * Hi?\n       * --> END POST\n       *\n       * <-- 200 OK (22ms)\n       * Content-Type: plain/text\n       * Content-Length: 6\n       *\n       * Hello!\n       * <-- END HTTP\n       * ```\n       */\n      BODY,\n    }\n\n    fun interface Logger {\n      fun log(message: String)\n\n      companion object {\n        /** A [Logger] defaults output appropriate for the current platform. */\n        @JvmField\n        val DEFAULT: Logger = DefaultLogger()\n\n        private class DefaultLogger : Logger {\n          override fun log(message: String) {\n            Platform.get().log(message)\n          }\n        }\n      }\n    }\n\n    fun redactHeader(name: String) {\n      val newHeadersToRedact = TreeSet(String.CASE_INSENSITIVE_ORDER)\n      newHeadersToRedact += headersToRedact\n      newHeadersToRedact += name\n      headersToRedact = newHeadersToRedact\n    }\n\n    fun redactQueryParams(vararg name: String) {\n      val newQueryParamsNameToRedact = TreeSet(String.CASE_INSENSITIVE_ORDER)\n      newQueryParamsNameToRedact += queryParamsNameToRedact\n      newQueryParamsNameToRedact.addAll(name)\n      queryParamsNameToRedact = newQueryParamsNameToRedact\n    }\n\n    /**\n     * Sets the level and returns this.\n     *\n     * This was deprecated in OkHttp 4.0 in favor of the [level] val. In OkHttp 4.3 it is\n     * un-deprecated because Java callers can't chain when assigning Kotlin vals. (The getter remains\n     * deprecated).\n     */\n    fun setLevel(level: Level) =\n      apply {\n        this.level = level\n      }\n\n    @JvmName(\"-deprecated_level\")\n    @Deprecated(\n      message = \"moved to var\",\n      replaceWith = ReplaceWith(expression = \"level\"),\n      level = DeprecationLevel.ERROR,\n    )\n    fun getLevel(): Level = level\n\n    @Throws(IOException::class)\n    override fun intercept(chain: Interceptor.Chain): Response {\n      val level = this.level\n\n      val request = chain.request()\n      if (level == Level.NONE) {\n        return chain.proceed(request)\n      }\n\n      val logBody = level == Level.BODY\n      val logHeaders = logBody || level == Level.HEADERS\n\n      val requestBody = request.body\n\n      val connection = chain.connection()\n      var requestStartMessage =\n        (\"--> ${request.method} ${redactUrl(request.url)}${if (connection != null) \" \" + connection.protocol() else \"\"}\")\n      if (!logHeaders && requestBody != null) {\n        requestStartMessage += \" (${requestBody.contentLength()}-byte body)\"\n      }\n      logger.log(requestStartMessage)\n\n      if (logHeaders) {\n        val headers = request.headers\n\n        if (requestBody != null) {\n          // Request body headers are only present when installed as a network interceptor. When not\n          // already present, force them to be included (if available) so their values are known.\n          requestBody.contentType()?.let {\n            if (headers[\"Content-Type\"] == null) {\n              logger.log(\"Content-Type: $it\")\n            }\n          }\n          if (requestBody.contentLength() != -1L) {\n            if (headers[\"Content-Length\"] == null) {\n              logger.log(\"Content-Length: ${requestBody.contentLength()}\")\n            }\n          }\n        }\n\n        for (i in 0 until headers.size) {\n          logHeader(headers, i)\n        }\n\n        if (!logBody || requestBody == null) {\n          logger.log(\"--> END ${request.method}\")\n        } else if (bodyHasUnknownEncoding(request.headers)) {\n          logger.log(\"--> END ${request.method} (encoded body omitted)\")\n        } else if (requestBody.isDuplex()) {\n          logger.log(\"--> END ${request.method} (duplex request body omitted)\")\n        } else if (requestBody.isOneShot()) {\n          logger.log(\"--> END ${request.method} (one-shot body omitted)\")\n        } else {\n          var buffer = Buffer()\n          requestBody.writeTo(buffer)\n\n          var gzippedLength: Long? = null\n          if (\"gzip\".equals(headers[\"Content-Encoding\"], ignoreCase = true)) {\n            gzippedLength = buffer.size\n            GzipSource(buffer).use { gzippedResponseBody ->\n              buffer = Buffer()\n              buffer.writeAll(gzippedResponseBody)\n            }\n          }\n\n          val charset: Charset = requestBody.contentType().charsetOrUtf8()\n\n          logger.log(\"\")\n          if (!buffer.isProbablyUtf8(16L)) {\n            logger.log(\n              \"--> END ${request.method} (binary ${requestBody.contentLength()}-byte body omitted)\",\n            )\n          } else if (gzippedLength != null) {\n            logger.log(\"--> END ${request.method} (${buffer.size}-byte, $gzippedLength-gzipped-byte body)\")\n          } else {\n            logger.log(buffer.readString(charset))\n            logger.log(\"--> END ${request.method} (${requestBody.contentLength()}-byte body)\")\n          }\n        }\n      }\n\n      val startNs = System.nanoTime()\n      val response: Response\n      try {\n        response = chain.proceed(request)\n      } catch (e: Exception) {\n        val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)\n        logger.log(\n          buildString {\n            append(\"<-- HTTP FAILED: $e.\")\n            append(\" ${redactUrl(request.url)} (${tookMs}ms)\")\n          },\n        )\n        throw e\n      }\n\n      val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)\n\n      val responseBody = response.body!!\n      val contentLength = responseBody.contentLength()\n      val bodySize = if (contentLength != -1L) \"$contentLength-byte\" else \"unknown-length\"\n      logger.log(\n        buildString {\n          append(\"<-- ${response.code}\")\n          if (response.message.isNotEmpty()) append(\" ${response.message}\")\n          append(\" ${redactUrl(response.request.url)} (${tookMs}ms\")\n          if (!logHeaders) append(\", $bodySize body\")\n          append(\")\")\n        },\n      )\n\n      if (logHeaders) {\n        val headers = response.headers\n        for (i in 0 until headers.size) {\n          logHeader(headers, i)\n        }\n\n        if (!logBody || !response.promisesBody()) {\n          logger.log(\"<-- END HTTP\")\n        } else if (bodyHasUnknownEncoding(response.headers)) {\n          logger.log(\"<-- END HTTP (encoded body omitted)\")\n        } else if (bodyIsStreaming(response)) {\n          logger.log(\"<-- END HTTP (streaming)\")\n        } else if (responseBody is UnreadableResponseBody) {\n          logger.log(\"<-- END HTTP (unreadable body)\")\n        } else {\n          val source = responseBody.source()\n          source.request(Long.MAX_VALUE) // Buffer the entire body.\n\n          val totalMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)\n\n          var buffer = source.buffer\n\n          var gzippedLength: Long? = null\n          if (\"gzip\".equals(headers[\"Content-Encoding\"], ignoreCase = true)) {\n            gzippedLength = buffer.size\n            GzipSource(buffer.clone()).use { gzippedResponseBody ->\n              buffer = Buffer()\n              buffer.writeAll(gzippedResponseBody)\n            }\n          }\n\n          val charset: Charset = responseBody.contentType().charsetOrUtf8()\n\n          if (!buffer.isProbablyUtf8(16L)) {\n            logger.log(\"\")\n            logger.log(\"<-- END HTTP (${totalMs}ms, binary ${buffer.size}-byte body omitted)\")\n            return response\n          }\n\n          if (contentLength != 0L) {\n            logger.log(\"\")\n            logger.log(buffer.clone().readString(charset))\n          }\n\n          logger.log(\n            buildString {\n              append(\"<-- END HTTP (${totalMs}ms, ${buffer.size}-byte\")\n              if (gzippedLength != null) append(\", $gzippedLength-gzipped-byte\")\n              append(\" body)\")\n            },\n          )\n        }\n      }\n\n      return response\n    }\n\n    internal fun redactUrl(url: HttpUrl): String {\n      if (queryParamsNameToRedact.isEmpty() || url.querySize == 0) {\n        return url.toString()\n      }\n      return url\n        .newBuilder()\n        .query(null)\n        .apply {\n          for (i in 0 until url.querySize) {\n            val parameterName = url.queryParameterName(i)\n            val newValue = if (parameterName in queryParamsNameToRedact) \"██\" else url.queryParameterValue(i)\n\n            addEncodedQueryParameter(parameterName, newValue)\n          }\n        }.toString()\n    }\n\n    private fun logHeader(\n      headers: Headers,\n      i: Int,\n    ) {\n      val value = if (headers.name(i) in headersToRedact) \"██\" else headers.value(i)\n      logger.log(headers.name(i) + \": \" + value)\n    }\n\n    private fun bodyHasUnknownEncoding(headers: Headers): Boolean {\n      val contentEncoding = headers[\"Content-Encoding\"] ?: return false\n      return !contentEncoding.equals(\"identity\", ignoreCase = true) &&\n        !contentEncoding.equals(\"gzip\", ignoreCase = true)\n    }\n\n    private fun bodyIsStreaming(response: Response): Boolean {\n      val contentType = response.body.contentType()\n      return contentType != null && contentType.type == \"text\" && contentType.subtype == \"event-stream\"\n    }\n\n    companion object\n  }\n"
  },
  {
    "path": "okhttp-logging-interceptor/src/main/kotlin/okhttp3/logging/LoggingEventListener.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.logging\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.util.concurrent.TimeUnit\nimport okhttp3.Call\nimport okhttp3.Connection\nimport okhttp3.Dispatcher\nimport okhttp3.EventListener\nimport okhttp3.Handshake\nimport okhttp3.HttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\n\n/**\n * An OkHttp EventListener, which logs call events. Can be applied as an\n * [event listener factory][OkHttpClient.eventListenerFactory].\n *\n * The format of the logs created by this class should not be considered stable and may change\n * slightly between releases. If you need a stable logging format, use your own event listener.\n */\nclass LoggingEventListener private constructor(\n  private val logger: HttpLoggingInterceptor.Logger,\n) : EventListener() {\n  private var startNs: Long = 0\n\n  override fun callStart(call: Call) {\n    startNs = System.nanoTime()\n\n    logWithTime(\"callStart: ${call.request()}\")\n  }\n\n  override fun dispatcherQueueStart(\n    call: Call,\n    dispatcher: Dispatcher,\n  ) {\n    logWithTime(\"dispatcherQueueStart: $call queuedCallsCount=${dispatcher.queuedCallsCount()}\")\n  }\n\n  override fun dispatcherQueueEnd(\n    call: Call,\n    dispatcher: Dispatcher,\n  ) {\n    logWithTime(\"dispatcherQueueEnd: $call queuedCallsCount=${dispatcher.queuedCallsCount()}\")\n  }\n\n  override fun proxySelectStart(\n    call: Call,\n    url: HttpUrl,\n  ) {\n    logWithTime(\"proxySelectStart: $url\")\n  }\n\n  override fun proxySelectEnd(\n    call: Call,\n    url: HttpUrl,\n    proxies: List<Proxy>,\n  ) {\n    logWithTime(\"proxySelectEnd: $proxies\")\n  }\n\n  override fun dnsStart(\n    call: Call,\n    domainName: String,\n  ) {\n    logWithTime(\"dnsStart: $domainName\")\n  }\n\n  override fun dnsEnd(\n    call: Call,\n    domainName: String,\n    inetAddressList: List<InetAddress>,\n  ) {\n    logWithTime(\"dnsEnd: $inetAddressList\")\n  }\n\n  override fun connectStart(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n  ) {\n    logWithTime(\"connectStart: $inetSocketAddress $proxy\")\n  }\n\n  override fun secureConnectStart(call: Call) {\n    logWithTime(\"secureConnectStart\")\n  }\n\n  override fun secureConnectEnd(\n    call: Call,\n    handshake: Handshake?,\n  ) {\n    logWithTime(\"secureConnectEnd: $handshake\")\n  }\n\n  override fun connectEnd(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n    protocol: Protocol?,\n  ) {\n    logWithTime(\"connectEnd: $protocol\")\n  }\n\n  override fun connectFailed(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n    protocol: Protocol?,\n    ioe: IOException,\n  ) {\n    logWithTime(\"connectFailed: $protocol $ioe\")\n  }\n\n  override fun connectionAcquired(\n    call: Call,\n    connection: Connection,\n  ) {\n    logWithTime(\"connectionAcquired: $connection\")\n  }\n\n  override fun connectionReleased(\n    call: Call,\n    connection: Connection,\n  ) {\n    logWithTime(\"connectionReleased\")\n  }\n\n  override fun requestHeadersStart(call: Call) {\n    logWithTime(\"requestHeadersStart\")\n  }\n\n  override fun requestHeadersEnd(\n    call: Call,\n    request: Request,\n  ) {\n    logWithTime(\"requestHeadersEnd\")\n  }\n\n  override fun requestBodyStart(call: Call) {\n    logWithTime(\"requestBodyStart\")\n  }\n\n  override fun requestBodyEnd(\n    call: Call,\n    byteCount: Long,\n  ) {\n    logWithTime(\"requestBodyEnd: byteCount=$byteCount\")\n  }\n\n  override fun requestFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n    logWithTime(\"requestFailed: $ioe\")\n  }\n\n  override fun responseHeadersStart(call: Call) {\n    logWithTime(\"responseHeadersStart\")\n  }\n\n  override fun responseHeadersEnd(\n    call: Call,\n    response: Response,\n  ) {\n    logWithTime(\"responseHeadersEnd: $response\")\n  }\n\n  override fun responseBodyStart(call: Call) {\n    logWithTime(\"responseBodyStart\")\n  }\n\n  override fun responseBodyEnd(\n    call: Call,\n    byteCount: Long,\n  ) {\n    logWithTime(\"responseBodyEnd: byteCount=$byteCount\")\n  }\n\n  override fun responseFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n    logWithTime(\"responseFailed: $ioe\")\n  }\n\n  override fun callEnd(call: Call) {\n    logWithTime(\"callEnd\")\n  }\n\n  override fun callFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n    logWithTime(\"callFailed: $ioe\")\n  }\n\n  override fun canceled(call: Call) {\n    logWithTime(\"canceled\")\n  }\n\n  override fun satisfactionFailure(\n    call: Call,\n    response: Response,\n  ) {\n    logWithTime(\"satisfactionFailure: $response\")\n  }\n\n  override fun cacheHit(\n    call: Call,\n    response: Response,\n  ) {\n    logWithTime(\"cacheHit: $response\")\n  }\n\n  override fun cacheMiss(call: Call) {\n    logWithTime(\"cacheMiss\")\n  }\n\n  override fun cacheConditionalHit(\n    call: Call,\n    cachedResponse: Response,\n  ) {\n    logWithTime(\"cacheConditionalHit: $cachedResponse\")\n  }\n\n  private fun logWithTime(message: String) {\n    val timeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)\n    logger.log(\"[$timeMs ms] $message\")\n  }\n\n  open class Factory\n    @JvmOverloads\n    constructor(\n      private val logger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger.DEFAULT,\n    ) : EventListener.Factory {\n      override fun create(call: Call): EventListener = LoggingEventListener(logger)\n    }\n\n  companion object\n}\n"
  },
  {
    "path": "okhttp-logging-interceptor/src/test/java/okhttp3/logging/HttpLoggingInterceptorTest.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.logging\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isLessThan\nimport assertk.assertions.isLessThanOrEqualTo\nimport assertk.assertions.isSameInstanceAs\nimport assertk.assertions.matches\nimport java.net.UnknownHostException\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.HttpUrl\nimport okhttp3.Interceptor\nimport okhttp3.MediaType\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Protocol\nimport okhttp3.RecordingHostnameVerifier\nimport okhttp3.Request\nimport okhttp3.RequestBody\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.WebSocketListener\nimport okhttp3.logging.HttpLoggingInterceptor.Level\nimport okhttp3.testing.PlatformRule\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.ByteString.Companion.decodeBase64\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.Assumptions\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass HttpLoggingInterceptorTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private val hostnameVerifier = RecordingHostnameVerifier()\n  private lateinit var client: OkHttpClient\n  private lateinit var host: String\n  private lateinit var url: HttpUrl\n  private val networkLogs = LogRecorder()\n  private val networkInterceptor = HttpLoggingInterceptor(networkLogs)\n  private val applicationLogs = LogRecorder()\n  private val applicationInterceptor = HttpLoggingInterceptor(applicationLogs)\n  private var extraNetworkInterceptor: Interceptor? = null\n\n  private fun setLevel(level: Level) {\n    networkInterceptor.setLevel(level)\n    applicationInterceptor.setLevel(level)\n  }\n\n  @BeforeEach\n  fun setUp() {\n    client =\n      OkHttpClient\n        .Builder()\n        .addNetworkInterceptor(\n          Interceptor { chain ->\n            when {\n              extraNetworkInterceptor != null -> extraNetworkInterceptor!!.intercept(chain)\n              else -> chain.proceed(chain.request())\n            }\n          },\n        ).addNetworkInterceptor(networkInterceptor)\n        .addInterceptor(applicationInterceptor)\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).hostnameVerifier(hostnameVerifier)\n        .build()\n    host = \"${server.hostName}:${server.port}\"\n    url = server.url(\"/\")\n  }\n\n  @Test\n  fun levelGetter() {\n    // The default is NONE.\n    assertThat(applicationInterceptor.level).isEqualTo(Level.NONE)\n    for (level in Level.entries) {\n      applicationInterceptor.setLevel(level)\n      assertThat(applicationInterceptor.level).isEqualTo(level)\n    }\n  }\n\n  @Test\n  fun setLevelShouldReturnSameInstanceOfInterceptor() {\n    for (level in Level.entries) {\n      assertThat(applicationInterceptor.setLevel(level)).isSameInstanceAs(applicationInterceptor)\n    }\n  }\n\n  @Test\n  fun none() {\n    server.enqueue(MockResponse())\n    client.newCall(request().build()).execute()\n    applicationLogs.assertNoMoreLogs()\n    networkLogs.assertNoMoreLogs()\n  }\n\n  @Test\n  fun basicGet() {\n    setLevel(Level.BASIC)\n    server.enqueue(MockResponse())\n    client.newCall(request().build()).execute()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun basicPost() {\n    setLevel(Level.BASIC)\n    server.enqueue(MockResponse())\n    client.newCall(request().post(\"Hi?\".toRequestBody(PLAIN)).build()).execute()\n    applicationLogs\n      .assertLogEqual(\"--> POST $url (3-byte body)\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> POST $url http/1.1 (3-byte body)\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun basicResponseBody() {\n    setLevel(Level.BASIC)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Hello!\")\n        .setHeader(\"Content-Type\", PLAIN)\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms, 6-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms, 6-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun basicChunkedResponseBody() {\n    setLevel(Level.BASIC)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .chunkedBody(\"Hello!\", 2)\n        .setHeader(\"Content-Type\", PLAIN)\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms, unknown-length body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms, unknown-length body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun headersGet() {\n    setLevel(Level.HEADERS)\n    server.enqueue(MockResponse())\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun headersPost() {\n    setLevel(Level.HEADERS)\n    server.enqueue(MockResponse())\n    val request = request().post(\"Hi?\".toRequestBody(PLAIN)).build()\n    val response = client.newCall(request).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> POST $url\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"Content-Length: 3\")\n      .assertLogEqual(\"--> END POST\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> POST $url http/1.1\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"Content-Length: 3\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END POST\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun headersPostNoContentType() {\n    setLevel(Level.HEADERS)\n    server.enqueue(MockResponse())\n    val request = request().post(\"Hi?\".toRequestBody(null)).build()\n    val response = client.newCall(request).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> POST $url\")\n      .assertLogEqual(\"Content-Length: 3\")\n      .assertLogEqual(\"--> END POST\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> POST $url http/1.1\")\n      .assertLogEqual(\"Content-Length: 3\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END POST\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun headersPostNoLength() {\n    setLevel(Level.HEADERS)\n    server.enqueue(MockResponse())\n    val body: RequestBody =\n      object : RequestBody() {\n        override fun contentType() = PLAIN\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.writeUtf8(\"Hi!\")\n        }\n      }\n    val response = client.newCall(request().post(body).build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> POST $url\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"--> END POST\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> POST $url http/1.1\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"Transfer-Encoding: chunked\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END POST\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun headersPostWithHeaderOverrides() {\n    setLevel(Level.HEADERS)\n    extraNetworkInterceptor =\n      Interceptor { chain: Interceptor.Chain ->\n        chain.proceed(\n          chain\n            .request()\n            .newBuilder()\n            .header(\"Content-Length\", \"2\")\n            .header(\"Content-Type\", \"text/plain-ish\")\n            .build(),\n        )\n      }\n    server.enqueue(MockResponse())\n    client\n      .newCall(\n        request()\n          .post(\"Hi?\".toRequestBody(PLAIN))\n          .build(),\n      ).execute()\n    applicationLogs\n      .assertLogEqual(\"--> POST $url\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"Content-Length: 3\")\n      .assertLogEqual(\"--> END POST\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> POST $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"Content-Length: 2\")\n      .assertLogEqual(\"Content-Type: text/plain-ish\")\n      .assertLogEqual(\"--> END POST\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun headersResponseBody() {\n    setLevel(Level.HEADERS)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Hello!\")\n        .setHeader(\"Content-Type\", PLAIN)\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 6\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 6\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyGet() {\n    setLevel(Level.BODY)\n    server.enqueue(MockResponse())\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyGet204() {\n    setLevel(Level.BODY)\n    bodyGetNoBody(204)\n  }\n\n  @Test\n  fun bodyGet205() {\n    setLevel(Level.BODY)\n    bodyGetNoBody(205)\n  }\n\n  private fun bodyGetNoBody(code: Int) {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .status(\"HTTP/1.1 $code No Content\")\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- $code No Content $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- $code No Content $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyPost() {\n    setLevel(Level.BODY)\n    server.enqueue(MockResponse())\n    val request = request().post(\"Hi?\".toRequestBody(PLAIN)).build()\n    val response = client.newCall(request).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> POST $url\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"Content-Length: 3\")\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hi?\")\n      .assertLogEqual(\"--> END POST (3-byte body)\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> POST $url http/1.1\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"Content-Length: 3\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hi?\")\n      .assertLogEqual(\"--> END POST (3-byte body)\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyResponseBody() {\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Hello!\")\n        .setHeader(\"Content-Type\", PLAIN)\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 6\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hello!\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 6-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 6\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hello!\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 6-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyResponseBodyChunked() {\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .chunkedBody(\"Hello!\", 2)\n        .setHeader(\"Content-Type\", PLAIN)\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Transfer-encoding: chunked\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hello!\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 6-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Transfer-encoding: chunked\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hello!\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 6-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyRequestGzipEncoded() {\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .setHeader(\"Content-Type\", PLAIN)\n        .body(Buffer().writeUtf8(\"Uncompressed\"))\n        .build(),\n    )\n    val response =\n      client\n        .newCall(\n          request()\n            .post(\"Uncompressed\".toRequestBody())\n            .gzip()\n            .build(),\n        ).execute()\n    val responseBody = response.body\n    assertThat(responseBody.string(), \"Expected response body to be valid\")\n      .isEqualTo(\"Uncompressed\")\n    responseBody.close()\n    networkLogs\n      .assertLogEqual(\"--> POST $url http/1.1\")\n      .assertLogEqual(\"Content-Encoding: gzip\")\n      .assertLogEqual(\"Transfer-Encoding: chunked\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"--> END POST (12-byte, 32-gzipped-byte body)\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogMatch(Regex(\"\"\"Content-Length: \\d+\"\"\"))\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Uncompressed\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 12-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyResponseGzipEncoded() {\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .setHeader(\"Content-Encoding\", \"gzip\")\n        .setHeader(\"Content-Type\", PLAIN)\n        .body(Buffer().write(\"H4sIAAAAAAAAAPNIzcnJ11HwQKIAdyO+9hMAAAA=\".decodeBase64()!!))\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    val responseBody = response.body\n    assertThat(responseBody.string(), \"Expected response body to be valid\")\n      .isEqualTo(\"Hello, Hello, Hello\")\n    responseBody.close()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Encoding: gzip\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogMatch(Regex(\"\"\"Content-Length: \\d+\"\"\"))\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hello, Hello, Hello\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 19-byte, 29-gzipped-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hello, Hello, Hello\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 19-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyResponseUnknownEncoded() {\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder() // It's invalid to return this if not requested, but the server might anyway\n        .setHeader(\"Content-Encoding\", \"br\")\n        .setHeader(\"Content-Type\", PLAIN)\n        .body(Buffer().write(\"iwmASGVsbG8sIEhlbGxvLCBIZWxsbwoD\".decodeBase64()!!))\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Encoding: br\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogMatch(Regex(\"\"\"Content-Length: \\d+\"\"\"))\n      .assertLogEqual(\"<-- END HTTP (encoded body omitted)\")\n      .assertNoMoreLogs()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Encoding: br\")\n      .assertLogEqual(\"Content-Type: text/plain; charset=utf-8\")\n      .assertLogMatch(Regex(\"\"\"Content-Length: \\d+\"\"\"))\n      .assertLogEqual(\"<-- END HTTP (encoded body omitted)\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyResponseIsStreaming() {\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .setHeader(\"Content-Type\", \"text/event-stream\")\n        .chunkedBody(\n          \"\"\"\n          |event: add\n          |data: 73857293\n          |\n          |event: remove\n          |data: 2153\n          |\n          |event: add\n          |data: 113411\n          |\n          |\n          \"\"\".trimMargin(),\n          8,\n        ).build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Type: text/event-stream\")\n      .assertLogMatch(Regex(\"\"\"Transfer-encoding: chunked\"\"\"))\n      .assertLogEqual(\"<-- END HTTP (streaming)\")\n      .assertNoMoreLogs()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Type: text/event-stream\")\n      .assertLogMatch(Regex(\"\"\"Transfer-encoding: chunked\"\"\"))\n      .assertLogEqual(\"<-- END HTTP (streaming)\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyResponseIsUnreadable() {\n    setLevel(Level.BODY)\n    val serverListener = object : WebSocketListener() {}\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .webSocketUpgrade(serverListener)\n        .build(),\n    )\n    val response =\n      client\n        .newCall(\n          request()\n            .header(\"Connection\", \"Upgrade\")\n            .header(\"Upgrade\", \"websocket\")\n            .header(\"Sec-WebSocket-Key\", \"abc123\")\n            .build(),\n        ).execute()\n    response.body.close()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Connection: Upgrade\")\n      .assertLogEqual(\"Upgrade: websocket\")\n      .assertLogEqual(\"Sec-WebSocket-Key: abc123\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 101 Switching Protocols $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"Connection: Upgrade\")\n      .assertLogEqual(\"Upgrade: websocket\")\n      .assertLogMatch(Regex(\"\"\"Sec-WebSocket-Accept: .+\"\"\"))\n      .assertLogEqual(\"<-- END HTTP (unreadable body)\")\n      .assertNoMoreLogs()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"Connection: Upgrade\")\n      .assertLogEqual(\"Upgrade: websocket\")\n      .assertLogEqual(\"Sec-WebSocket-Key: abc123\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 101 Switching Protocols $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"Connection: Upgrade\")\n      .assertLogEqual(\"Upgrade: websocket\")\n      .assertLogMatch(Regex(\"\"\"Sec-WebSocket-Accept: .+\"\"\"))\n      .assertLogEqual(\"<-- END HTTP (unreadable body)\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun bodyGetMalformedCharset() {\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .setHeader(\"Content-Type\", \"text/html; charset=0\")\n        .body(\"Body with unknown charset\")\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Type: text/html; charset=0\")\n      .assertLogMatch(Regex(\"\"\"Content-Length: \\d+\"\"\"))\n      .assertLogMatch(Regex(\"\"))\n      .assertLogEqual(\"Body with unknown charset\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 25-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Type: text/html; charset=0\")\n      .assertLogMatch(Regex(\"\"\"Content-Length: \\d+\"\"\"))\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Body with unknown charset\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 25-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun responseBodyIsBinary() {\n    setLevel(Level.BODY)\n    val buffer = Buffer()\n    buffer.writeUtf8CodePoint(0x89)\n    buffer.writeUtf8CodePoint(0x50)\n    buffer.writeUtf8CodePoint(0x4e)\n    buffer.writeUtf8CodePoint(0x47)\n    buffer.writeUtf8CodePoint(0x0d)\n    buffer.writeUtf8CodePoint(0x0a)\n    buffer.writeUtf8CodePoint(0x1a)\n    buffer.writeUtf8CodePoint(0x0a)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(buffer)\n        .setHeader(\"Content-Type\", \"image/png; charset=utf-8\")\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 9\")\n      .assertLogEqual(\"Content-Type: image/png; charset=utf-8\")\n      .assertLogEqual(\"\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, binary 9-byte body omitted\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 9\")\n      .assertLogEqual(\"Content-Type: image/png; charset=utf-8\")\n      .assertLogEqual(\"\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, binary 9-byte body omitted\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun connectFail() {\n    setLevel(Level.BASIC)\n    client =\n      OkHttpClient\n        .Builder()\n        .dns { hostname: String? -> throw UnknownHostException(\"reason\") }\n        .addInterceptor(applicationInterceptor)\n        .build()\n    try {\n      client.newCall(request().build()).execute()\n      fail<Any>()\n    } catch (expected: UnknownHostException) {\n    }\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogMatch(Regex(\"\"\"<-- HTTP FAILED: java.net.UnknownHostException: reason. $url \\(\\d+ms\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun http2() {\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    url = server.url(\"/\")\n    setLevel(Level.BASIC)\n    server.enqueue(MockResponse())\n    val response = client.newCall(request().build()).execute()\n    Assumptions.assumeTrue(response.protocol == Protocol.HTTP_2)\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 $url \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url h2\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 $url \\(\\d+ms, 0-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun headersAreRedacted() {\n    val networkInterceptor =\n      HttpLoggingInterceptor(networkLogs).setLevel(\n        Level.HEADERS,\n      )\n    networkInterceptor.redactHeader(\"sEnSiTiVe\")\n    val applicationInterceptor =\n      HttpLoggingInterceptor(applicationLogs).setLevel(\n        Level.HEADERS,\n      )\n    applicationInterceptor.redactHeader(\"sEnSiTiVe\")\n    client =\n      OkHttpClient\n        .Builder()\n        .addNetworkInterceptor(networkInterceptor)\n        .addInterceptor(applicationInterceptor)\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .addHeader(\"SeNsItIvE\", \"Value\")\n        .addHeader(\"Not-Sensitive\", \"Value\")\n        .build(),\n    )\n    val response =\n      client\n        .newCall(\n          request()\n            .addHeader(\"SeNsItIvE\", \"Value\")\n            .addHeader(\"Not-Sensitive\", \"Value\")\n            .build(),\n        ).execute()\n    response.body.close()\n    applicationLogs\n      .assertLogEqual(\"--> GET $url\")\n      .assertLogEqual(\"SeNsItIvE: ██\")\n      .assertLogEqual(\"Not-Sensitive: Value\")\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"SeNsItIvE: ██\")\n      .assertLogEqual(\"Not-Sensitive: Value\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $url http/1.1\")\n      .assertLogEqual(\"SeNsItIvE: ██\")\n      .assertLogEqual(\"Not-Sensitive: Value\")\n      .assertLogEqual(\"Host: $host\")\n      .assertLogEqual(\"Connection: Keep-Alive\")\n      .assertLogEqual(\"Accept-Encoding: gzip\")\n      .assertLogMatch(Regex(\"\"\"User-Agent: okhttp/.+\"\"\"))\n      .assertLogEqual(\"--> END GET\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"Content-Length: 0\")\n      .assertLogEqual(\"SeNsItIvE: ██\")\n      .assertLogEqual(\"Not-Sensitive: Value\")\n      .assertLogEqual(\"<-- END HTTP\")\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun sensitiveQueryParamsAreRedacted() {\n    url = server.url(\"/api/login?user=test_user&authentication=basic&password=confidential_password\")\n    val networkInterceptor =\n      HttpLoggingInterceptor(networkLogs).setLevel(\n        Level.BASIC,\n      )\n    networkInterceptor.redactQueryParams(\"user\", \"passWord\")\n\n    val applicationInterceptor =\n      HttpLoggingInterceptor(applicationLogs).setLevel(\n        Level.BASIC,\n      )\n    applicationInterceptor.redactQueryParams(\"user\", \"PassworD\")\n\n    client =\n      OkHttpClient\n        .Builder()\n        .addNetworkInterceptor(networkInterceptor)\n        .addInterceptor(applicationInterceptor)\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .build(),\n    )\n    val response =\n      client\n        .newCall(\n          request()\n            .build(),\n        ).execute()\n    response.body.close()\n    val redactedUrl = networkInterceptor.redactUrl(url)\n    val redactedUrlPattern = redactedUrl.replace(\"?\", \"\"\"\\?\"\"\")\n    applicationLogs\n      .assertLogEqual(\"--> GET $redactedUrl\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $redactedUrlPattern \\(\\d+ms, \\d+-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $redactedUrl http/1.1\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $redactedUrlPattern \\(\\d+ms, \\d+-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun preserveQueryParamsAfterRedacted() {\n    url =\n      server.url(\n        \"\"\"/api/login?\n      |user=test_user&\n      |authentication=basic&\n      |password=confidential_password&\n      |authentication=rather simple login method\n        \"\"\".trimMargin(),\n      )\n    val networkInterceptor =\n      HttpLoggingInterceptor(networkLogs).setLevel(\n        Level.BASIC,\n      )\n    networkInterceptor.redactQueryParams(\"user\", \"passWord\")\n\n    val applicationInterceptor =\n      HttpLoggingInterceptor(applicationLogs).setLevel(\n        Level.BASIC,\n      )\n    applicationInterceptor.redactQueryParams(\"user\", \"PassworD\")\n\n    client =\n      OkHttpClient\n        .Builder()\n        .addNetworkInterceptor(networkInterceptor)\n        .addInterceptor(applicationInterceptor)\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .build(),\n    )\n    val response =\n      client\n        .newCall(\n          request()\n            .build(),\n        ).execute()\n    response.body.close()\n    val redactedUrl = networkInterceptor.redactUrl(url)\n    val redactedUrlPattern = redactedUrl.replace(\"?\", \"\"\"\\?\"\"\")\n    applicationLogs\n      .assertLogEqual(\"--> GET $redactedUrl\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $redactedUrlPattern \\(\\d+ms, \\d+-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n    networkLogs\n      .assertLogEqual(\"--> GET $redactedUrl http/1.1\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $redactedUrlPattern \\(\\d+ms, \\d+-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun duplexRequestsAreNotLogged() {\n    platform.assumeHttp2Support()\n    server.useHttps(handshakeCertificates.sslSocketFactory()) // HTTP/2\n    url = server.url(\"/\")\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Hello response!\")\n        .build(),\n    )\n    val asyncRequestBody: RequestBody =\n      object : RequestBody() {\n        override fun contentType(): MediaType? = null\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.writeUtf8(\"Hello request!\")\n          sink.close()\n        }\n\n        override fun isDuplex(): Boolean = true\n      }\n    val request =\n      request()\n        .post(asyncRequestBody)\n        .build()\n    val response = client.newCall(request).execute()\n    Assumptions.assumeTrue(response.protocol == Protocol.HTTP_2)\n    assertThat(response.body.string()).isEqualTo(\"Hello response!\")\n    applicationLogs\n      .assertLogEqual(\"--> POST $url\")\n      .assertLogEqual(\"--> END POST (duplex request body omitted)\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"content-length: 15\")\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"Hello response!\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 15-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun oneShotRequestsAreNotLogged() {\n    url = server.url(\"/\")\n    setLevel(Level.BODY)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Hello response!\")\n        .build(),\n    )\n    val asyncRequestBody: RequestBody =\n      object : RequestBody() {\n        var counter = 0\n\n        override fun contentType() = null\n\n        override fun writeTo(sink: BufferedSink) {\n          counter++\n          assertThat(counter).isLessThanOrEqualTo(1)\n          sink.writeUtf8(\"Hello request!\")\n          sink.close()\n        }\n\n        override fun isOneShot() = true\n      }\n    val request =\n      request()\n        .post(asyncRequestBody)\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.body.string()).isEqualTo(\"Hello response!\")\n    applicationLogs\n      .assertLogEqual(\"\"\"--> POST $url\"\"\")\n      .assertLogEqual(\"\"\"--> END POST (one-shot body omitted)\"\"\")\n      .assertLogMatch(Regex(\"\"\"<-- 200 OK $url \\(\\d+ms\\)\"\"\"))\n      .assertLogEqual(\"\"\"Content-Length: 15\"\"\")\n      .assertLogEqual(\"\")\n      .assertLogEqual(\"\"\"Hello response!\"\"\")\n      .assertLogMatch(Regex(\"\"\"<-- END HTTP \\(\\d+ms, 15-byte body\\)\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  private fun request(): Request.Builder = Request.Builder().url(url)\n\n  internal class LogRecorder(\n    val prefix: Regex = Regex(\"\"),\n  ) : HttpLoggingInterceptor.Logger {\n    private val logs = mutableListOf<String>()\n    private var index = 0\n\n    fun assertLogEqual(expected: String) =\n      apply {\n        assertThat(index, \"No more messages found\")\n          .isLessThan(logs.size)\n        assertThat(logs[index++]).isEqualTo(expected)\n        return this\n      }\n\n    fun assertLogMatch(regex: Regex) =\n      apply {\n        assertThat(index, \"No more messages found\")\n          .isLessThan(logs.size)\n        assertThat(logs[index++])\n          .matches(Regex(prefix.pattern + regex.pattern, RegexOption.DOT_MATCHES_ALL))\n      }\n\n    fun assertNoMoreLogs() {\n      assertThat(logs.size, \"More messages remain: ${logs.subList(index, logs.size)}\")\n        .isEqualTo(index)\n    }\n\n    override fun log(message: String) {\n      logs.add(message)\n    }\n  }\n\n  companion object {\n    private val PLAIN = \"text/plain; charset=utf-8\".toMediaType()\n  }\n}\n"
  },
  {
    "path": "okhttp-logging-interceptor/src/test/java/okhttp3/logging/LoggingEventListenerTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.logging\n\nimport assertk.assertThat\nimport assertk.assertions.isNotNull\nimport java.io.IOException\nimport java.net.UnknownHostException\nimport java.util.Arrays\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.HttpUrl\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport okhttp3.Response\nimport okhttp3.TestUtil.assumeNotWindows\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Suppress(\"ktlint:standard:max-line-length\")\nclass LoggingEventListenerTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  private val handshakeCertificates = platform.localhostHandshakeCertificates()\n  private val logRecorder =\n    HttpLoggingInterceptorTest.LogRecorder(\n      prefix = Regex(\"\"\"\\[\\d+ ms] \"\"\"),\n    )\n  private val loggingEventListenerFactory = LoggingEventListener.Factory(logRecorder)\n  private lateinit var client: OkHttpClient\n  private lateinit var url: HttpUrl\n\n  @BeforeEach\n  fun setUp() {\n    client =\n      clientTestRule\n        .newClientBuilder()\n        .eventListenerFactory(loggingEventListenerFactory)\n        .sslSocketFactory(\n          handshakeCertificates.sslSocketFactory(),\n          handshakeCertificates.trustManager,\n        ).retryOnConnectionFailure(false)\n        .build()\n    url = server.url(\"/\")\n  }\n\n  @Test\n  fun get() {\n    assumeNotWindows()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"Hello!\")\n        .setHeader(\"Content-Type\", PLAIN)\n        .build(),\n    )\n    val response = client.newCall(request().build()).execute()\n    assertThat(response.body).isNotNull()\n    response.body.bytes()\n    logRecorder\n      .assertLogMatch(Regex(\"\"\"callStart: Request\\{method=GET, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectStart: $url\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectEnd: \\[DIRECT]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsStart: ${url.host}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsEnd: \\[.+]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectStart: ${url.host}/.+ DIRECT\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectEnd: http/1.1\"\"\"))\n      .assertLogMatch(\n        Regex(\n          \"\"\"connectionAcquired: Connection\\{${url.host}:\\d+, proxy=DIRECT hostAddress=${url.host}/.+ cipherSuite=none protocol=http/1\\.1\\}\"\"\",\n        ),\n      ).assertLogMatch(Regex(\"\"\"requestHeadersStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"requestHeadersEnd\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseHeadersStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseHeadersEnd: Response\\{protocol=http/1\\.1, code=200, message=OK, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseBodyStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseBodyEnd: byteCount=6\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectionReleased\"\"\"))\n      .assertLogMatch(Regex(\"\"\"callEnd\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun post() {\n    assumeNotWindows()\n    server.enqueue(MockResponse())\n    client.newCall(request().post(\"Hello!\".toRequestBody(PLAIN)).build()).execute()\n    logRecorder\n      .assertLogMatch(Regex(\"\"\"callStart: Request\\{method=POST, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectStart: $url\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectEnd: \\[DIRECT]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsStart: ${url.host}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsEnd: \\[.+]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectStart: ${url.host}/.+ DIRECT\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectEnd: http/1.1\"\"\"))\n      .assertLogMatch(\n        Regex(\n          \"\"\"connectionAcquired: Connection\\{${url.host}:\\d+, proxy=DIRECT hostAddress=${url.host}/.+ cipherSuite=none protocol=http/1\\.1\\}\"\"\",\n        ),\n      ).assertLogMatch(Regex(\"\"\"requestHeadersStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"requestHeadersEnd\"\"\"))\n      .assertLogMatch(Regex(\"\"\"requestBodyStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"requestBodyEnd: byteCount=6\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseHeadersStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseHeadersEnd: Response\\{protocol=http/1\\.1, code=200, message=OK, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseBodyStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseBodyEnd: byteCount=0\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectionReleased\"\"\"))\n      .assertLogMatch(Regex(\"\"\"callEnd\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun secureGet() {\n    assumeNotWindows()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    url = server.url(\"/\")\n    server.enqueue(MockResponse())\n    val response = client.newCall(request().build()).execute()\n    assertThat(response.body).isNotNull()\n    response.body.bytes()\n    platform.assumeHttp2Support()\n    logRecorder\n      .assertLogMatch(Regex(\"\"\"callStart: Request\\{method=GET, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectStart: $url\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectEnd: \\[DIRECT]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsStart: ${url.host}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsEnd: \\[.+]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectStart: ${url.host}/.+ DIRECT\"\"\"))\n      .assertLogMatch(Regex(\"\"\"secureConnectStart\"\"\"))\n      .assertLogMatch(\n        Regex(\n          \"\"\"secureConnectEnd: Handshake\\{tlsVersion=TLS_1_[23] cipherSuite=TLS_.* peerCertificates=\\[CN=localhost] localCertificates=\\[]\\}\"\"\",\n        ),\n      ).assertLogMatch(Regex(\"\"\"connectEnd: h2\"\"\"))\n      .assertLogMatch(\n        Regex(\"\"\"connectionAcquired: Connection\\{${url.host}:\\d+, proxy=DIRECT hostAddress=${url.host}/.+ cipherSuite=.+ protocol=h2\\}\"\"\"),\n      ).assertLogMatch(Regex(\"\"\"requestHeadersStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"requestHeadersEnd\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseHeadersStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseHeadersEnd: Response\\{protocol=h2, code=200, message=, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseBodyStart\"\"\"))\n      .assertLogMatch(Regex(\"\"\"responseBodyEnd: byteCount=0\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectionReleased\"\"\"))\n      .assertLogMatch(Regex(\"\"\"callEnd\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun dnsFail() {\n    client =\n      OkHttpClient\n        .Builder()\n        .dns { _ -> throw UnknownHostException(\"reason\") }\n        .eventListenerFactory(loggingEventListenerFactory)\n        .build()\n    try {\n      client.newCall(request().build()).execute()\n      fail<Any>()\n    } catch (expected: UnknownHostException) {\n    }\n    logRecorder\n      .assertLogMatch(Regex(\"\"\"callStart: Request\\{method=GET, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectStart: $url\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectEnd: \\[DIRECT]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsStart: ${url.host}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"callFailed: java.net.UnknownHostException: reason\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  @Test\n  fun connectFail() {\n    assumeNotWindows()\n    server.useHttps(handshakeCertificates.sslSocketFactory())\n    server.protocols = Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .failHandshake()\n        .build(),\n    )\n    url = server.url(\"/\")\n    try {\n      client.newCall(request().build()).execute()\n      fail<Any>()\n    } catch (expected: IOException) {\n    }\n    logRecorder\n      .assertLogMatch(Regex(\"\"\"callStart: Request\\{method=GET, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectStart: $url\"\"\"))\n      .assertLogMatch(Regex(\"\"\"proxySelectEnd: \\[DIRECT]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsStart: ${url.host}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"dnsEnd: \\[.+]\"\"\"))\n      .assertLogMatch(Regex(\"\"\"connectStart: ${url.host}/.+ DIRECT\"\"\"))\n      .assertLogMatch(Regex(\"\"\"secureConnectStart\"\"\"))\n      .assertLogMatch(\n        Regex(\n          \"\"\"connectFailed: null \\S+(?:SSLProtocolException|SSLHandshakeException|TlsFatalAlert): .*(?:Unexpected handshake message: client_hello|Handshake message sequence violation, 1|Read error|Handshake failed|unexpected_message\\(10\\)).*\"\"\",\n        ),\n      ).assertLogMatch(\n        Regex(\n          \"\"\"callFailed: \\S+(?:SSLProtocolException|SSLHandshakeException|TlsFatalAlert): .*(?:Unexpected handshake message: client_hello|Handshake message sequence violation, 1|Read error|Handshake failed|unexpected_message\\(10\\)).*\"\"\",\n        ),\n      ).assertNoMoreLogs()\n  }\n\n  @Test\n  fun testCacheEvents() {\n    val request = Request.Builder().url(url).build()\n    val call = client.newCall(request)\n    val response =\n      Response\n        .Builder()\n        .request(request)\n        .code(200)\n        .message(\"\")\n        .protocol(Protocol.HTTP_2)\n        .build()\n    val listener = loggingEventListenerFactory.create(call)\n    listener.cacheConditionalHit(call, response)\n    listener.cacheHit(call, response)\n    listener.cacheMiss(call)\n    listener.satisfactionFailure(call, response)\n    logRecorder\n      .assertLogMatch(Regex(\"\"\"cacheConditionalHit: Response\\{protocol=h2, code=200, message=, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"cacheHit: Response\\{protocol=h2, code=200, message=, url=$url\\}\"\"\"))\n      .assertLogMatch(Regex(\"\"\"cacheMiss\"\"\"))\n      .assertLogMatch(Regex(\"\"\"satisfactionFailure: Response\\{protocol=h2, code=200, message=, url=$url\\}\"\"\"))\n      .assertNoMoreLogs()\n  }\n\n  private fun request(): Request.Builder = Request.Builder().url(url)\n\n  companion object {\n    private val PLAIN = \"text/plain\".toMediaType()\n  }\n}\n"
  },
  {
    "path": "okhttp-osgi-tests/build.gradle.kts",
    "content": "import okhttp3.buildsupport.testJavaVersion\n\nplugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\ndependencies {\n  implementation(projects.okhttp)\n  implementation(projects.okhttpBrotli)\n  implementation(projects.okhttpCoroutines)\n  implementation(projects.okhttpDnsoverhttps)\n  implementation(projects.loggingInterceptor)\n  implementation(projects.okhttpSse)\n  implementation(projects.okhttpTls)\n  implementation(projects.okhttpUrlconnection)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(libs.junit)\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n  testImplementation(libs.assertk)\n\n  testImplementation(libs.aqute.resolve)\n}\n\nnormalization {\n  runtimeClasspath {\n    /*\n       - The below two ignored files are generated during test execution\n       by the test: okhttp/src/test/java/okhttp3/osgi/OsgiTest.java\n\n       - The compressed index.xml file contains a timestamp property which\n       changes with every test execution, such that running the test\n       actually changes the test classpath itself. This means that it\n       can\"t benefit from incremental build acceleration, because on every\n       execution it sees that the classpath has changed, and so to be\n       safe, it needs to re-run.\n\n       - This is unfortunate, because actually it would be safe to declare\n       the task as up-to-date, because these two files, which are based on\n       the generated index.xml, are outputs, not inputs. We can be sure of\n       this because they are deleted in the @BeforeEach method of the\n       OsgiTest test class.\n\n       - To enable the benefit of incremental builds, we can ask Gradle\n       to ignore these two files when considering whether the classpath\n       has changed. That is the purpose of this normalization block.\n   */\n    ignore(\"okhttp3/osgi/workspace/cnf/repo/index.xml.gz\")\n    ignore(\"okhttp3/osgi/workspace/cnf/repo/index.xml.gz.sha\")\n  }\n}\n\n// Expose OSGi jars to the test environment.\nval osgiTestDeploy: Configuration by configurations.creating\n\nval copyOsgiTestDeployment = tasks.register<Copy>(\"copyOsgiTestDeployment\") {\n  from(osgiTestDeploy)\n  into(layout.buildDirectory.dir(\"resources/test/okhttp3/osgi/deployments\"))\n}\n\ndependencies {\n  osgiTestDeploy(libs.eclipse.osgi)\n  osgiTestDeploy(libs.kotlin.stdlib.osgi)\n}\n\ntasks.withType<Test> {\n  dependsOn(copyOsgiTestDeployment)\n  val javaVersion = project.testJavaVersion\n  onlyIf(\"Tests require JDK 17\") {\n    javaVersion >= 17\n  }\n}\n"
  },
  {
    "path": "okhttp-osgi-tests/src/test/kotlin/okhttp3/osgi/OsgiTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.osgi\n\nimport aQute.bnd.build.Project\nimport aQute.bnd.build.Workspace\nimport aQute.bnd.build.model.BndEditModel\nimport aQute.bnd.deployer.repository.LocalIndexedRepo\nimport aQute.bnd.osgi.Constants\nimport aQute.bnd.service.RepositoryPlugin\nimport biz.aQute.resolve.Bndrun\nimport java.io.File\nimport okio.FileSystem\nimport okio.Path\nimport okio.Path.Companion.toPath\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\n\n@Tag(\"Slow\")\nclass OsgiTest {\n  private lateinit var testResourceDir: Path\n  private lateinit var workspaceDir: Path\n\n  @BeforeEach\n  fun setUp() {\n    testResourceDir = \"./build/resources/test/okhttp3/osgi\".toPath()\n    workspaceDir = testResourceDir / \"workspace\"\n\n    // Ensure we start from scratch.\n    fileSystem.deleteRecursively(workspaceDir)\n    fileSystem.createDirectories(workspaceDir)\n  }\n\n  /**\n   * Resolve the OSGi metadata of the all okhttp3 modules. If required modules do not have OSGi\n   * metadata this will fail with an exception.\n   */\n  @Test\n  fun testMainModuleWithSiblings() {\n    createWorkspace().use { workspace ->\n      createBndRun(workspace).use { bndRun ->\n        bndRun.resolve(\n          false,\n          false,\n        )\n      }\n    }\n  }\n\n  private fun createWorkspace(): Workspace {\n    val bndDir = workspaceDir / \"cnf\"\n    val repoDir = bndDir / \"repo\"\n    fileSystem.createDirectories(repoDir)\n    return Workspace(workspaceDir.toFile(), bndDir.name)\n      .apply {\n        setProperty(\n          \"${Constants.PLUGIN}.$REPO_NAME\",\n          LocalIndexedRepo::class.java.getName() +\n            \"; ${LocalIndexedRepo.PROP_NAME} = '$REPO_NAME'\" +\n            \"; ${LocalIndexedRepo.PROP_LOCAL_DIR} = '$repoDir'\",\n        )\n        refresh()\n        prepareWorkspace()\n      }\n  }\n\n  private fun Workspace.prepareWorkspace() {\n    val repositoryPlugin = getRepository(REPO_NAME)\n\n    // Deploy the bundles in the deployments test directory.\n    repositoryPlugin.deployDirectory(testResourceDir / \"deployments\")\n    repositoryPlugin.deployClassPath()\n  }\n\n  private fun createBndRun(workspace: Workspace): Bndrun {\n    // Creating the run require string. It will always use the latest version of each bundle\n    // available in the repository.\n    val runRequireString =\n      REQUIRED_BUNDLES.joinToString(separator = \",\") {\n        \"osgi.identity;filter:='(osgi.identity=$it)'\"\n      }\n\n    val bndEditModel =\n      BndEditModel(workspace).apply {\n        // Temporary project to satisfy bnd API.\n        project = Project(workspace, workspaceDir.toFile())\n      }\n\n    return Bndrun(bndEditModel).apply {\n      setRunfw(RESOLVE_OSGI_FRAMEWORK)\n      runee = RESOLVE_JAVA_VERSION\n      setRunRequires(runRequireString)\n    }\n  }\n\n  private fun RepositoryPlugin.deployDirectory(directory: Path) {\n    for (path in fileSystem.list(directory)) {\n      deployFile(path)\n    }\n  }\n\n  private fun RepositoryPlugin.deployClassPath() {\n    val classpath = System.getProperty(\"java.class.path\")\n    val entries =\n      classpath\n        .split(File.pathSeparator.toRegex())\n        .dropLastWhile { it.isEmpty() }\n        .toTypedArray()\n    for (classPathEntry in entries) {\n      deployFile(classPathEntry.toPath())\n    }\n  }\n\n  private fun RepositoryPlugin.deployFile(file: Path) {\n    if (fileSystem.metadataOrNull(file)?.isRegularFile != true) return\n    try {\n      fileSystem.read(file) {\n        put(inputStream(), RepositoryPlugin.PutOptions())\n        println(\"Deployed ${file.name}\")\n      }\n    } catch (e: IllegalArgumentException) {\n      if (\"Jar does not have a symbolic name\" in e.message!!) {\n        println(\"Skipped non-OSGi dependency: ${file.name}\")\n        return\n      }\n      throw e\n    }\n  }\n\n  companion object {\n    val fileSystem = FileSystem.SYSTEM\n\n    /** Each is the Bundle-SymbolicName of an OkHttp module's OSGi configuration.  */\n    private val REQUIRED_BUNDLES: List<String> =\n      mutableListOf(\n        \"com.squareup.okhttp3\",\n        \"com.squareup.okhttp3.brotli\",\n        \"com.squareup.okhttp3.dnsoverhttps\",\n        \"com.squareup.okhttp3.logging\",\n        \"com.squareup.okhttp3.sse\",\n        \"com.squareup.okhttp3.tls\",\n        \"com.squareup.okhttp3.urlconnection\",\n      )\n\n    /** Equinox must also be on the testing classpath.  */\n    private const val RESOLVE_OSGI_FRAMEWORK = \"org.eclipse.osgi\"\n    private const val RESOLVE_JAVA_VERSION = \"JavaSE-1.8\"\n    private const val REPO_NAME = \"OsgiTest\"\n  }\n}\n"
  },
  {
    "path": "okhttp-sse/Module.md",
    "content": "# Module okhttp-sse\n\nSupport for server-sent events.\n"
  },
  {
    "path": "okhttp-sse/README.md",
    "content": "OkHttp Server-Sent Events\n=========================\n\nExperimental support for server-sent events.\nAPI is not considered stable and may change at any time.\n\n### Download\n\n```kotlin\ntestImplementation(\"com.squareup.okhttp3:okhttp-sse:5.3.0\")\n```\n"
  },
  {
    "path": "okhttp-sse/api/okhttp-sse.api",
    "content": "public abstract interface class okhttp3/sse/EventSource {\n\tpublic abstract fun cancel ()V\n\tpublic abstract fun request ()Lokhttp3/Request;\n}\n\npublic abstract interface class okhttp3/sse/EventSource$Factory {\n\tpublic abstract fun newEventSource (Lokhttp3/Request;Lokhttp3/sse/EventSourceListener;)Lokhttp3/sse/EventSource;\n}\n\npublic abstract class okhttp3/sse/EventSourceListener {\n\tpublic fun <init> ()V\n\tpublic fun onClosed (Lokhttp3/sse/EventSource;)V\n\tpublic fun onEvent (Lokhttp3/sse/EventSource;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V\n\tpublic fun onFailure (Lokhttp3/sse/EventSource;Ljava/lang/Throwable;Lokhttp3/Response;)V\n\tpublic fun onOpen (Lokhttp3/sse/EventSource;Lokhttp3/Response;)V\n}\n\npublic final class okhttp3/sse/EventSources {\n\tpublic static final field INSTANCE Lokhttp3/sse/EventSources;\n\tpublic static final fun createFactory (Lokhttp3/Call$Factory;)Lokhttp3/sse/EventSource$Factory;\n\tpublic static final synthetic fun createFactory (Lokhttp3/OkHttpClient;)Lokhttp3/sse/EventSource$Factory;\n\tpublic static final fun processResponse (Lokhttp3/Response;Lokhttp3/sse/EventSourceListener;)V\n}\n\n"
  },
  {
    "path": "okhttp-sse/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Export-Package: okhttp3.sse\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.sse\",\n)\n\nproject.applyJavaModules(\"okhttp3.sse\")\n\ndependencies {\n  api(projects.okhttp)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(projects.mockwebserver3)\n  testImplementation(projects.mockwebserver3Junit5)\n  testImplementation(libs.junit)\n}\n"
  },
  {
    "path": "okhttp-sse/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.sse {\n  requires okhttp3;\n  exports okhttp3.sse;\n}\n"
  },
  {
    "path": "okhttp-sse/src/main/kotlin/okhttp3/sse/EventSource.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse\n\nimport okhttp3.Request\n\ninterface EventSource {\n  /** Returns the original request that initiated this event source. */\n  fun request(): Request\n\n  /**\n   * Immediately and violently release resources held by this event source. This does nothing if\n   * the event source has already been closed or canceled.\n   */\n  fun cancel()\n\n  fun interface Factory {\n    /**\n     * Creates a new event source and immediately returns it. Creating an event source initiates an\n     * asynchronous process to connect the socket. Once that succeeds or fails, `listener` will be\n     * notified. The caller must cancel the returned event source when it is no longer in use.\n     */\n    fun newEventSource(\n      request: Request,\n      listener: EventSourceListener,\n    ): EventSource\n  }\n}\n"
  },
  {
    "path": "okhttp-sse/src/main/kotlin/okhttp3/sse/EventSourceListener.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse\n\nimport okhttp3.Response\n\nabstract class EventSourceListener {\n  /**\n   * Invoked when an event source has been accepted by the remote peer and may begin transmitting\n   * events.\n   */\n  open fun onOpen(\n    eventSource: EventSource,\n    response: Response,\n  ) {\n  }\n\n  open fun onEvent(\n    eventSource: EventSource,\n    id: String?,\n    type: String?,\n    data: String,\n  ) {\n  }\n\n  /**\n   * No further calls to this listener will be made.\n   */\n  open fun onClosed(eventSource: EventSource) {\n  }\n\n  /**\n   * Invoked when an event source has been closed due to an error reading from or writing to the\n   * network. Incoming events may have been lost. No further calls to this listener will be made.\n   */\n  open fun onFailure(\n    eventSource: EventSource,\n    t: Throwable?,\n    response: Response?,\n  ) {\n  }\n}\n"
  },
  {
    "path": "okhttp-sse/src/main/kotlin/okhttp3/sse/EventSources.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse\n\nimport okhttp3.Call\nimport okhttp3.OkHttpClient\nimport okhttp3.Response\nimport okhttp3.sse.internal.RealEventSource\n\nobject EventSources {\n  @Deprecated(\n    message = \"required for binary-compatibility!\",\n    level = DeprecationLevel.HIDDEN,\n  )\n  @JvmStatic\n  fun createFactory(client: OkHttpClient) = createFactory(client as Call.Factory)\n\n  @JvmStatic\n  fun createFactory(callFactory: Call.Factory): EventSource.Factory =\n    EventSource.Factory { request, listener ->\n      val actualRequest =\n        if (request.header(\"Accept\") == null) {\n          request.newBuilder().addHeader(\"Accept\", \"text/event-stream\").build()\n        } else {\n          request\n        }\n\n      RealEventSource(actualRequest, listener).apply {\n        connect(callFactory)\n      }\n    }\n\n  @JvmStatic\n  fun processResponse(\n    response: Response,\n    listener: EventSourceListener,\n  ) {\n    val eventSource = RealEventSource(response.request, listener)\n    eventSource.processResponse(response)\n  }\n}\n"
  },
  {
    "path": "okhttp-sse/src/main/kotlin/okhttp3/sse/internal/RealEventSource.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse.internal\n\nimport java.io.IOException\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.ResponseBody\nimport okhttp3.internal.stripBody\nimport okhttp3.sse.EventSource\nimport okhttp3.sse.EventSourceListener\n\ninternal class RealEventSource(\n  private val request: Request,\n  private val listener: EventSourceListener,\n) : EventSource,\n  ServerSentEventReader.Callback,\n  Callback {\n  private var call: Call? = null\n\n  @Volatile private var canceled = false\n\n  fun connect(callFactory: Call.Factory) {\n    call =\n      callFactory.newCall(request).apply {\n        enqueue(this@RealEventSource)\n      }\n  }\n\n  override fun onResponse(\n    call: Call,\n    response: Response,\n  ) {\n    processResponse(response)\n  }\n\n  fun processResponse(response: Response) {\n    response.use {\n      if (!response.isSuccessful) {\n        listener.onFailure(this, null, response)\n        return\n      }\n\n      val body = response.body\n\n      if (!body.isEventStream()) {\n        listener.onFailure(\n          this,\n          IllegalStateException(\"Invalid content-type: ${body.contentType()}\"),\n          response,\n        )\n        return\n      }\n\n      // This is a long-lived response. Cancel full-call timeouts.\n      call?.timeout()?.cancel()\n\n      // Replace the body with a stripped one so the callbacks can't see real data.\n      val response = response.stripBody()\n\n      val reader = ServerSentEventReader(body.source(), this)\n      try {\n        if (!canceled) {\n          listener.onOpen(this, response)\n          while (!canceled && reader.processNextEvent()) {\n          }\n        }\n      } catch (e: Exception) {\n        val exception =\n          when {\n            canceled -> IOException(\"canceled\", e)\n            else -> e\n          }\n        listener.onFailure(this, exception, response)\n        return\n      }\n      if (canceled) {\n        listener.onFailure(this, IOException(\"canceled\"), response)\n      } else {\n        listener.onClosed(this)\n      }\n    }\n  }\n\n  private fun ResponseBody.isEventStream(): Boolean {\n    val contentType = contentType() ?: return false\n    return contentType.type == \"text\" && contentType.subtype == \"event-stream\"\n  }\n\n  override fun onFailure(\n    call: Call,\n    e: IOException,\n  ) {\n    listener.onFailure(this, e, null)\n  }\n\n  override fun request(): Request = request\n\n  override fun cancel() {\n    canceled = true\n    call?.cancel()\n  }\n\n  override fun onEvent(\n    id: String?,\n    type: String?,\n    data: String,\n  ) {\n    listener.onEvent(this, id, type, data)\n  }\n\n  override fun onRetryChange(timeMs: Long) {\n    // Ignored. We do not auto-retry.\n  }\n}\n"
  },
  {
    "path": "okhttp-sse/src/main/kotlin/okhttp3/sse/internal/ServerSentEventReader.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse.internal\n\nimport java.io.IOException\nimport okhttp3.internal.toLongOrDefault\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.Options\n\nclass ServerSentEventReader(\n  private val source: BufferedSource,\n  private val callback: Callback,\n) {\n  private var lastId: String? = null\n\n  interface Callback {\n    fun onEvent(\n      id: String?,\n      type: String?,\n      data: String,\n    )\n\n    fun onRetryChange(timeMs: Long)\n  }\n\n  /**\n   * Process the next event. This will result in a single call to [Callback.onEvent] *unless* the\n   * data section was empty. Any number of calls to [Callback.onRetryChange] may occur while\n   * processing an event.\n   *\n   * @return false when EOF is reached\n   */\n  @Throws(IOException::class)\n  fun processNextEvent(): Boolean {\n    var id = lastId\n    var type: String? = null\n    val data = Buffer()\n\n    while (true) {\n      when (source.select(options)) {\n        in 0..2 -> {\n          completeEvent(id, type, data)\n          return true\n        }\n\n        in 3..4 -> {\n          source.readData(data)\n        }\n\n        in 5..7 -> {\n          data.writeByte('\\n'.code) // 'data' on a line of its own.\n        }\n\n        in 8..9 -> {\n          id = source.readUtf8LineStrict().takeIf { it.isNotEmpty() }\n        }\n\n        in 10..12 -> {\n          id = null // 'id' on a line of its own.\n        }\n\n        in 13..14 -> {\n          type = source.readUtf8LineStrict().takeIf { it.isNotEmpty() }\n        }\n\n        in 15..17 -> {\n          type = null // 'event' on a line of its own\n        }\n\n        in 18..19 -> {\n          val retryMs = source.readRetryMs()\n          if (retryMs != -1L) {\n            callback.onRetryChange(retryMs)\n          }\n        }\n\n        -1 -> {\n          val lineEnd = source.indexOfElement(CRLF)\n          if (lineEnd != -1L) {\n            // Skip the line and newline\n            source.skip(lineEnd)\n            source.select(options)\n          } else {\n            return false // No more newlines.\n          }\n        }\n\n        else -> {\n          throw AssertionError()\n        }\n      }\n    }\n  }\n\n  @Throws(IOException::class)\n  private fun completeEvent(\n    id: String?,\n    type: String?,\n    data: Buffer,\n  ) {\n    if (data.size != 0L) {\n      lastId = id\n      data.skip(1L) // Leading newline.\n      callback.onEvent(id, type, data.readUtf8())\n    }\n  }\n\n  companion object {\n    val options =\n      Options.of(\n        // 0\n        \"\\r\\n\".encodeUtf8(),\n        // 1\n        \"\\r\".encodeUtf8(),\n        // 2\n        \"\\n\".encodeUtf8(),\n        // 3\n        \"data: \".encodeUtf8(),\n        // 4\n        \"data:\".encodeUtf8(),\n        // 5\n        \"data\\r\\n\".encodeUtf8(),\n        // 6\n        \"data\\r\".encodeUtf8(),\n        // 7\n        \"data\\n\".encodeUtf8(),\n        // 8\n        \"id: \".encodeUtf8(),\n        // 9\n        \"id:\".encodeUtf8(),\n        // 10\n        \"id\\r\\n\".encodeUtf8(),\n        // 11\n        \"id\\r\".encodeUtf8(),\n        // 12\n        \"id\\n\".encodeUtf8(),\n        // 13\n        \"event: \".encodeUtf8(),\n        // 14\n        \"event:\".encodeUtf8(),\n        // 15\n        \"event\\r\\n\".encodeUtf8(),\n        // 16\n        \"event\\r\".encodeUtf8(),\n        // 17\n        \"event\\n\".encodeUtf8(),\n        // 18\n        \"retry: \".encodeUtf8(),\n        // 19\n        \"retry:\".encodeUtf8(),\n      )\n\n    private val CRLF = \"\\r\\n\".encodeUtf8()\n\n    @Throws(IOException::class)\n    private fun BufferedSource.readData(data: Buffer) {\n      data.writeByte('\\n'.code)\n      readFully(data, indexOfElement(CRLF))\n      select(options) // Skip the newline bytes.\n    }\n\n    @Throws(IOException::class)\n    private fun BufferedSource.readRetryMs(): Long {\n      val retryString = readUtf8LineStrict()\n      return retryString.toLongOrDefault(-1L)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-sse/src/test/java/okhttp3/sse/internal/Event.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse.internal\n\ninternal data class Event(\n  val id: String?,\n  val type: String?,\n  val data: String,\n)\n"
  },
  {
    "path": "okhttp-sse/src/test/java/okhttp3/sse/internal/EventSourceHttpTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse.internal\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isEqualTo\nimport java.util.concurrent.TimeUnit\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.FollowUpDecision\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.RequestHeadersEnd\nimport okhttp3.CallEvent.RequestHeadersStart\nimport okhttp3.CallEvent.ResponseBodyEnd\nimport okhttp3.CallEvent.ResponseBodyStart\nimport okhttp3.CallEvent.ResponseHeadersEnd\nimport okhttp3.CallEvent.ResponseHeadersStart\nimport okhttp3.EventRecorder\nimport okhttp3.Headers\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Request\nimport okhttp3.sse.EventSource\nimport okhttp3.sse.EventSources.createFactory\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport org.junitpioneer.jupiter.RetryingTest\n\n@Tag(\"Slowish\")\nclass EventSourceHttpTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n  private val eventRecorder = EventRecorder()\n  private val listener = EventSourceRecorder()\n  private var client =\n    clientTestRule\n      .newClientBuilder()\n      .eventListenerFactory(clientTestRule.wrap(eventRecorder))\n      .build()\n\n  @AfterEach\n  fun after() {\n    listener.assertExhausted()\n  }\n\n  @Test\n  fun event() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/event-stream\")\n        .build(),\n    )\n    val source = newEventSource()\n    assertThat(source.request().url.encodedPath).isEqualTo(\"/\")\n    listener.assertOpen()\n    listener.assertEvent(null, null, \"hey\")\n    listener.assertClose()\n  }\n\n  @RetryingTest(5)\n  fun cancelInEventShortCircuits() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/event-stream\")\n        .build(),\n    )\n    listener.enqueueCancel() // Will cancel in onOpen().\n    newEventSource()\n    listener.assertOpen()\n    listener.assertFailure(\"canceled\")\n  }\n\n  @Test\n  fun badContentType() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/plain\")\n        .build(),\n    )\n    newEventSource()\n    listener.assertFailure(\"Invalid content-type: text/plain\")\n  }\n\n  @Test\n  fun badResponseCode() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/event-stream\")\n        .code(401)\n        .build(),\n    )\n    newEventSource()\n    listener.assertFailure(null)\n  }\n\n  @Test\n  fun fullCallTimeoutDoesNotApplyOnceConnected() {\n    client =\n      client\n        .newBuilder()\n        .callTimeout(250, TimeUnit.MILLISECONDS)\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .bodyDelay(500, TimeUnit.MILLISECONDS)\n        .setHeader(\"content-type\", \"text/event-stream\")\n        .body(\"data: hey\\n\\n\")\n        .build(),\n    )\n    val source = newEventSource()\n    assertThat(source.request().url.encodedPath).isEqualTo(\"/\")\n    listener.assertOpen()\n    listener.assertEvent(null, null, \"hey\")\n    listener.assertClose()\n  }\n\n  @Test\n  fun fullCallTimeoutAppliesToSetup() {\n    client =\n      client\n        .newBuilder()\n        .callTimeout(250, TimeUnit.MILLISECONDS)\n        .build()\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .headersDelay(500, TimeUnit.MILLISECONDS)\n        .setHeader(\"content-type\", \"text/event-stream\")\n        .body(\"data: hey\\n\\n\")\n        .build(),\n    )\n    newEventSource()\n    listener.assertFailure(\"timeout\")\n  }\n\n  @Test\n  fun retainsAccept() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/event-stream\")\n        .build(),\n    )\n    newEventSource(\"text/plain\")\n    listener.assertOpen()\n    listener.assertEvent(null, null, \"hey\")\n    listener.assertClose()\n    assertThat(server.takeRequest().headers[\"Accept\"]).isEqualTo(\"text/plain\")\n  }\n\n  @Test\n  fun setsMissingAccept() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/event-stream\")\n        .build(),\n    )\n    newEventSource()\n    listener.assertOpen()\n    listener.assertEvent(null, null, \"hey\")\n    listener.assertClose()\n    assertThat(server.takeRequest().headers[\"Accept\"])\n      .isEqualTo(\"text/event-stream\")\n  }\n\n  @Test\n  fun eventListenerEvents() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/event-stream\")\n        .build(),\n    )\n    val source = newEventSource()\n    assertThat(source.request().url.encodedPath).isEqualTo(\"/\")\n    listener.assertOpen()\n    listener.assertEvent(null, null, \"hey\")\n    listener.assertClose()\n    assertThat(eventRecorder.recordedEventTypes()).containsExactly(\n      CallStart::class,\n      ProxySelectStart::class,\n      ProxySelectEnd::class,\n      DnsStart::class,\n      DnsEnd::class,\n      ConnectStart::class,\n      ConnectEnd::class,\n      ConnectionAcquired::class,\n      RequestHeadersStart::class,\n      RequestHeadersEnd::class,\n      ResponseHeadersStart::class,\n      ResponseHeadersEnd::class,\n      FollowUpDecision::class,\n      ResponseBodyStart::class,\n      ResponseBodyEnd::class,\n      ConnectionReleased::class,\n      CallEnd::class,\n    )\n  }\n\n  @Test\n  fun sseReauths() {\n    client =\n      client\n        .newBuilder()\n        .authenticator { route, response ->\n          response.request\n            .newBuilder()\n            .header(\"Authorization\", \"XYZ\")\n            .build()\n        }.build()\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        body = \"{\\\"error\\\":{\\\"message\\\":\\\"No auth credentials found\\\",\\\"code\\\":401}}\",\n        headers = Headers.headersOf(\"content-type\", \"application/json\"),\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        body =\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        headers = Headers.headersOf(\"content-type\", \"text/event-stream\"),\n      ),\n    )\n    val source = newEventSource()\n    assertThat(source.request().url.encodedPath).isEqualTo(\"/\")\n    listener.assertOpen()\n    listener.assertEvent(null, null, \"hey\")\n    listener.assertClose()\n  }\n\n  @Test\n  fun sseWithoutAuthenticator() {\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        body = \"{\\\"error\\\":{\\\"message\\\":\\\"No auth credentials found\\\",\\\"code\\\":401}}\",\n        headers = Headers.headersOf(\"content-type\", \"application/json\"),\n      ),\n    )\n    val source = newEventSource()\n    assertThat(source.request().url.encodedPath).isEqualTo(\"/\")\n    listener.assertFailure(code = 401, message = \"{\\\"error\\\":{\\\"message\\\":\\\"No auth credentials found\\\",\\\"code\\\":401}}\")\n  }\n\n  private fun newEventSource(accept: String? = null): EventSource {\n    val builder =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n    if (accept != null) {\n      builder.header(\"Accept\", accept)\n    }\n    val request = builder.build()\n    val factory = createFactory(client)\n    return factory.newEventSource(request, listener)\n  }\n}\n"
  },
  {
    "path": "okhttp-sse/src/test/java/okhttp3/sse/internal/EventSourceRecorder.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse.internal\n\nimport assertk.assertThat\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport java.util.concurrent.LinkedBlockingDeque\nimport java.util.concurrent.TimeUnit\nimport okhttp3.Response\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.Platform.Companion.get\nimport okhttp3.sse.EventSource\nimport okhttp3.sse.EventSourceListener\n\nclass EventSourceRecorder : EventSourceListener() {\n  private val events = LinkedBlockingDeque<Any>()\n  private var cancel = false\n\n  fun enqueueCancel() {\n    cancel = true\n  }\n\n  override fun onOpen(\n    eventSource: EventSource,\n    response: Response,\n  ) {\n    get().log(\"[ES] onOpen\", Platform.INFO, null)\n    events.add(Open(eventSource, response))\n    drainCancelQueue(eventSource)\n  }\n\n  override fun onEvent(\n    eventSource: EventSource,\n    id: String?,\n    type: String?,\n    data: String,\n  ) {\n    get().log(\"[ES] onEvent\", Platform.INFO, null)\n    events.add(Event(id, type, data))\n    drainCancelQueue(eventSource)\n  }\n\n  override fun onClosed(eventSource: EventSource) {\n    get().log(\"[ES] onClosed\", Platform.INFO, null)\n    events.add(Closed)\n    drainCancelQueue(eventSource)\n  }\n\n  override fun onFailure(\n    eventSource: EventSource,\n    t: Throwable?,\n    response: Response?,\n  ) {\n    get().log(\"[ES] onFailure\", Platform.INFO, t)\n    events.add(Failure(t, response, t?.message ?: response?.body?.string()))\n    drainCancelQueue(eventSource)\n  }\n\n  private fun drainCancelQueue(eventSource: EventSource) {\n    if (cancel) {\n      cancel = false\n      eventSource.cancel()\n    }\n  }\n\n  private fun nextEvent(): Any =\n    events.poll(10, TimeUnit.SECONDS)\n      ?: throw AssertionError(\"Timed out waiting for event.\")\n\n  fun assertExhausted() {\n    assertThat(events).isEmpty()\n  }\n\n  fun assertEvent(\n    id: String?,\n    type: String?,\n    data: String,\n  ) {\n    assertThat(nextEvent()).isEqualTo(Event(id, type, data))\n  }\n\n  fun assertOpen(): EventSource {\n    val event = nextEvent() as Open\n    return event.eventSource\n  }\n\n  fun assertClose() {\n    nextEvent() as Closed\n  }\n\n  fun assertFailure(\n    message: String?,\n    code: Int? = null,\n  ) {\n    val event = nextEvent() as Failure\n    if (code != null) {\n      assertThat(event.response?.code).isEqualTo(code)\n    }\n    if (message != null) {\n      assertThat(event.message).isEqualTo(message)\n    } else {\n      assertThat(event.t).isNull()\n    }\n  }\n\n  internal data class Open(\n    val eventSource: EventSource,\n    val response: Response,\n  )\n\n  internal data class Failure(\n    val t: Throwable?,\n    val response: Response?,\n    val message: String?,\n  )\n\n  internal object Closed\n}\n"
  },
  {
    "path": "okhttp-sse/src/test/java/okhttp3/sse/internal/EventSourcesHttpTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse.internal\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.Headers\nimport okhttp3.OkHttpClientTestRule\nimport okhttp3.Request\nimport okhttp3.sse.EventSources.processResponse\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n@Tag(\"Slowish\")\nclass EventSourcesHttpTest {\n  @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @RegisterExtension\n  val clientTestRule = OkHttpClientTestRule()\n\n  private val listener = EventSourceRecorder()\n  private val client = clientTestRule.newClient()\n\n  @AfterEach\n  fun after() {\n    listener.assertExhausted()\n  }\n\n  @Test\n  fun processResponse() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/event-stream\")\n        .build(),\n    )\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val response = client.newCall(request).execute()\n    processResponse(response, listener)\n    listener.assertOpen()\n    listener.assertEvent(null, null, \"hey\")\n    listener.assertClose()\n  }\n\n  @Test\n  fun cancelShortCircuits() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        ).setHeader(\"content-type\", \"text/event-stream\")\n        .build(),\n    )\n    listener.enqueueCancel() // Will cancel in onOpen().\n    val request = Request.Builder().url(server.url(\"/\")).build()\n    val response = client.newCall(request).execute()\n    processResponse(response, listener)\n    listener.assertOpen()\n    listener.assertFailure(\"canceled\")\n  }\n\n  @Test\n  fun failureWith401IsReadable() {\n    server.enqueue(\n      MockResponse(\n        code = 401,\n        body = \"{\\\"error\\\":{\\\"message\\\":\\\"No auth credentials found\\\",\\\"code\\\":401}}\",\n        headers = Headers.headersOf(\"content-type\", \"application/json\"),\n      ),\n    )\n    server.enqueue(\n      MockResponse(\n        body =\n          \"\"\"\n          |data: hey\n          |\n          |\n          \"\"\".trimMargin(),\n        headers = Headers.headersOf(\"content-type\", \"text/event-stream\"),\n      ),\n    )\n\n    val request1 = Request.Builder().url(server.url(\"/\")).build()\n    val response1 = client.newCall(request1).execute()\n    assertThat(response1.code).isEqualTo(401)\n    assertThat(response1.body.string())\n      .isEqualTo(\"{\\\"error\\\":{\\\"message\\\":\\\"No auth credentials found\\\",\\\"code\\\":401}}\")\n\n    val request2 = request1.newBuilder().header(\"Authorization\", \"XYZ\").build()\n    val response2 = client.newCall(request2).execute()\n    processResponse(response2, listener)\n    listener.assertOpen()\n    listener.assertEvent(null, null, \"hey\")\n    listener.assertClose()\n  }\n}\n"
  },
  {
    "path": "okhttp-sse/src/test/java/okhttp3/sse/internal/ServerSentEventIteratorTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sse.internal\n\nimport assertk.assertThat\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport java.util.ArrayDeque\nimport java.util.Deque\nimport okio.Buffer\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\n\nclass ServerSentEventIteratorTest {\n  /** Either [Event] or [Long] items for events and retry changes, respectively.  */\n  private val callbacks: Deque<Any> = ArrayDeque()\n\n  @AfterEach\n  fun after() {\n    assertThat(callbacks).isEmpty()\n  }\n\n  @Test\n  fun multiline() {\n    consumeEvents(\n      \"\"\"\n      |data: YHOO\n      |data: +2\n      |data: 10\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"YHOO\\n+2\\n10\"))\n  }\n\n  @Test\n  fun multilineCr() {\n    consumeEvents(\n      \"\"\"\n      |data: YHOO\n      |data: +2\n      |data: 10\n      |\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\"),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"YHOO\\n+2\\n10\"))\n  }\n\n  @Test\n  fun multilineCrLf() {\n    consumeEvents(\n      \"\"\"\n      |data: YHOO\n      |data: +2\n      |data: 10\n      |\n      |\n      \"\"\".trimMargin().replace(\"\\n\", \"\\r\\n\"),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"YHOO\\n+2\\n10\"))\n  }\n\n  @Test\n  fun eventType() {\n    consumeEvents(\n      \"\"\"\n      |event: add\n      |data: 73857293\n      |\n      |event: remove\n      |data: 2153\n      |\n      |event: add\n      |data: 113411\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, \"add\", \"73857293\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(null, \"remove\", \"2153\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(null, \"add\", \"113411\"))\n  }\n\n  @Test\n  fun commentsIgnored() {\n    consumeEvents(\n      \"\"\"\n      |: test stream\n      |\n      |data: first event\n      |id: 1\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(\"1\", null, \"first event\"))\n  }\n\n  @Test\n  fun idCleared() {\n    consumeEvents(\n      \"\"\"\n      |data: first event\n      |id: 1\n      |\n      |data: second event\n      |id\n      |\n      |data: third event\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(\"1\", null, \"first event\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"second event\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"third event\"))\n  }\n\n  @Test\n  fun nakedFieldNames() {\n    consumeEvents(\n      \"\"\"\n      |data\n      |\n      |data\n      |data\n      |\n      |data:\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"\\n\"))\n  }\n\n  @Test\n  fun colonSpaceOptional() {\n    consumeEvents(\n      \"\"\"\n      |data:test\n      |\n      |data: test\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"test\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"test\"))\n  }\n\n  @Test\n  fun leadingWhitespace() {\n    consumeEvents(\n      \"\"\"\n      |data:  test\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \" test\"))\n  }\n\n  @Test\n  fun idReusedAcrossEvents() {\n    consumeEvents(\n      \"\"\"\n      |data: first event\n      |id: 1\n      |\n      |data: second event\n      |\n      |id: 2\n      |data: third event\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(\"1\", null, \"first event\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(\"1\", null, \"second event\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(\"2\", null, \"third event\"))\n  }\n\n  @Test\n  fun idIgnoredFromEmptyEvent() {\n    consumeEvents(\n      \"\"\"\n      |data: first event\n      |id: 1\n      |\n      |id: 2\n      |\n      |data: second event\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(\"1\", null, \"first event\"))\n    assertThat(callbacks.remove()).isEqualTo(Event(\"1\", null, \"second event\"))\n  }\n\n  @Test\n  fun retry() {\n    consumeEvents(\n      \"\"\"\n      |retry: 22\n      |\n      |data: first event\n      |id: 1\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(22L)\n    assertThat(callbacks.remove()).isEqualTo(Event(\"1\", null, \"first event\"))\n  }\n\n  @Test\n  fun retryInvalidFormatIgnored() {\n    consumeEvents(\n      \"\"\"\n      |retry: 22\n      |\n      |retry: hey\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(22L)\n  }\n\n  @Test\n  fun namePrefixIgnored() {\n    consumeEvents(\n      \"\"\"\n      |data: a\n      |eventually\n      |database\n      |identity\n      |retrying\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"a\"))\n  }\n\n  @Test\n  fun nakedNameClearsIdAndTypeAppendsData() {\n    consumeEvents(\n      \"\"\"\n      |id: a\n      |event: b\n      |data: c\n      |id\n      |event\n      |data\n      |\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks.remove()).isEqualTo(Event(null, null, \"c\\n\"))\n  }\n\n  @Test\n  fun nakedRetryIgnored() {\n    consumeEvents(\n      \"\"\"\n      |retry\n      |\n      \"\"\".trimMargin(),\n    )\n    assertThat(callbacks).isEmpty()\n  }\n\n  private fun consumeEvents(source: String) {\n    val callback: ServerSentEventReader.Callback =\n      object : ServerSentEventReader.Callback {\n        override fun onEvent(\n          id: String?,\n          type: String?,\n          data: String,\n        ) {\n          callbacks.add(Event(id, type, data))\n        }\n\n        override fun onRetryChange(timeMs: Long) {\n          callbacks.add(timeMs)\n        }\n      }\n    val buffer = Buffer().writeUtf8(source)\n    val reader = ServerSentEventReader(buffer, callback)\n    while (reader.processNextEvent()) {\n    }\n    assertThat(buffer.size, \"Unconsumed buffer: ${buffer.readUtf8()}\")\n      .isEqualTo(0)\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/README.md",
    "content": "OkHttp Testing Support\n======================\n\nThis module offers utilities and support for testing OkHttp itself. It's not intended for use by\nother projects or consumers of the OkHttp library.\n"
  },
  {
    "path": "okhttp-testing-support/build.gradle.kts",
    "content": "import org.gradle.internal.os.OperatingSystem\nplugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\ndependencies {\n  api(libs.square.okio)\n  api(projects.mockwebserver3)\n  \"friendsApi\"(projects.okhttp)\n  api(projects.okhttpTls)\n  api(libs.assertk)\n  api(libs.bouncycastle.bcprov)\n  implementation(libs.bouncycastle.bcpkix)\n  implementation(libs.bouncycastle.bctls)\n  api(libs.conscrypt.openjdk)\n  api(libs.openjsse)\n\n  api(libs.junit.jupiter.engine)\n\n  // This runs Corretto on macOS (aarch64) and Linux (x86_64). We don't test Corretto on other\n  // operating systems or architectures.\n  api(\n    variantOf(libs.amazon.corretto) {\n      classifier(\n        when {\n          OperatingSystem.current().isMacOsX -> \"osx-aarch_64\"\n          OperatingSystem.current().isLinux -> \"linux-x86_64\"\n          else -> \"linux-x86_64\" // Code that references Corretto will build but not run.\n        },\n      )\n    },\n  )\n\n  api(libs.hamcrest.library)\n  api(libs.junit.jupiter.api)\n  api(libs.junit.jupiter.params)\n\n  api(libs.junit.pioneer)\n\n  compileOnly(libs.robolectric.android)\n\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n}\n\nanimalsniffer {\n  isIgnoreFailures = true\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/CallEvent.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport okhttp3.internal.SuppressSignatureCheck\n\n/** Data classes that correspond to each of the methods of [EventListener]. */\n@SuppressSignatureCheck\nsealed class CallEvent {\n  abstract val timestampNs: Long\n  abstract val call: Call\n\n  val name: String\n    get() = javaClass.simpleName\n\n  /** Returns if the event closes this event, or null if this is no open event. */\n  open fun closes(event: CallEvent): Boolean? = null\n\n  data class DispatcherQueueStart(\n    override val timestampNs: Long,\n    override val call: Call,\n    val dispatcher: Dispatcher,\n  ) : CallEvent()\n\n  data class DispatcherQueueEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val dispatcher: Dispatcher,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is DispatcherQueueStart && call == event.call\n  }\n\n  data class ProxySelectStart(\n    override val timestampNs: Long,\n    override val call: Call,\n    val url: HttpUrl,\n  ) : CallEvent()\n\n  data class ProxySelectEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val url: HttpUrl,\n    val proxies: List<Proxy>?,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is ProxySelectStart && call == event.call && url == event.url\n  }\n\n  data class DnsStart(\n    override val timestampNs: Long,\n    override val call: Call,\n    val domainName: String,\n  ) : CallEvent()\n\n  data class DnsEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val domainName: String,\n    val inetAddressList: List<InetAddress>,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is DnsStart && call == event.call && domainName == event.domainName\n  }\n\n  data class ConnectStart(\n    override val timestampNs: Long,\n    override val call: Call,\n    val inetSocketAddress: InetSocketAddress,\n    val proxy: Proxy?,\n  ) : CallEvent()\n\n  data class ConnectEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val inetSocketAddress: InetSocketAddress,\n    val proxy: Proxy?,\n    val protocol: Protocol?,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean =\n      event is ConnectStart && call == event.call && inetSocketAddress == event.inetSocketAddress && proxy == event.proxy\n  }\n\n  data class ConnectFailed(\n    override val timestampNs: Long,\n    override val call: Call,\n    val inetSocketAddress: InetSocketAddress,\n    val proxy: Proxy,\n    val protocol: Protocol?,\n    val ioe: IOException,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean =\n      event is ConnectStart && call == event.call && inetSocketAddress == event.inetSocketAddress && proxy == event.proxy\n  }\n\n  data class SecureConnectStart(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class SecureConnectEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val handshake: Handshake?,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is SecureConnectStart && call == event.call\n  }\n\n  data class ConnectionAcquired(\n    override val timestampNs: Long,\n    override val call: Call,\n    val connection: Connection,\n  ) : CallEvent()\n\n  data class ConnectionReleased(\n    override val timestampNs: Long,\n    override val call: Call,\n    val connection: Connection,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is ConnectionAcquired && call == event.call && connection == event.connection\n  }\n\n  data class CallStart(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class CallEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is CallStart && call == event.call\n  }\n\n  data class CallFailed(\n    override val timestampNs: Long,\n    override val call: Call,\n    val ioe: IOException,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is CallStart && call == event.call\n  }\n\n  data class Canceled(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class RequestHeadersStart(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class RequestHeadersEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val headerLength: Long,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is RequestHeadersStart && call == event.call\n  }\n\n  data class RequestBodyStart(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class RequestBodyEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val bytesWritten: Long,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is RequestBodyStart && call == event.call\n  }\n\n  data class RequestFailed(\n    override val timestampNs: Long,\n    override val call: Call,\n    val ioe: IOException,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is RequestHeadersStart && call == event.call\n  }\n\n  data class ResponseHeadersStart(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class ResponseHeadersEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val headerLength: Long,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is ResponseHeadersStart && call == event.call\n  }\n\n  data class ResponseBodyStart(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class ResponseBodyEnd(\n    override val timestampNs: Long,\n    override val call: Call,\n    val bytesRead: Long,\n  ) : CallEvent() {\n    override fun closes(event: CallEvent): Boolean = event is ResponseBodyStart && call == event.call\n  }\n\n  data class ResponseFailed(\n    override val timestampNs: Long,\n    override val call: Call,\n    val ioe: IOException,\n  ) : CallEvent()\n\n  data class SatisfactionFailure(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class CacheHit(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class CacheMiss(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class CacheConditionalHit(\n    override val timestampNs: Long,\n    override val call: Call,\n  ) : CallEvent()\n\n  data class RetryDecision(\n    override val timestampNs: Long,\n    override val call: Call,\n    val exception: IOException,\n    val retry: Boolean,\n  ) : CallEvent()\n\n  data class FollowUpDecision(\n    override val timestampNs: Long,\n    override val call: Call,\n    val networkResponse: Response,\n    val nextRequest: Request?,\n  ) : CallEvent()\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/ClientRuleEventListener.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.util.concurrent.TimeUnit\n\nclass ClientRuleEventListener(\n  var logger: (String) -> Unit,\n) : EventListener(),\n  EventListener.Factory {\n  private var startNs: Long? = null\n\n  override fun create(call: Call): EventListener = this\n\n  override fun callStart(call: Call) {\n    startNs = System.nanoTime()\n\n    logWithTime(\"callStart: ${call.request()}\")\n  }\n\n  override fun dispatcherQueueStart(\n    call: Call,\n    dispatcher: Dispatcher,\n  ) {\n    logWithTime(\"dispatcherQueueStart: queuedCallsCount=${dispatcher.queuedCallsCount()}\")\n  }\n\n  override fun dispatcherQueueEnd(\n    call: Call,\n    dispatcher: Dispatcher,\n  ) {\n    logWithTime(\"dispatcherQueueEnd: queuedCallsCount=${dispatcher.queuedCallsCount()}\")\n  }\n\n  override fun proxySelectStart(\n    call: Call,\n    url: HttpUrl,\n  ) {\n    logWithTime(\"proxySelectStart: $url\")\n  }\n\n  override fun proxySelectEnd(\n    call: Call,\n    url: HttpUrl,\n    proxies: List<Proxy>,\n  ) {\n    logWithTime(\"proxySelectEnd: $proxies\")\n  }\n\n  override fun dnsStart(\n    call: Call,\n    domainName: String,\n  ) {\n    logWithTime(\"dnsStart: $domainName\")\n  }\n\n  override fun dnsEnd(\n    call: Call,\n    domainName: String,\n    inetAddressList: List<InetAddress>,\n  ) {\n    logWithTime(\"dnsEnd: $inetAddressList\")\n  }\n\n  override fun connectStart(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n  ) {\n    logWithTime(\"connectStart: $inetSocketAddress $proxy\")\n  }\n\n  override fun secureConnectStart(call: Call) {\n    logWithTime(\"secureConnectStart\")\n  }\n\n  override fun secureConnectEnd(\n    call: Call,\n    handshake: Handshake?,\n  ) {\n    logWithTime(\"secureConnectEnd: $handshake\")\n  }\n\n  override fun connectEnd(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n    protocol: Protocol?,\n  ) {\n    logWithTime(\"connectEnd: $protocol\")\n  }\n\n  override fun connectFailed(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n    protocol: Protocol?,\n    ioe: IOException,\n  ) {\n    logWithTime(\"connectFailed: $protocol $ioe\")\n  }\n\n  override fun connectionAcquired(\n    call: Call,\n    connection: Connection,\n  ) {\n    logWithTime(\"connectionAcquired: $connection\")\n  }\n\n  override fun connectionReleased(\n    call: Call,\n    connection: Connection,\n  ) {\n    logWithTime(\"connectionReleased\")\n  }\n\n  override fun requestHeadersStart(call: Call) {\n    logWithTime(\"requestHeadersStart\")\n  }\n\n  override fun requestHeadersEnd(\n    call: Call,\n    request: Request,\n  ) {\n    logWithTime(\"requestHeadersEnd\")\n  }\n\n  override fun requestBodyStart(call: Call) {\n    logWithTime(\"requestBodyStart\")\n  }\n\n  override fun requestBodyEnd(\n    call: Call,\n    byteCount: Long,\n  ) {\n    logWithTime(\"requestBodyEnd: byteCount=$byteCount\")\n  }\n\n  override fun requestFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n    logWithTime(\"requestFailed: $ioe\")\n  }\n\n  override fun responseHeadersStart(call: Call) {\n    logWithTime(\"responseHeadersStart\")\n  }\n\n  override fun responseHeadersEnd(\n    call: Call,\n    response: Response,\n  ) {\n    logWithTime(\"responseHeadersEnd: $response\")\n  }\n\n  override fun responseBodyStart(call: Call) {\n    logWithTime(\"responseBodyStart\")\n  }\n\n  override fun responseBodyEnd(\n    call: Call,\n    byteCount: Long,\n  ) {\n    logWithTime(\"responseBodyEnd: byteCount=$byteCount\")\n  }\n\n  override fun responseFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n    logWithTime(\"responseFailed: $ioe\")\n  }\n\n  override fun callEnd(call: Call) {\n    logWithTime(\"callEnd\")\n  }\n\n  override fun callFailed(\n    call: Call,\n    ioe: IOException,\n  ) {\n    logWithTime(\"callFailed: $ioe\")\n  }\n\n  override fun canceled(call: Call) {\n    logWithTime(\"canceled\")\n  }\n\n  override fun satisfactionFailure(\n    call: Call,\n    response: Response,\n  ) {\n    logWithTime(\"satisfactionFailure\")\n  }\n\n  override fun cacheMiss(call: Call) {\n    logWithTime(\"cacheMiss\")\n  }\n\n  override fun cacheHit(\n    call: Call,\n    response: Response,\n  ) {\n    logWithTime(\"cacheHit\")\n  }\n\n  override fun cacheConditionalHit(\n    call: Call,\n    cachedResponse: Response,\n  ) {\n    logWithTime(\"cacheConditionalHit\")\n  }\n\n  override fun retryDecision(\n    call: Call,\n    exception: IOException,\n    retry: Boolean,\n  ) {\n    logWithTime(\"retryDecision\")\n  }\n\n  override fun followUpDecision(\n    call: Call,\n    networkResponse: Response,\n    nextRequest: Request?,\n  ) {\n    logWithTime(\"followUpDecision\")\n  }\n\n  private fun logWithTime(message: String) {\n    val startNs = startNs\n    val timeMs =\n      if (startNs == null) {\n        // Event occurred before start, for an example an early cancel.\n        0L\n      } else {\n        TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)\n      }\n\n    logger.invoke(\"[$timeMs ms] $message\")\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/ConnectionEvent.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\n  \"CANNOT_OVERRIDE_INVISIBLE_MEMBER\",\n  \"INVISIBLE_MEMBER\",\n  \"INVISIBLE_REFERENCE\",\n)\n\npackage okhttp3\n\nimport java.io.IOException\nimport okhttp3.internal.SuppressSignatureCheck\n\n/** Data classes that correspond to each of the methods of [ConnectionListener]. */\n@SuppressSignatureCheck\nsealed class ConnectionEvent {\n  abstract val timestampNs: Long\n  open val connection: Connection?\n    get() = null\n\n  /** Returns if the event closes this event, or null if this is no open event. */\n  open fun closes(event: ConnectionEvent): Boolean? = null\n\n  val name: String\n    get() = javaClass.simpleName\n\n  data class ConnectStart(\n    override val timestampNs: Long,\n    val route: Route,\n    val call: Call,\n  ) : ConnectionEvent()\n\n  data class ConnectFailed(\n    override val timestampNs: Long,\n    val route: Route,\n    val call: Call,\n    val exception: IOException,\n  ) : ConnectionEvent() {\n    override fun closes(event: ConnectionEvent): Boolean = event is ConnectStart && call == event.call && route == event.route\n  }\n\n  data class ConnectEnd(\n    override val timestampNs: Long,\n    override val connection: Connection,\n    val route: Route,\n    val call: Call,\n  ) : ConnectionEvent() {\n    override fun closes(event: ConnectionEvent): Boolean = event is ConnectStart && call == event.call && route == event.route\n  }\n\n  data class ConnectionClosed(\n    override val timestampNs: Long,\n    override val connection: Connection,\n  ) : ConnectionEvent()\n\n  data class ConnectionAcquired(\n    override val timestampNs: Long,\n    override val connection: Connection,\n    val call: Call,\n  ) : ConnectionEvent()\n\n  data class ConnectionReleased(\n    override val timestampNs: Long,\n    override val connection: Connection,\n    val call: Call,\n  ) : ConnectionEvent() {\n    override fun closes(event: ConnectionEvent): Boolean =\n      event is ConnectionAcquired && connection == event.connection && call == event.call\n  }\n\n  data class NoNewExchanges(\n    override val timestampNs: Long,\n    override val connection: Connection,\n  ) : ConnectionEvent()\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/DelegatingSSLSession.kt",
    "content": "/*\n * Copyright 2019 Square Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"DEPRECATION\")\n\npackage okhttp3\n\nimport java.security.Principal\nimport java.security.cert.Certificate\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSession\nimport javax.net.ssl.SSLSessionContext\nimport javax.security.cert.X509Certificate\n\n/** An [SSLSession] that delegates all calls.  */\nabstract class DelegatingSSLSession(\n  protected val delegate: SSLSession?,\n) : SSLSession {\n  override fun getId(): ByteArray = delegate!!.id\n\n  override fun getSessionContext(): SSLSessionContext = delegate!!.sessionContext\n\n  override fun getCreationTime(): Long = delegate!!.creationTime\n\n  override fun getLastAccessedTime(): Long = delegate!!.lastAccessedTime\n\n  override fun invalidate() {\n    delegate!!.invalidate()\n  }\n\n  override fun isValid(): Boolean = delegate!!.isValid\n\n  override fun putValue(\n    s: String,\n    o: Any,\n  ) {\n    delegate!!.putValue(s, o)\n  }\n\n  override fun getValue(s: String): Any = delegate!!.getValue(s)\n\n  override fun removeValue(s: String) {\n    delegate!!.removeValue(s)\n  }\n\n  override fun getValueNames(): Array<String> = delegate!!.valueNames\n\n  @Throws(SSLPeerUnverifiedException::class)\n  override fun getPeerCertificates(): Array<Certificate>? = delegate!!.peerCertificates\n\n  override fun getLocalCertificates(): Array<Certificate>? = delegate!!.localCertificates\n\n  @Suppress(\"removal\", \"OVERRIDE_DEPRECATION\")\n  @Throws(SSLPeerUnverifiedException::class)\n  override fun getPeerCertificateChain(): Array<X509Certificate> = delegate!!.peerCertificateChain\n\n  @Throws(SSLPeerUnverifiedException::class)\n  override fun getPeerPrincipal(): Principal = delegate!!.peerPrincipal\n\n  override fun getLocalPrincipal(): Principal = delegate!!.localPrincipal\n\n  override fun getCipherSuite(): String = delegate!!.cipherSuite\n\n  override fun getProtocol(): String = delegate!!.protocol\n\n  override fun getPeerHost(): String = delegate!!.peerHost\n\n  override fun getPeerPort(): Int = delegate!!.peerPort\n\n  override fun getPacketBufferSize(): Int = delegate!!.packetBufferSize\n\n  override fun getApplicationBufferSize(): Int = delegate!!.applicationBufferSize\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/DelegatingSSLSocket.kt",
    "content": "/*\n * Copyright 2014 Square Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.InetAddress\nimport java.net.SocketAddress\nimport java.net.SocketException\nimport java.nio.channels.SocketChannel\nimport java.util.function.BiFunction\nimport javax.net.ssl.HandshakeCompletedListener\nimport javax.net.ssl.SSLParameters\nimport javax.net.ssl.SSLSession\nimport javax.net.ssl.SSLSocket\n\n/** An [SSLSocket] that delegates all calls.  */\nabstract class DelegatingSSLSocket(\n  protected val delegate: SSLSocket?,\n) : SSLSocket() {\n  @Throws(IOException::class)\n  override fun shutdownInput() {\n    delegate!!.shutdownInput()\n  }\n\n  @Throws(IOException::class)\n  override fun shutdownOutput() {\n    delegate!!.shutdownOutput()\n  }\n\n  override fun getSupportedCipherSuites(): Array<String> = delegate!!.supportedCipherSuites\n\n  override fun getEnabledCipherSuites(): Array<String> = delegate!!.enabledCipherSuites\n\n  override fun setEnabledCipherSuites(suites: Array<String>) {\n    delegate!!.enabledCipherSuites = suites\n  }\n\n  override fun getSupportedProtocols(): Array<String> = delegate!!.supportedProtocols\n\n  override fun getEnabledProtocols(): Array<String> = delegate!!.enabledProtocols\n\n  override fun setEnabledProtocols(protocols: Array<String>) {\n    delegate!!.enabledProtocols = protocols\n  }\n\n  override fun getSession(): SSLSession = delegate!!.session\n\n  override fun addHandshakeCompletedListener(listener: HandshakeCompletedListener) {\n    delegate!!.addHandshakeCompletedListener(listener)\n  }\n\n  override fun removeHandshakeCompletedListener(listener: HandshakeCompletedListener) {\n    delegate!!.removeHandshakeCompletedListener(listener)\n  }\n\n  @Throws(IOException::class)\n  override fun startHandshake() {\n    delegate!!.startHandshake()\n  }\n\n  override fun setUseClientMode(mode: Boolean) {\n    delegate!!.useClientMode = mode\n  }\n\n  override fun getUseClientMode(): Boolean = delegate!!.useClientMode\n\n  override fun setNeedClientAuth(need: Boolean) {\n    delegate!!.needClientAuth = need\n  }\n\n  override fun setWantClientAuth(want: Boolean) {\n    delegate!!.wantClientAuth = want\n  }\n\n  override fun getNeedClientAuth(): Boolean = delegate!!.needClientAuth\n\n  override fun getWantClientAuth(): Boolean = delegate!!.wantClientAuth\n\n  override fun setEnableSessionCreation(flag: Boolean) {\n    delegate!!.enableSessionCreation = flag\n  }\n\n  override fun getEnableSessionCreation(): Boolean = delegate!!.enableSessionCreation\n\n  override fun getSSLParameters(): SSLParameters = delegate!!.sslParameters\n\n  override fun setSSLParameters(p: SSLParameters) {\n    delegate!!.sslParameters = p\n  }\n\n  @Throws(IOException::class)\n  override fun close() {\n    delegate!!.close()\n  }\n\n  override fun getInetAddress(): InetAddress = delegate!!.inetAddress\n\n  @Throws(IOException::class)\n  override fun getInputStream(): InputStream = delegate!!.inputStream\n\n  @Throws(SocketException::class)\n  override fun getKeepAlive(): Boolean = delegate!!.keepAlive\n\n  override fun getLocalAddress(): InetAddress = delegate!!.localAddress\n\n  override fun getLocalPort(): Int = delegate!!.localPort\n\n  @Throws(IOException::class)\n  override fun getOutputStream(): OutputStream = delegate!!.outputStream\n\n  override fun getPort(): Int = delegate!!.port\n\n  @Throws(SocketException::class)\n  override fun getSoLinger(): Int = delegate!!.soLinger\n\n  @Throws(SocketException::class)\n  override fun getReceiveBufferSize(): Int = delegate!!.receiveBufferSize\n\n  @Throws(SocketException::class)\n  override fun getSendBufferSize(): Int = delegate!!.sendBufferSize\n\n  @Throws(SocketException::class)\n  override fun getSoTimeout(): Int = delegate!!.soTimeout\n\n  @Throws(SocketException::class)\n  override fun getTcpNoDelay(): Boolean = delegate!!.tcpNoDelay\n\n  @Throws(SocketException::class)\n  override fun setKeepAlive(keepAlive: Boolean) {\n    delegate!!.keepAlive = keepAlive\n  }\n\n  @Throws(SocketException::class)\n  override fun setSendBufferSize(size: Int) {\n    delegate!!.sendBufferSize = size\n  }\n\n  @Throws(SocketException::class)\n  override fun setReceiveBufferSize(size: Int) {\n    delegate!!.receiveBufferSize = size\n  }\n\n  @Throws(SocketException::class)\n  override fun setSoLinger(\n    on: Boolean,\n    timeout: Int,\n  ) {\n    delegate!!.setSoLinger(on, timeout)\n  }\n\n  @Throws(SocketException::class)\n  override fun setSoTimeout(timeout: Int) {\n    delegate!!.soTimeout = timeout\n  }\n\n  @Throws(SocketException::class)\n  override fun setTcpNoDelay(on: Boolean) {\n    delegate!!.tcpNoDelay = on\n  }\n\n  override fun toString(): String = delegate!!.toString()\n\n  override fun getLocalSocketAddress(): SocketAddress = delegate!!.localSocketAddress\n\n  override fun getRemoteSocketAddress(): SocketAddress = delegate!!.remoteSocketAddress\n\n  override fun isBound(): Boolean = delegate!!.isBound\n\n  override fun isConnected(): Boolean = delegate!!.isConnected\n\n  override fun isClosed(): Boolean = delegate!!.isClosed\n\n  @Throws(IOException::class)\n  override fun bind(localAddr: SocketAddress) {\n    delegate!!.bind(localAddr)\n  }\n\n  @Throws(IOException::class)\n  override fun connect(remoteAddr: SocketAddress) {\n    delegate!!.connect(remoteAddr)\n  }\n\n  @Throws(IOException::class)\n  override fun connect(\n    remoteAddr: SocketAddress,\n    timeout: Int,\n  ) {\n    delegate!!.connect(remoteAddr, timeout)\n  }\n\n  override fun isInputShutdown(): Boolean = delegate!!.isInputShutdown\n\n  override fun isOutputShutdown(): Boolean = delegate!!.isOutputShutdown\n\n  @Throws(SocketException::class)\n  override fun setReuseAddress(reuse: Boolean) {\n    delegate!!.reuseAddress = reuse\n  }\n\n  @Throws(SocketException::class)\n  override fun getReuseAddress(): Boolean = delegate!!.reuseAddress\n\n  @Throws(SocketException::class)\n  override fun setOOBInline(oobinline: Boolean) {\n    delegate!!.oobInline = oobinline\n  }\n\n  @Throws(SocketException::class)\n  override fun getOOBInline(): Boolean = delegate!!.oobInline\n\n  @Throws(SocketException::class)\n  override fun setTrafficClass(value: Int) {\n    delegate!!.trafficClass = value\n  }\n\n  @Throws(SocketException::class)\n  override fun getTrafficClass(): Int = delegate!!.trafficClass\n\n  @Throws(IOException::class)\n  override fun sendUrgentData(value: Int) {\n    delegate!!.sendUrgentData(value)\n  }\n\n  override fun getChannel(): SocketChannel = delegate!!.channel\n\n  override fun getHandshakeSession(): SSLSession = delegate!!.handshakeSession\n\n  override fun getApplicationProtocol(): String = delegate!!.applicationProtocol\n\n  override fun getHandshakeApplicationProtocol(): String = delegate!!.handshakeApplicationProtocol\n\n  override fun setHandshakeApplicationProtocolSelector(selector: BiFunction<SSLSocket, MutableList<String>, String>?) {\n    delegate!!.handshakeApplicationProtocolSelector = selector\n  }\n\n  override fun getHandshakeApplicationProtocolSelector(): BiFunction<SSLSocket, MutableList<String>, String> =\n    delegate!!.handshakeApplicationProtocolSelector\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/DelegatingSSLSocketFactory.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.Socket\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\n\n/**\n * A [SSLSocketFactory] that delegates calls. Sockets can be configured after creation by\n * overriding [.configureSocket].\n */\nopen class DelegatingSSLSocketFactory(\n  private val delegate: SSLSocketFactory,\n) : SSLSocketFactory() {\n  @Throws(IOException::class)\n  override fun createSocket(): SSLSocket {\n    val sslSocket = delegate.createSocket() as SSLSocket\n    return configureSocket(sslSocket)\n  }\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    host: String,\n    port: Int,\n  ): SSLSocket {\n    val sslSocket = delegate.createSocket(host, port) as SSLSocket\n    return configureSocket(sslSocket)\n  }\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    host: String,\n    port: Int,\n    localAddress: InetAddress,\n    localPort: Int,\n  ): SSLSocket {\n    val sslSocket = delegate.createSocket(host, port, localAddress, localPort) as SSLSocket\n    return configureSocket(sslSocket)\n  }\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    host: InetAddress,\n    port: Int,\n  ): SSLSocket {\n    val sslSocket = delegate.createSocket(host, port) as SSLSocket\n    return configureSocket(sslSocket)\n  }\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    host: InetAddress,\n    port: Int,\n    localAddress: InetAddress,\n    localPort: Int,\n  ): SSLSocket {\n    val sslSocket = delegate.createSocket(host, port, localAddress, localPort) as SSLSocket\n    return configureSocket(sslSocket)\n  }\n\n  override fun getDefaultCipherSuites(): Array<String> = delegate.defaultCipherSuites\n\n  override fun getSupportedCipherSuites(): Array<String> = delegate.supportedCipherSuites\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    socket: Socket,\n    host: String,\n    port: Int,\n    autoClose: Boolean,\n  ): SSLSocket {\n    val sslSocket = delegate.createSocket(socket, host, port, autoClose) as SSLSocket\n    return configureSocket(sslSocket)\n  }\n\n  @Throws(IOException::class)\n  protected open fun configureSocket(sslSocket: SSLSocket): SSLSocket {\n    // No-op by default.\n    return sslSocket\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/DelegatingServerSocketFactory.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.ServerSocket\nimport javax.net.ServerSocketFactory\n\n/**\n * A [ServerSocketFactory] that delegates calls. Sockets can be configured after creation by\n * overriding [.configureServerSocket].\n */\nopen class DelegatingServerSocketFactory(\n  private val delegate: ServerSocketFactory,\n) : ServerSocketFactory() {\n  @Throws(IOException::class)\n  override fun createServerSocket(): ServerSocket {\n    val serverSocket = delegate.createServerSocket()\n    return configureServerSocket(serverSocket)\n  }\n\n  @Throws(IOException::class)\n  override fun createServerSocket(port: Int): ServerSocket {\n    val serverSocket = delegate.createServerSocket(port)\n    return configureServerSocket(serverSocket)\n  }\n\n  @Throws(IOException::class)\n  override fun createServerSocket(\n    port: Int,\n    backlog: Int,\n  ): ServerSocket {\n    val serverSocket = delegate.createServerSocket(port, backlog)\n    return configureServerSocket(serverSocket)\n  }\n\n  @Throws(IOException::class)\n  override fun createServerSocket(\n    port: Int,\n    backlog: Int,\n    ifAddress: InetAddress,\n  ): ServerSocket {\n    val serverSocket = delegate.createServerSocket(port, backlog, ifAddress)\n    return configureServerSocket(serverSocket)\n  }\n\n  @Throws(IOException::class)\n  protected open fun configureServerSocket(serverSocket: ServerSocket): ServerSocket {\n    // No-op by default.\n    return serverSocket\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/DelegatingSocketFactory.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.Socket\nimport javax.net.SocketFactory\n\n/**\n * A [SocketFactory] that delegates calls. Sockets can be configured after creation by\n * overriding [.configureSocket].\n */\nopen class DelegatingSocketFactory(\n  private val delegate: SocketFactory,\n) : SocketFactory() {\n  @Throws(IOException::class)\n  override fun createSocket(): Socket {\n    val socket = delegate.createSocket()\n    return configureSocket(socket)\n  }\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    host: String,\n    port: Int,\n  ): Socket {\n    val socket = delegate.createSocket(host, port)\n    return configureSocket(socket)\n  }\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    host: String,\n    port: Int,\n    localAddress: InetAddress,\n    localPort: Int,\n  ): Socket {\n    val socket = delegate.createSocket(host, port, localAddress, localPort)\n    return configureSocket(socket)\n  }\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    host: InetAddress,\n    port: Int,\n  ): Socket {\n    val socket = delegate.createSocket(host, port)\n    return configureSocket(socket)\n  }\n\n  @Throws(IOException::class)\n  override fun createSocket(\n    host: InetAddress,\n    port: Int,\n    localAddress: InetAddress,\n    localPort: Int,\n  ): Socket {\n    val socket = delegate.createSocket(host, port, localAddress, localPort)\n    return configureSocket(socket)\n  }\n\n  @Throws(IOException::class)\n  protected open fun configureSocket(socket: Socket): Socket {\n    // No-op by default.\n    return socket\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/EventListenerAdapter.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport okhttp3.CallEvent.CacheConditionalHit\nimport okhttp3.CallEvent.CacheHit\nimport okhttp3.CallEvent.CacheMiss\nimport okhttp3.CallEvent.CallEnd\nimport okhttp3.CallEvent.CallFailed\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.Canceled\nimport okhttp3.CallEvent.ConnectEnd\nimport okhttp3.CallEvent.ConnectFailed\nimport okhttp3.CallEvent.ConnectStart\nimport okhttp3.CallEvent.ConnectionAcquired\nimport okhttp3.CallEvent.ConnectionReleased\nimport okhttp3.CallEvent.DispatcherQueueEnd\nimport okhttp3.CallEvent.DispatcherQueueStart\nimport okhttp3.CallEvent.DnsEnd\nimport okhttp3.CallEvent.DnsStart\nimport okhttp3.CallEvent.FollowUpDecision\nimport okhttp3.CallEvent.ProxySelectEnd\nimport okhttp3.CallEvent.ProxySelectStart\nimport okhttp3.CallEvent.RequestBodyEnd\nimport okhttp3.CallEvent.RequestBodyStart\nimport okhttp3.CallEvent.RequestFailed\nimport okhttp3.CallEvent.RequestHeadersEnd\nimport okhttp3.CallEvent.RequestHeadersStart\nimport okhttp3.CallEvent.ResponseBodyEnd\nimport okhttp3.CallEvent.ResponseBodyStart\nimport okhttp3.CallEvent.ResponseFailed\nimport okhttp3.CallEvent.ResponseHeadersEnd\nimport okhttp3.CallEvent.ResponseHeadersStart\nimport okhttp3.CallEvent.RetryDecision\nimport okhttp3.CallEvent.SatisfactionFailure\nimport okhttp3.CallEvent.SecureConnectEnd\nimport okhttp3.CallEvent.SecureConnectStart\n\n/**\n * This accepts events as function calls on [EventListener], and publishes them as subtypes of\n * [CallEvent].\n */\nclass EventListenerAdapter : EventListener() {\n  var listeners = listOf<(CallEvent) -> Unit>()\n\n  private fun onEvent(listener: CallEvent) {\n    for (function in listeners) {\n      function(listener)\n    }\n  }\n\n  override fun dispatcherQueueStart(\n    call: Call,\n    dispatcher: Dispatcher,\n  ) = onEvent(DispatcherQueueStart(System.nanoTime(), call, dispatcher))\n\n  override fun dispatcherQueueEnd(\n    call: Call,\n    dispatcher: Dispatcher,\n  ) = onEvent(DispatcherQueueEnd(System.nanoTime(), call, dispatcher))\n\n  override fun proxySelectStart(\n    call: Call,\n    url: HttpUrl,\n  ) = onEvent(ProxySelectStart(System.nanoTime(), call, url))\n\n  override fun proxySelectEnd(\n    call: Call,\n    url: HttpUrl,\n    proxies: List<Proxy>,\n  ) = onEvent(ProxySelectEnd(System.nanoTime(), call, url, proxies))\n\n  override fun dnsStart(\n    call: Call,\n    domainName: String,\n  ) = onEvent(DnsStart(System.nanoTime(), call, domainName))\n\n  override fun dnsEnd(\n    call: Call,\n    domainName: String,\n    inetAddressList: List<InetAddress>,\n  ) = onEvent(DnsEnd(System.nanoTime(), call, domainName, inetAddressList))\n\n  override fun connectStart(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n  ) = onEvent(ConnectStart(System.nanoTime(), call, inetSocketAddress, proxy))\n\n  override fun secureConnectStart(call: Call) =\n    onEvent(\n      SecureConnectStart(\n        System.nanoTime(),\n        call,\n      ),\n    )\n\n  override fun secureConnectEnd(\n    call: Call,\n    handshake: Handshake?,\n  ) = onEvent(SecureConnectEnd(System.nanoTime(), call, handshake))\n\n  override fun connectEnd(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n    protocol: Protocol?,\n  ) = onEvent(ConnectEnd(System.nanoTime(), call, inetSocketAddress, proxy, protocol))\n\n  override fun connectFailed(\n    call: Call,\n    inetSocketAddress: InetSocketAddress,\n    proxy: Proxy,\n    protocol: Protocol?,\n    ioe: IOException,\n  ) = onEvent(\n    ConnectFailed(\n      System.nanoTime(),\n      call,\n      inetSocketAddress,\n      proxy,\n      protocol,\n      ioe,\n    ),\n  )\n\n  override fun connectionAcquired(\n    call: Call,\n    connection: Connection,\n  ) = onEvent(ConnectionAcquired(System.nanoTime(), call, connection))\n\n  override fun connectionReleased(\n    call: Call,\n    connection: Connection,\n  ) = onEvent(ConnectionReleased(System.nanoTime(), call, connection))\n\n  override fun callStart(call: Call) = onEvent(CallStart(System.nanoTime(), call))\n\n  override fun requestHeadersStart(call: Call) =\n    onEvent(\n      RequestHeadersStart(\n        System.nanoTime(),\n        call,\n      ),\n    )\n\n  override fun requestHeadersEnd(\n    call: Call,\n    request: Request,\n  ) = onEvent(RequestHeadersEnd(System.nanoTime(), call, request.headers.byteCount()))\n\n  override fun requestBodyStart(call: Call) =\n    onEvent(\n      RequestBodyStart(\n        System.nanoTime(),\n        call,\n      ),\n    )\n\n  override fun requestBodyEnd(\n    call: Call,\n    byteCount: Long,\n  ) = onEvent(RequestBodyEnd(System.nanoTime(), call, byteCount))\n\n  override fun requestFailed(\n    call: Call,\n    ioe: IOException,\n  ) = onEvent(RequestFailed(System.nanoTime(), call, ioe))\n\n  override fun responseHeadersStart(call: Call) =\n    onEvent(\n      ResponseHeadersStart(\n        System.nanoTime(),\n        call,\n      ),\n    )\n\n  override fun responseHeadersEnd(\n    call: Call,\n    response: Response,\n  ) = onEvent(ResponseHeadersEnd(System.nanoTime(), call, response.headers.byteCount()))\n\n  override fun responseBodyStart(call: Call) =\n    onEvent(\n      ResponseBodyStart(\n        System.nanoTime(),\n        call,\n      ),\n    )\n\n  override fun responseBodyEnd(\n    call: Call,\n    byteCount: Long,\n  ) = onEvent(ResponseBodyEnd(System.nanoTime(), call, byteCount))\n\n  override fun responseFailed(\n    call: Call,\n    ioe: IOException,\n  ) = onEvent(ResponseFailed(System.nanoTime(), call, ioe))\n\n  override fun callEnd(call: Call) = onEvent(CallEnd(System.nanoTime(), call))\n\n  override fun callFailed(\n    call: Call,\n    ioe: IOException,\n  ) = onEvent(CallFailed(System.nanoTime(), call, ioe))\n\n  override fun canceled(call: Call) = onEvent(Canceled(System.nanoTime(), call))\n\n  override fun satisfactionFailure(\n    call: Call,\n    response: Response,\n  ) = onEvent(SatisfactionFailure(System.nanoTime(), call))\n\n  override fun cacheMiss(call: Call) = onEvent(CacheMiss(System.nanoTime(), call))\n\n  override fun cacheHit(\n    call: Call,\n    response: Response,\n  ) = onEvent(CacheHit(System.nanoTime(), call))\n\n  override fun cacheConditionalHit(\n    call: Call,\n    cachedResponse: Response,\n  ) = onEvent(CacheConditionalHit(System.nanoTime(), call))\n\n  override fun retryDecision(\n    call: Call,\n    exception: IOException,\n    retry: Boolean,\n  ) = onEvent(RetryDecision(System.nanoTime(), call, exception, retry))\n\n  override fun followUpDecision(\n    call: Call,\n    networkResponse: Response,\n    nextRequest: Request?,\n  ) = onEvent(FollowUpDecision(System.nanoTime(), call, networkResponse, nextRequest))\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/EventListenerRelay.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\n/**\n * A special [EventListener] for testing the mechanics of event listeners.\n *\n * Each instance processes a single event on [call], and then adds a successor [EventListenerRelay]\n * on the same [call] to process the next event.\n *\n * By forcing the list of listeners to change after every event, we can detect if buggy code caches\n * a stale [EventListener] in a field or local variable.\n */\nclass EventListenerRelay(\n  val call: Call,\n  val eventRecorder: EventRecorder,\n) {\n  private val eventListenerAdapter =\n    EventListenerAdapter()\n      .apply {\n        listeners += ::onEvent\n      }\n\n  val eventListener: EventListener\n    get() = eventListenerAdapter\n\n  var eventCount = 0\n\n  private fun onEvent(callEvent: CallEvent) {\n    if (eventCount++ == 0) {\n      eventRecorder.logEvent(callEvent)\n      val next = EventListenerRelay(call, eventRecorder)\n      call.addEventListener(next.eventListener)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/EventRecorder.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.matchesPredicate\nimport java.util.Deque\nimport java.util.concurrent.ConcurrentLinkedDeque\nimport java.util.concurrent.TimeUnit\nimport okhttp3.CallEvent.CallStart\nimport okhttp3.CallEvent.Canceled\nimport org.junit.jupiter.api.Assertions.fail\n\nopen class EventRecorder(\n  /**\n   * An override to ignore the normal order that is enforced.\n   * EventListeners added by Interceptors will not see all events.\n   */\n  private val enforceOrder: Boolean = true,\n) {\n  private val eventListenerAdapter =\n    EventListenerAdapter()\n      .apply {\n        listeners += ::logEvent\n      }\n\n  val eventListener: EventListener\n    get() = eventListenerAdapter\n\n  /** Events that haven't yet been removed. */\n  val eventSequence: Deque<CallEvent> = ConcurrentLinkedDeque()\n\n  /** The full set of events, used to match starts with ends. */\n  private val eventsForMatching = ConcurrentLinkedDeque<CallEvent>()\n\n  private val forbiddenLocks = mutableListOf<Any>()\n\n  /** The timestamp of the last taken event, used to measure elapsed time between events. */\n  private var lastTimestampNs: Long? = null\n\n  /** Confirm that the thread does not hold a lock on `lock` during the callback. */\n  fun forbidLock(lock: Any) {\n    forbiddenLocks.add(lock)\n  }\n\n  /**\n   * Removes recorded events up to (and including) an event is found whose class equals [eventClass]\n   * and returns it.\n   */\n  fun <T : CallEvent> removeUpToEvent(eventClass: Class<T>): T {\n    val fullEventSequence = eventSequence.toList()\n    try {\n      while (true) {\n        val event = takeEvent()\n        if (eventClass.isInstance(event)) {\n          return eventClass.cast(event)\n        }\n      }\n    } catch (e: NoSuchElementException) {\n      throw AssertionError(\"full event sequence: $fullEventSequence\", e)\n    }\n  }\n\n  inline fun <reified T : CallEvent> removeUpToEvent(): T = removeUpToEvent(T::class.java)\n\n  inline fun <reified T : CallEvent> findEvent(): T = eventSequence.first { it is T } as T\n\n  /**\n   * Remove and return the next event from the recorded sequence.\n   *\n   * @param eventClass a class to assert that the returned event is an instance of, or null to\n   *     take any event class.\n   * @param elapsedMs the time in milliseconds elapsed since the immediately-preceding event, or\n   *     -1L to take any duration.\n   */\n  fun takeEvent(\n    eventClass: Class<out CallEvent>? = null,\n    elapsedMs: Long = -1L,\n  ): CallEvent {\n    val result = eventSequence.remove()\n    val actualElapsedNs = result.timestampNs - (lastTimestampNs ?: result.timestampNs)\n    lastTimestampNs = result.timestampNs\n\n    if (eventClass != null) {\n      assertThat(result).isInstanceOf(eventClass)\n    }\n\n    if (elapsedMs != -1L) {\n      assertThat(\n        TimeUnit.NANOSECONDS\n          .toMillis(actualElapsedNs)\n          .toDouble(),\n      ).isCloseTo(elapsedMs.toDouble(), 100.0)\n    }\n\n    return result\n  }\n\n  fun recordedEventTypes() = eventSequence.map { it::class }\n\n  fun clearAllEvents() {\n    while (eventSequence.isNotEmpty()) {\n      takeEvent()\n    }\n  }\n\n  internal fun logEvent(e: CallEvent) {\n    for (lock in forbiddenLocks) {\n      assertThat(Thread.holdsLock(lock), lock.toString()).isFalse()\n    }\n\n    if (enforceOrder) {\n      checkForStartEvent(e)\n    }\n\n    eventsForMatching.offer(e)\n    eventSequence.offer(e)\n  }\n\n  private fun checkForStartEvent(e: CallEvent) {\n    if (eventsForMatching.isEmpty()) {\n      assertThat(e).matchesPredicate { it is CallStart || it is Canceled }\n    } else {\n      eventsForMatching.forEach loop@{\n        when (e.closes(it)) {\n          null -> return\n\n          // no open event\n          true -> return\n\n          // found open event\n          false -> return@loop // this is not the open event so continue\n        }\n      }\n      fail<Any>(\"event $e without matching start event\")\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/FailingCall.kt",
    "content": "/*\n * Copyright (C) 2024 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport kotlin.reflect.KClass\nimport okio.Timeout\n\nopen class FailingCall : Call {\n  override fun request(): Request = error(\"unexpected\")\n\n  override fun execute(): Response = error(\"unexpected\")\n\n  override fun enqueue(responseCallback: Callback): Unit = error(\"unexpected\")\n\n  override fun cancel(): Unit = error(\"unexpected\")\n\n  override fun isExecuted(): Boolean = error(\"unexpected\")\n\n  override fun isCanceled(): Boolean = error(\"unexpected\")\n\n  override fun timeout(): Timeout = error(\"unexpected\")\n\n  override fun addEventListener(eventListener: EventListener) = error(\"unexpected\")\n\n  override fun <T : Any> tag(type: KClass<T>): T? = error(\"unexpected\")\n\n  override fun <T> tag(type: Class<out T>): T? = error(\"unexpected\")\n\n  override fun <T : Any> tag(\n    type: KClass<T>,\n    computeIfAbsent: () -> T,\n  ): T = error(\"unexpected\")\n\n  override fun <T : Any> tag(\n    type: Class<T>,\n    computeIfAbsent: () -> T,\n  ): T = error(\"unexpected\")\n\n  override fun clone(): Call = error(\"unexpected\")\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/FakeDns.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport java.util.Collections\nimport okio.Buffer\n\nclass FakeDns : Dns {\n  private val hostAddresses: MutableMap<String, List<InetAddress>> =\n    Collections.synchronizedMap(mutableMapOf())\n  private val requestedHosts: MutableList<String> = Collections.synchronizedList(mutableListOf())\n  private var nextAddress = 0xff000064L // 255.0.0.100 in IPv4; ::ff00:64 in IPv6.\n\n  /** Sets the results for `hostname`.  */\n  operator fun set(\n    hostname: String,\n    addresses: List<InetAddress>,\n  ): FakeDns {\n    hostAddresses[hostname] = addresses\n    return this\n  }\n\n  /** Clears the results for `hostname`.  */\n  fun clear(hostname: String): FakeDns {\n    hostAddresses.remove(hostname)\n    return this\n  }\n\n  @Throws(UnknownHostException::class)\n  fun lookup(\n    hostname: String,\n    index: Int,\n  ): InetAddress = hostAddresses[hostname]!![index]\n\n  @Throws(UnknownHostException::class)\n  override fun lookup(hostname: String): List<InetAddress> {\n    requestedHosts.add(hostname)\n    return hostAddresses[hostname] ?: throw UnknownHostException()\n  }\n\n  fun assertRequests(vararg expectedHosts: String?) {\n    assertThat(requestedHosts).containsExactly(*expectedHosts)\n    requestedHosts.clear()\n  }\n\n  /** Allocates and returns `count` fake IPv4 addresses like [255.0.0.100, 255.0.0.101].  */\n  fun allocate(count: Int): List<InetAddress> {\n    val from = nextAddress\n    nextAddress += count\n    return (from until nextAddress)\n      .map {\n        return@map InetAddress.getByAddress(\n          Buffer().writeInt(it.toInt()).readByteArray(),\n        )\n      }\n  }\n\n  /** Allocates and returns `count` fake IPv6 addresses like [::ff00:64, ::ff00:65].  */\n  fun allocateIpv6(count: Int): List<InetAddress> {\n    val from = nextAddress\n    nextAddress += count\n    return (from until nextAddress)\n      .map {\n        return@map InetAddress.getByAddress(\n          Buffer().writeLong(0L).writeLong(it).readByteArray(),\n        )\n      }\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/FakeProxySelector.kt",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.SocketAddress\nimport java.net.URI\n\nclass FakeProxySelector : ProxySelector() {\n  val proxies: MutableList<Proxy> = mutableListOf()\n\n  fun addProxy(proxy: Proxy): FakeProxySelector {\n    proxies.add(proxy)\n    return this\n  }\n\n  override fun select(uri: URI): List<Proxy> {\n    // Don't handle 'socket' schemes, which the RI's Socket class may request (for SOCKS).\n    return if (uri.scheme == \"http\" || uri.scheme == \"https\") proxies else listOf(Proxy.NO_PROXY)\n  }\n\n  override fun connectFailed(\n    uri: URI,\n    sa: SocketAddress,\n    ioe: IOException,\n  ) {\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/FakeSSLSession.kt",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with this\n * work for additional information regarding copyright ownership. The ASF\n * licenses this file to You under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n@file:Suppress(\"DEPRECATION\")\n\npackage okhttp3\n\nimport java.security.Principal\nimport java.security.cert.Certificate\nimport javax.net.ssl.SSLPeerUnverifiedException\nimport javax.net.ssl.SSLSession\nimport javax.net.ssl.SSLSessionContext\nimport javax.security.cert.X509Certificate\n\nclass FakeSSLSession(\n  vararg val certificates: Certificate,\n) : SSLSession {\n  override fun getApplicationBufferSize(): Int = throw UnsupportedOperationException()\n\n  override fun getCipherSuite(): String = throw UnsupportedOperationException()\n\n  override fun getCreationTime(): Long = throw UnsupportedOperationException()\n\n  override fun getId(): ByteArray = throw UnsupportedOperationException()\n\n  override fun getLastAccessedTime(): Long = throw UnsupportedOperationException()\n\n  override fun getLocalCertificates(): Array<Certificate> = throw UnsupportedOperationException()\n\n  override fun getLocalPrincipal(): Principal = throw UnsupportedOperationException()\n\n  override fun getPacketBufferSize(): Int = throw UnsupportedOperationException()\n\n  @Suppress(\"UNCHECKED_CAST\")\n  @Throws(SSLPeerUnverifiedException::class)\n  override fun getPeerCertificates(): Array<Certificate> =\n    if (certificates.isEmpty()) {\n      throw SSLPeerUnverifiedException(\"peer not authenticated\")\n    } else {\n      certificates as Array<Certificate>\n    }\n\n  @Suppress(\"removal\", \"OVERRIDE_DEPRECATION\")\n  @Throws(\n    SSLPeerUnverifiedException::class,\n  )\n  override fun getPeerCertificateChain(): Array<X509Certificate> = throw UnsupportedOperationException()\n\n  override fun getPeerHost(): String = throw UnsupportedOperationException()\n\n  override fun getPeerPort(): Int = throw UnsupportedOperationException()\n\n  @Throws(SSLPeerUnverifiedException::class)\n  override fun getPeerPrincipal(): Principal = throw UnsupportedOperationException()\n\n  override fun getProtocol(): String = throw UnsupportedOperationException()\n\n  override fun getSessionContext(): SSLSessionContext = throw UnsupportedOperationException()\n\n  override fun putValue(\n    s: String,\n    obj: Any,\n  ): Unit = throw UnsupportedOperationException()\n\n  override fun removeValue(s: String): Unit = throw UnsupportedOperationException()\n\n  override fun getValue(s: String): Any = throw UnsupportedOperationException()\n\n  override fun getValueNames(): Array<String> = throw UnsupportedOperationException()\n\n  override fun invalidate(): Unit = throw UnsupportedOperationException()\n\n  override fun isValid(): Boolean = throw UnsupportedOperationException()\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/ForwardingRequestBody.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport okio.BufferedSink\n\nopen class ForwardingRequestBody(\n  delegate: RequestBody?,\n) : RequestBody() {\n  private val delegate: RequestBody\n\n  fun delegate(): RequestBody = delegate\n\n  override fun contentType(): MediaType? = delegate.contentType()\n\n  @Throws(IOException::class)\n  override fun contentLength(): Long = delegate.contentLength()\n\n  @Throws(IOException::class)\n  override fun writeTo(sink: BufferedSink) {\n    delegate.writeTo(sink)\n  }\n\n  override fun isDuplex(): Boolean = delegate.isDuplex()\n\n  override fun toString(): String = javaClass.simpleName + \"(\" + delegate.toString() + \")\"\n\n  init {\n    requireNotNull(delegate) { \"delegate == null\" }\n    this.delegate = delegate\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/ForwardingResponseBody.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okio.BufferedSource\n\nopen class ForwardingResponseBody(\n  delegate: ResponseBody?,\n) : ResponseBody() {\n  private val delegate: ResponseBody\n\n  fun delegate(): ResponseBody = delegate\n\n  override fun contentType(): MediaType? = delegate.contentType()\n\n  override fun contentLength(): Long = delegate.contentLength()\n\n  override fun source(): BufferedSource = delegate.source()\n\n  override fun toString(): String = javaClass.simpleName + \"(\" + delegate.toString() + \")\"\n\n  init {\n    requireNotNull(delegate) { \"delegate == null\" }\n    this.delegate = delegate\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/JsseDebugLogging.kt",
    "content": "/*\n * Copyright (C) 2021 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.Closeable\nimport java.util.logging.Handler\nimport java.util.logging.LogRecord\n\nobject JsseDebugLogging {\n  data class JsseDebugMessage(\n    val message: String,\n    val param: String?,\n  ) {\n    enum class Type {\n      Handshake,\n      Plaintext,\n      Encrypted,\n      Setup,\n      Unknown,\n    }\n\n    val type: Type\n      get() =\n        when {\n          message == \"adding as trusted certificates\" -> Type.Setup\n          message == \"Raw read\" || message == \"Raw write\" -> Type.Encrypted\n          message == \"Plaintext before ENCRYPTION\" || message == \"Plaintext after DECRYPTION\" -> Type.Plaintext\n          message.startsWith(\"System property \") -> Type.Setup\n          message.startsWith(\"Reload \") -> Type.Setup\n          message == \"No session to resume.\" -> Type.Handshake\n          message.startsWith(\"Consuming \") -> Type.Handshake\n          message.startsWith(\"Produced \") -> Type.Handshake\n          message.startsWith(\"Negotiated \") -> Type.Handshake\n          message.startsWith(\"Found resumable session\") -> Type.Handshake\n          message.startsWith(\"Resuming session\") -> Type.Handshake\n          message.startsWith(\"Using PSK to derive early secret\") -> Type.Handshake\n          else -> Type.Unknown\n        }\n\n    override fun toString(): String =\n      if (param != null) {\n        message + \"\\n\" + param\n      } else {\n        message\n      }\n  }\n\n  private fun quietDebug(message: JsseDebugMessage) {\n    if (message.message.startsWith(\"Ignore\")) {\n      return\n    }\n\n    when (message.type) {\n      JsseDebugMessage.Type.Setup, JsseDebugMessage.Type.Encrypted, JsseDebugMessage.Type.Plaintext -> {\n        println(message.message + \" (skipped output)\")\n      }\n\n      else -> {\n        println(message)\n      }\n    }\n  }\n\n  fun enableJsseDebugLogging(debugHandler: (JsseDebugMessage) -> Unit = this::quietDebug): Closeable {\n    System.setProperty(\"javax.net.debug\", \"\")\n    return OkHttpDebugLogging.enable(\n      \"javax.net.ssl\",\n      object : Handler() {\n        override fun publish(record: LogRecord) {\n          val param = record.parameters?.firstOrNull() as? String\n          debugHandler(JsseDebugMessage(record.message, param))\n        }\n\n        override fun flush() {\n        }\n\n        override fun close() {\n        }\n      },\n    )\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/OkHttpClientTestRule.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\n  \"CANNOT_OVERRIDE_INVISIBLE_MEMBER\",\n  \"INVISIBLE_MEMBER\",\n  \"INVISIBLE_REFERENCE\",\n)\n\npackage okhttp3\n\nimport android.annotation.SuppressLint\nimport java.util.concurrent.ThreadFactory\nimport java.util.concurrent.TimeUnit\nimport java.util.logging.Handler\nimport java.util.logging.Level\nimport java.util.logging.LogManager\nimport java.util.logging.LogRecord\nimport java.util.logging.Logger\nimport okhttp3.internal.buildConnectionPool\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.connection.RealConnectionPool\nimport okhttp3.internal.http2.Http2\nimport okhttp3.internal.taskRunnerInternal\nimport okhttp3.testing.Flaky\nimport okhttp3.testing.PlatformRule.Companion.LOOM_PROPERTY\nimport okhttp3.testing.PlatformRule.Companion.getPlatformSystemProperty\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.extension.AfterEachCallback\nimport org.junit.jupiter.api.extension.BeforeEachCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\n\n/**\n * Apply this rule to all tests. It adds additional checks for leaked resources and uncaught\n * exceptions.\n *\n * Use [newClient] as a factory for a OkHttpClient instances. These instances are specifically\n * configured for testing.\n */\nclass OkHttpClientTestRule :\n  BeforeEachCallback,\n  AfterEachCallback {\n  private val clientEventsList = mutableListOf<String>()\n  private var testClient: OkHttpClient? = null\n  private var uncaughtException: Throwable? = null\n  private lateinit var testName: String\n  private var defaultUncaughtExceptionHandler: Thread.UncaughtExceptionHandler? = null\n  private var taskQueuesWereIdle: Boolean = false\n  private val connectionListener = RecordingConnectionListener()\n\n  var logger: Logger? = null\n\n  var recordEvents = true\n  var recordTaskRunner = false\n  var recordFrames = false\n  var recordSslDebug = false\n\n  private val sslExcludeFilter =\n    Regex(\n      buildString {\n        append(\"^(?:\")\n        append(\n          listOf(\n            \"Inaccessible trust store\",\n            \"trustStore is\",\n            \"Reload the trust store\",\n            \"Reload trust certs\",\n            \"Reloaded\",\n            \"adding as trusted certificates\",\n            \"Ignore disabled cipher suite\",\n            \"Ignore unsupported cipher suite\",\n          ).joinToString(separator = \"|\"),\n        )\n        append(\").*\")\n      },\n    )\n\n  private val testLogHandler =\n    object : Handler() {\n      override fun publish(record: LogRecord) {\n        val recorded =\n          when (record.loggerName) {\n            TaskRunner::class.java.name -> recordTaskRunner\n            Http2::class.java.name -> recordFrames\n            \"javax.net.ssl\" -> recordSslDebug && !sslExcludeFilter.matches(record.message)\n            else -> false\n          }\n\n        if (recorded) {\n          synchronized(clientEventsList) {\n            clientEventsList.add(record.message)\n\n            if (record.loggerName == \"javax.net.ssl\") {\n              val parameters = record.parameters\n\n              if (parameters != null) {\n                clientEventsList.add(parameters.first().toString())\n              }\n            }\n          }\n        }\n      }\n\n      override fun flush() {\n      }\n\n      override fun close() {\n      }\n    }.apply {\n      level = Level.FINEST\n    }\n\n  private fun applyLogger(fn: Logger.() -> Unit) {\n    Logger.getLogger(OkHttpClient::class.java.`package`.name).fn()\n    Logger.getLogger(OkHttpClient::class.java.name).fn()\n    Logger.getLogger(Http2::class.java.name).fn()\n    Logger.getLogger(TaskRunner::class.java.name).fn()\n    Logger.getLogger(\"javax.net.ssl\").fn()\n  }\n\n  fun wrap(eventListener: EventListener) =\n    EventListener.Factory {\n      ClientRuleEventListener(::addEvent) + eventListener\n    }\n\n  fun wrap(eventRecorder: EventRecorder) = wrap(eventRecorder.eventListener)\n\n  fun wrap(eventListenerFactory: EventListener.Factory) =\n    EventListener.Factory { call ->\n      ClientRuleEventListener(::addEvent) + eventListenerFactory.create(call)\n    }\n\n  /**\n   * Returns an OkHttpClient for tests to use as a starting point.\n   *\n   * The returned client installs a default event listener that gathers debug information. This will\n   * be logged if the test fails.\n   *\n   * This client is also configured to be slightly more deterministic, returning a single IP\n   * address for all hosts, regardless of the actual number of IP addresses reported by DNS.\n   */\n  fun newClient(): OkHttpClient {\n    var client = testClient\n    if (client == null) {\n      client =\n        initialClientBuilder()\n          .dns(SINGLE_INET_ADDRESS_DNS) // Prevent unexpected fallback addresses.\n          .eventListenerFactory { ClientRuleEventListener(logger = ::addEvent) }\n          .build()\n      connectionListener.forbidLock(RealConnectionPool.get(client.connectionPool))\n      connectionListener.forbidLock(client.dispatcher)\n      testClient = client\n    }\n    return client\n  }\n\n  private fun initialClientBuilder(): OkHttpClient.Builder =\n    if (isLoom()) {\n      val backend = TaskRunner.RealBackend(loomThreadFactory())\n      val taskRunner = TaskRunner(backend)\n\n      OkHttpClient\n        .Builder()\n        .connectionPool(\n          buildConnectionPool(\n            connectionListener = connectionListener,\n            taskRunner = taskRunner,\n          ),\n        ).dispatcher(Dispatcher(backend.executor))\n        .taskRunnerInternal(taskRunner)\n    } else {\n      OkHttpClient\n        .Builder()\n        .connectionPool(ConnectionPool(connectionListener = connectionListener))\n    }\n\n  private fun loomThreadFactory(): ThreadFactory {\n    val ofVirtual = Thread::class.java.getMethod(\"ofVirtual\").invoke(null)\n\n    return Class\n      .forName(\"java.lang.Thread\\$Builder\")\n      .getMethod(\"factory\")\n      .invoke(ofVirtual) as ThreadFactory\n  }\n\n  private fun isLoom(): Boolean = getPlatformSystemProperty() == LOOM_PROPERTY\n\n  fun newClientBuilder(): OkHttpClient.Builder = newClient().newBuilder()\n\n  @Synchronized private fun addEvent(event: String) {\n    if (recordEvents) {\n      logger?.info(event)\n\n      synchronized(clientEventsList) {\n        clientEventsList.add(event)\n      }\n    }\n  }\n\n  @Synchronized private fun initUncaughtException(throwable: Throwable) {\n    if (uncaughtException == null) {\n      uncaughtException = throwable\n    }\n  }\n\n  @Synchronized fun takeUncaughtException(): Throwable? =\n    uncaughtException\n      .also { uncaughtException = null }\n\n  fun ensureAllConnectionsReleased() {\n    testClient?.let {\n      val connectionPool = it.connectionPool\n\n      connectionPool.evictAll()\n      if (connectionPool.connectionCount() > 0) {\n        // Minimise test flakiness due to possible race conditions with connections closing.\n        // Some number of tests will report here, but not fail due to this delay.\n        println(\"Delaying to avoid flakes\")\n        Thread.sleep(500L)\n        println(\"After delay: \" + connectionPool.connectionCount())\n      }\n\n      connectionPool.evictAll()\n      assertEquals(0, connectionPool.connectionCount()) {\n        \"Still ${connectionPool.connectionCount()} connections open\"\n      }\n    }\n  }\n\n  private fun ensureAllTaskQueuesIdle() {\n    val entryTime = System.nanoTime()\n\n    for (queue in TaskRunner.INSTANCE.activeQueues()) {\n      // We wait at most 1 second, so we don't ever turn multiple lost threads into\n      // a test timeout failure.\n      val waitTime = (entryTime + 1_000_000_000L - System.nanoTime())\n      if (!queue.idleLatch().await(waitTime, TimeUnit.NANOSECONDS)) {\n        TaskRunner.INSTANCE.withLock {\n          TaskRunner.INSTANCE.cancelAll()\n        }\n        fail<Unit>(\"Queue still active after 1000 ms\")\n      }\n    }\n  }\n\n  override fun beforeEach(context: ExtensionContext) {\n    testName = context.displayName\n\n    beforeEach()\n  }\n\n  private fun beforeEach() {\n    defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()\n    Thread.setDefaultUncaughtExceptionHandler { _, throwable ->\n      initUncaughtException(throwable)\n    }\n\n    taskQueuesWereIdle = TaskRunner.INSTANCE.activeQueues().isEmpty()\n\n    applyLogger {\n      addHandler(testLogHandler)\n      level = Level.FINEST\n      useParentHandlers = false\n    }\n  }\n\n  @SuppressLint(\"NewApi\")\n  override fun afterEach(context: ExtensionContext) {\n    val failure = context.executionException.orElseGet { null }\n\n    if (uncaughtException != null) {\n      throw failure + AssertionError(\"uncaught exception thrown during test\", uncaughtException)\n    }\n\n    if (context.isFlaky()) {\n      logEvents()\n    }\n\n    LogManager.getLogManager().reset()\n\n    var result: Throwable? = failure\n    Thread.setDefaultUncaughtExceptionHandler(defaultUncaughtExceptionHandler)\n    try {\n      ensureAllConnectionsReleased()\n      releaseClient()\n    } catch (ae: AssertionError) {\n      result += ae\n    }\n\n    try {\n      if (taskQueuesWereIdle) {\n        ensureAllTaskQueuesIdle()\n      }\n    } catch (ae: AssertionError) {\n      result += ae\n    }\n\n    if (result != null) throw result\n  }\n\n  private fun releaseClient() {\n    testClient?.dispatcher?.executorService?.shutdown()\n  }\n\n  @SuppressLint(\"NewApi\")\n  private fun ExtensionContext.isFlaky(): Boolean =\n    (testMethod.orElseGet { null }?.isAnnotationPresent(Flaky::class.java) == true) ||\n      (testClass.orElseGet { null }?.isAnnotationPresent(Flaky::class.java) == true)\n\n  @Synchronized private fun logEvents() {\n    // Will be ineffective if test overrides the listener\n    synchronized(clientEventsList) {\n      println(\"$testName Events (${clientEventsList.size})\")\n\n      for (e in clientEventsList) {\n        println(e)\n      }\n    }\n  }\n\n  fun recordedConnectionEventTypes(): List<String> = connectionListener.recordedEventTypes()\n\n  companion object {\n    /**\n     * A network that resolves only one IP address per host. Use this when testing route selection\n     * fallbacks to prevent the host machine's various IP addresses from interfering.\n     */\n    private val SINGLE_INET_ADDRESS_DNS =\n      Dns { hostname ->\n        val addresses = Dns.SYSTEM.lookup(hostname)\n        listOf(addresses[0])\n      }\n\n    private operator fun Throwable?.plus(throwable: Throwable): Throwable {\n      if (this != null) {\n        addSuppressed(throwable)\n        return this\n      }\n      return throwable\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/OkHttpDebugLogging.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.Closeable\nimport java.util.concurrent.CopyOnWriteArraySet\nimport java.util.logging.ConsoleHandler\nimport java.util.logging.Handler\nimport java.util.logging.Level\nimport java.util.logging.LogRecord\nimport java.util.logging.Logger\nimport java.util.logging.SimpleFormatter\nimport kotlin.reflect.KClass\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.http2.Http2\n\nobject OkHttpDebugLogging {\n  // Keep references to loggers to prevent their configuration from being GC'd.\n  private val configuredLoggers = CopyOnWriteArraySet<Logger>()\n\n  fun enableHttp2() = enable(Http2::class)\n\n  fun enableTaskRunner() = enable(TaskRunner::class)\n\n  fun logHandler() =\n    ConsoleHandler().apply {\n      level = Level.FINE\n      formatter =\n        object : SimpleFormatter() {\n          override fun format(record: LogRecord) = String.format(\"[%1\\$tF %1\\$tT] %2\\$s %n\", record.millis, record.message)\n        }\n    }\n\n  fun enable(\n    loggerClass: String,\n    handler: Handler = logHandler(),\n  ): Closeable {\n    val logger = Logger.getLogger(loggerClass)\n    if (configuredLoggers.add(logger)) {\n      logger.addHandler(handler)\n      logger.level = Level.FINEST\n    }\n    return Closeable {\n      logger.removeHandler(handler)\n    }\n  }\n\n  fun enable(loggerClass: KClass<*>) = enable(loggerClass.java.name)\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/RecordingConnectionListener.kt",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\n  \"CANNOT_OVERRIDE_INVISIBLE_MEMBER\",\n  \"INVISIBLE_MEMBER\",\n  \"INVISIBLE_REFERENCE\",\n)\n\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isInstanceOf\nimport assertk.assertions.matchesPredicate\nimport java.util.Deque\nimport java.util.concurrent.ConcurrentLinkedDeque\nimport java.util.concurrent.TimeUnit\nimport okhttp3.ConnectionEvent.NoNewExchanges\nimport okhttp3.internal.connection.ConnectionListener\nimport okhttp3.internal.connection.RealConnection\nimport okio.IOException\nimport org.junit.jupiter.api.Assertions\n\ninternal open class RecordingConnectionListener(\n  /**\n   * An override to ignore the normal order that is enforced.\n   * EventListeners added by Interceptors will not see all events.\n   */\n  private val enforceOrder: Boolean = true,\n) : ConnectionListener() {\n  val eventSequence: Deque<ConnectionEvent> = ConcurrentLinkedDeque()\n\n  private val forbiddenLocks = mutableSetOf<Any>()\n\n  /** The timestamp of the last taken event, used to measure elapsed time between events. */\n  private var lastTimestampNs: Long? = null\n\n  /** Confirm that the thread does not hold a lock on `lock` during the callback. */\n  fun forbidLock(lock: Any) {\n    forbiddenLocks.add(lock)\n  }\n\n  /**\n   * Removes recorded events up to (and including) an event is found whose class equals [eventClass]\n   * and returns it.\n   */\n  fun <T : ConnectionEvent> removeUpToEvent(eventClass: Class<T>): T {\n    val fullEventSequence = eventSequence.toList()\n    try {\n      while (true) {\n        val event = takeEvent()\n        if (eventClass.isInstance(event)) {\n          return eventClass.cast(event)\n        }\n      }\n    } catch (e: NoSuchElementException) {\n      throw AssertionError(\"full event sequence: $fullEventSequence\", e)\n    }\n  }\n\n  /**\n   * Remove and return the next event from the recorded sequence.\n   *\n   * @param eventClass a class to assert that the returned event is an instance of, or null to\n   *     take any event class.\n   * @param elapsedMs the time in milliseconds elapsed since the immediately-preceding event, or\n   *     -1L to take any duration.\n   */\n  fun takeEvent(\n    eventClass: Class<out ConnectionEvent>? = null,\n    elapsedMs: Long = -1L,\n  ): ConnectionEvent {\n    val result = eventSequence.remove()\n    val actualElapsedNs = result.timestampNs - (lastTimestampNs ?: result.timestampNs)\n    lastTimestampNs = result.timestampNs\n\n    if (eventClass != null) {\n      assertThat(result).isInstanceOf(eventClass)\n    }\n\n    if (elapsedMs != -1L) {\n      assertThat(\n        TimeUnit.NANOSECONDS\n          .toMillis(actualElapsedNs)\n          .toDouble(),\n      ).isCloseTo(elapsedMs.toDouble(), 100.0)\n    }\n\n    return result\n  }\n\n  fun recordedEventTypes() = eventSequence.map { it.name }\n\n  fun clearAllEvents() {\n    while (eventSequence.isNotEmpty()) {\n      takeEvent()\n    }\n  }\n\n  private fun logEvent(e: ConnectionEvent) {\n    if (e.connection != null) {\n      assertThat(Thread.holdsLock(e.connection), \"Called with lock $${e.connection}\")\n        .isFalse()\n    }\n    for (lock in forbiddenLocks) {\n      assertThat(Thread.holdsLock(lock), \"Called with lock $lock\")\n        .isFalse()\n    }\n\n    if (enforceOrder) {\n      checkForStartEvent(e)\n    }\n\n    eventSequence.offer(e)\n  }\n\n  private fun checkForStartEvent(e: ConnectionEvent) {\n    if (eventSequence.isEmpty()) {\n      assertThat(e).isInstanceOf(ConnectionEvent.ConnectStart::class.java)\n    } else {\n      eventSequence.forEach loop@{\n        when (e.closes(it)) {\n          null -> return\n\n          // no open event\n          true -> return\n\n          // found open event\n          false -> return@loop // this is not the open event so continue\n        }\n      }\n      Assertions.fail<Any>(\"event $e without matching start event\")\n    }\n  }\n\n  override fun connectStart(\n    route: Route,\n    call: Call,\n  ) = logEvent(ConnectionEvent.ConnectStart(System.nanoTime(), route, call))\n\n  override fun connectFailed(\n    route: Route,\n    call: Call,\n    failure: IOException,\n  ) = logEvent(\n    ConnectionEvent.ConnectFailed(System.nanoTime(), route, call, failure),\n  )\n\n  override fun connectEnd(\n    connection: Connection,\n    route: Route,\n    call: Call,\n  ) {\n    logEvent(ConnectionEvent.ConnectEnd(System.nanoTime(), connection, route, call))\n  }\n\n  override fun connectionClosed(connection: Connection) = logEvent(ConnectionEvent.ConnectionClosed(System.nanoTime(), connection))\n\n  override fun connectionAcquired(\n    connection: Connection,\n    call: Call,\n  ) {\n    logEvent(ConnectionEvent.ConnectionAcquired(System.nanoTime(), connection, call))\n  }\n\n  override fun connectionReleased(\n    connection: Connection,\n    call: Call,\n  ) {\n    if (eventSequence.find { it is ConnectionEvent.ConnectStart && it.connection == connection } != null && connection is RealConnection) {\n      if (connection.noNewExchanges) {\n        assertThat(eventSequence).matchesPredicate { deque ->\n          deque.any { it is NoNewExchanges && it.connection == connection }\n        }\n      }\n    }\n\n    logEvent(ConnectionEvent.ConnectionReleased(System.nanoTime(), connection, call))\n  }\n\n  override fun noNewExchanges(connection: Connection) = logEvent(NoNewExchanges(System.nanoTime(), connection))\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/RecordingCookieJar.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport java.util.ArrayDeque\nimport java.util.Deque\n\nclass RecordingCookieJar : CookieJar {\n  private val requestCookies: Deque<List<Cookie>> = ArrayDeque()\n  private val responseCookies: Deque<List<Cookie>> = ArrayDeque()\n\n  fun enqueueRequestCookies(vararg cookies: Cookie) {\n    requestCookies.add(cookies.toList())\n  }\n\n  fun takeResponseCookies(): List<Cookie> = responseCookies.removeFirst()\n\n  fun assertResponseCookies(vararg cookies: String?) {\n    assertThat(takeResponseCookies().map(Cookie::toString)).containsExactly(*cookies)\n  }\n\n  override fun saveFromResponse(\n    url: HttpUrl,\n    cookies: List<Cookie>,\n  ) {\n    responseCookies.add(cookies)\n  }\n\n  override fun loadForRequest(url: HttpUrl): List<Cookie> = if (requestCookies.isEmpty()) emptyList() else requestCookies.removeFirst()\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/RecordingHostnameVerifier.kt",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.SSLSession\n\nclass RecordingHostnameVerifier : HostnameVerifier {\n  @JvmField\n  val calls: MutableList<String> = mutableListOf()\n\n  @Synchronized override fun verify(\n    hostname: String,\n    session: SSLSession,\n  ): Boolean {\n    calls.add(\"verify $hostname\")\n    return true\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/SimpleProvider.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport kotlin.jvm.Throws\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.params.provider.Arguments\nimport org.junit.jupiter.params.provider.ArgumentsProvider\n\nabstract class SimpleProvider : ArgumentsProvider {\n  @Suppress(\"OVERRIDE_DEPRECATION\")\n  override fun provideArguments(context: ExtensionContext) = arguments().map { Arguments.of(it) }.stream()\n\n  @Throws(Exception::class)\n  abstract fun arguments(): List<Any>\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/SpecificHostSocketFactory.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.Socket\nimport java.net.SocketAddress\nimport okhttp3.internal.platform.Platform\n\n/**\n * A [SocketFactory] that redirects connections to [defaultAddress] or specific overridden address via [set].\n */\nclass SpecificHostSocketFactory(\n  val defaultAddress: InetSocketAddress?,\n) : DelegatingSocketFactory(getDefault()) {\n  private val hostMapping = mutableMapOf<InetAddress, InetSocketAddress>()\n\n  /** Sets the [real] address for [requested].  */\n  operator fun set(\n    requested: InetAddress,\n    real: InetSocketAddress,\n  ) {\n    hostMapping[requested] = real\n  }\n\n  override fun createSocket(): Socket =\n    object : Socket() {\n      override fun connect(\n        endpoint: SocketAddress?,\n        timeout: Int,\n      ) {\n        val requested = (endpoint as InetSocketAddress)\n        val inetSocketAddress = hostMapping[requested.address] ?: defaultAddress ?: requested\n        Platform.get().log(\"Socket connection to: $inetSocketAddress was: $requested\")\n        super.connect(inetSocketAddress, timeout)\n      }\n    }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/TestUtilCommon.kt",
    "content": "/*\n * Copyright (C) 2023 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport okio.Buffer\nimport okio.Path\nimport okio.Path.Companion.toPath\n\nval okHttpRoot: Path\n  get() = getEnv(\"OKHTTP_ROOT\")!!.toPath()\n\nfun String(vararg codePoints: Int): String {\n  val buffer = Buffer()\n  for (codePoint in codePoints) {\n    buffer.writeUtf8CodePoint(codePoint)\n  }\n  return buffer.readUtf8()\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/TestUtilJvm.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.File\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.UnknownHostException\nimport java.util.Arrays\nimport java.util.concurrent.ThreadFactory\nimport okhttp3.internal.http2.Header\nimport okio.Buffer\nimport okio.FileSystem\nimport org.junit.jupiter.api.Assumptions.assumeFalse\nimport org.junit.jupiter.api.Assumptions.assumeTrue\n\nobject TestUtil {\n  @JvmField\n  val UNREACHABLE_ADDRESS_IPV4 = InetSocketAddress(\"198.51.100.1\", 8080)\n  val UNREACHABLE_ADDRESS_IPV6 = InetSocketAddress(\"::ffff:198.51.100.1\", 8080)\n\n  /** See `org.graalvm.nativeimage.ImageInfo`. */\n  @JvmStatic val isGraalVmImage = System.getProperty(\"org.graalvm.nativeimage.imagecode\") != null\n\n  @JvmStatic\n  fun headerEntries(vararg elements: String?): List<Header> = List(elements.size / 2) { Header(elements[it * 2]!!, elements[it * 2 + 1]!!) }\n\n  @JvmStatic\n  fun repeat(\n    c: Char,\n    count: Int,\n  ): String {\n    val array = CharArray(count)\n    Arrays.fill(array, c)\n    return String(array)\n  }\n\n  /**\n   * Okio buffers are internally implemented as a linked list of arrays. Usually this implementation\n   * detail is invisible to the caller, but subtle use of certain APIs may depend on these internal\n   * structures.\n   *\n   * We make such subtle calls in [okhttp3.internal.ws.MessageInflater] because we try to read a\n   * compressed stream that is terminated in a web socket frame even though the DEFLATE stream is\n   * not terminated.\n   *\n   * Use this method to create a degenerate Okio Buffer where each byte is in a separate segment of\n   * the internal list.\n   */\n  @JvmStatic\n  fun fragmentBuffer(buffer: Buffer): Buffer {\n    // Write each byte into a new buffer, then clone it so that the segments are shared.\n    // Shared segments cannot be compacted so we'll get a long chain of short segments.\n    val result = Buffer()\n    while (!buffer.exhausted()) {\n      val box = Buffer()\n      box.write(buffer, 1)\n      result.write(box.copy(), 1)\n    }\n    return result\n  }\n\n  tailrec fun File.isDescendentOf(directory: File): Boolean {\n    val parentFile = parentFile ?: return false\n    if (parentFile == directory) return true\n    return parentFile.isDescendentOf(directory)\n  }\n\n  /**\n   * See FinalizationTester for discussion on how to best trigger GC in tests.\n   * https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/\n   * java/lang/ref/FinalizationTester.java\n   */\n  @Throws(Exception::class)\n  @JvmStatic\n  fun awaitGarbageCollection() {\n    Runtime.getRuntime().gc()\n    Thread.sleep(100)\n    System.runFinalization()\n  }\n\n  @JvmStatic\n  fun assumeNetwork() {\n    try {\n      InetAddress.getByName(\"www.google.com\")\n    } catch (uhe: UnknownHostException) {\n      assumeTrue(false, \"requires network\")\n    }\n  }\n\n  @JvmStatic\n  fun assumeNotWindows() {\n    assumeFalse(windows, \"This test fails on Windows.\")\n  }\n\n  @JvmStatic\n  val windows: Boolean\n    get() = System.getProperty(\"os.name\", \"?\").startsWith(\"Windows\")\n\n  /**\n   * Make assertions about the suppressed exceptions on this. Prefer this over making direct calls\n   * so tests pass on GraalVM, where suppressed exceptions are silently discarded.\n   *\n   * https://github.com/oracle/graal/issues/3008\n   */\n  @JvmStatic\n  fun Throwable.assertSuppressed(block: (List<@JvmSuppressWildcards Throwable>) -> Unit) {\n    if (isGraalVmImage) return\n    block(suppressed.toList())\n  }\n\n  @JvmStatic\n  fun threadFactory(name: String): ThreadFactory =\n    object : ThreadFactory {\n      private var nextId = 1\n\n      override fun newThread(runnable: Runnable): Thread = Thread(runnable, \"$name-${nextId++}\")\n    }\n}\n\nfun getEnv(name: String) = System.getenv(name)\n\nval SYSTEM_FILE_SYSTEM = FileSystem.SYSTEM\n\nval isJvm = true\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/TestValueFactory.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\n  \"CANNOT_OVERRIDE_INVISIBLE_MEMBER\",\n  \"INVISIBLE_MEMBER\",\n  \"INVISIBLE_REFERENCE\",\n)\n\npackage okhttp3\n\nimport java.io.Closeable\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.Socket\nimport java.util.concurrent.TimeUnit\nimport javax.net.SocketFactory\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.HttpsURLConnection\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.internal.RecordingOkAuthenticator\nimport okhttp3.internal.concurrent.TaskFaker\nimport okhttp3.internal.concurrent.TaskRunner\nimport okhttp3.internal.concurrent.withLock\nimport okhttp3.internal.connection.ConnectionListener\nimport okhttp3.internal.connection.RealCall\nimport okhttp3.internal.connection.RealConnection\nimport okhttp3.internal.connection.RealConnectionPool\nimport okhttp3.internal.connection.RealRoutePlanner\nimport okhttp3.internal.http.RealInterceptorChain\nimport okhttp3.internal.http.RecordingProxySelector\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.internal.TlsUtil.localhost\n\n/**\n * OkHttp is usually tested with functional tests: these use public APIs to confirm behavior against\n * MockWebServer. In cases where logic is particularly tricky, we use unit tests. This class makes\n * it easy to get sample values to use in such tests.\n *\n * This class is pretty fast and loose with default values: it attempts to provide values that are\n * well-formed, but doesn't guarantee values are internally consistent. Callers must take care to\n * configure the factory when sample values impact the correctness of the test.\n */\nclass TestValueFactory : Closeable {\n  var taskFaker: TaskFaker = TaskFaker()\n  var taskRunner: TaskRunner = taskFaker.taskRunner\n  var dns: Dns = Dns.SYSTEM\n  var proxy: Proxy = Proxy.NO_PROXY\n  var proxySelector: ProxySelector = RecordingProxySelector()\n  var proxyAuthenticator: Authenticator = RecordingOkAuthenticator(\"password\", null)\n  var connectionSpecs: List<ConnectionSpec> =\n    listOf(\n      ConnectionSpec.MODERN_TLS,\n      ConnectionSpec.COMPATIBLE_TLS,\n      ConnectionSpec.CLEARTEXT,\n    )\n  var protocols: List<Protocol> =\n    listOf(\n      Protocol.HTTP_1_1,\n    )\n  var handshakeCertificates: HandshakeCertificates = localhost()\n  var sslSocketFactory: SSLSocketFactory? = handshakeCertificates.sslSocketFactory()\n  var hostnameVerifier: HostnameVerifier? = HttpsURLConnection.getDefaultHostnameVerifier()\n  var uriHost: String = \"example.com\"\n  var uriPort: Int = 1\n\n  fun newConnection(\n    pool: RealConnectionPool,\n    route: Route,\n    idleAtNanos: Long = Long.MAX_VALUE,\n    taskRunner: TaskRunner = this.taskRunner,\n  ): RealConnection {\n    val result =\n      RealConnection.newTestConnection(\n        taskRunner = taskRunner,\n        connectionPool = pool,\n        route = route,\n        socket = Socket(),\n        idleAtNs = idleAtNanos,\n      )\n    result.withLock { pool.put(result) }\n    return result\n  }\n\n  fun newConnectionPool(\n    taskRunner: TaskRunner = this.taskRunner,\n    maxIdleConnections: Int = Int.MAX_VALUE,\n  ): RealConnectionPool =\n    RealConnectionPool(\n      taskRunner = taskRunner,\n      maxIdleConnections = maxIdleConnections,\n      keepAliveDuration = 100L,\n      timeUnit = TimeUnit.NANOSECONDS,\n      connectionListener = ConnectionListener.NONE,\n    )\n\n  /** Returns an address that's without an SSL socket factory or hostname verifier.  */\n  fun newAddress(\n    uriHost: String = this.uriHost,\n    uriPort: Int = this.uriPort,\n    proxy: Proxy? = null,\n    proxySelector: ProxySelector = this.proxySelector,\n  ): Address =\n    Address(\n      uriHost = uriHost,\n      uriPort = uriPort,\n      dns = dns,\n      socketFactory = SocketFactory.getDefault(),\n      sslSocketFactory = null,\n      hostnameVerifier = null,\n      certificatePinner = null,\n      proxyAuthenticator = proxyAuthenticator,\n      proxy = proxy,\n      protocols = protocols,\n      connectionSpecs = connectionSpecs,\n      proxySelector = proxySelector,\n    )\n\n  fun newHttpsAddress(\n    uriHost: String = this.uriHost,\n    uriPort: Int = this.uriPort,\n    proxy: Proxy? = null,\n    proxySelector: ProxySelector = this.proxySelector,\n    sslSocketFactory: SSLSocketFactory? = this.sslSocketFactory,\n    hostnameVerifier: HostnameVerifier? = this.hostnameVerifier,\n  ): Address =\n    Address(\n      uriHost = uriHost,\n      uriPort = uriPort,\n      dns = dns,\n      socketFactory = SocketFactory.getDefault(),\n      sslSocketFactory = sslSocketFactory,\n      hostnameVerifier = hostnameVerifier,\n      certificatePinner = null,\n      proxyAuthenticator = proxyAuthenticator,\n      proxy = proxy,\n      protocols = protocols,\n      connectionSpecs = connectionSpecs,\n      proxySelector = proxySelector,\n    )\n\n  fun newRoute(\n    address: Address = newAddress(),\n    proxy: Proxy = this.proxy,\n    socketAddress: InetSocketAddress = InetSocketAddress.createUnresolved(uriHost, uriPort),\n  ): Route =\n    Route(\n      address = address,\n      proxy = proxy,\n      socketAddress = socketAddress,\n    )\n\n  fun newChain(call: RealCall): RealInterceptorChain =\n    RealInterceptorChain(\n      call = call,\n      interceptors = listOf(),\n      index = 0,\n      exchange = null,\n      request = call.request(),\n    )\n\n  fun newRoutePlanner(\n    client: OkHttpClient,\n    address: Address = newAddress(),\n  ): RealRoutePlanner {\n    val call = RealCall(client, Request(address.url), forWebSocket = false)\n    val chain = newChain(call)\n    return RealRoutePlanner(\n      taskRunner = client.taskRunner,\n      connectionPool = client.connectionPool.delegate,\n      readTimeoutMillis = client.readTimeoutMillis,\n      writeTimeoutMillis = client.writeTimeoutMillis,\n      socketConnectTimeoutMillis = chain.connectTimeoutMillis,\n      socketReadTimeoutMillis = chain.readTimeoutMillis,\n      pingIntervalMillis = client.pingIntervalMillis,\n      retryOnConnectionFailure = client.retryOnConnectionFailure,\n      fastFallback = client.fastFallback,\n      address = address,\n      routeDatabase = client.routeDatabase,\n      call = call,\n      request = call.request(),\n    )\n  }\n\n  override fun close() {\n    taskFaker.close()\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/UppercaseRequestInterceptor.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport okhttp3.Interceptor.Chain\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.ForwardingSink\nimport okio.Sink\nimport okio.buffer\n\n/** Rewrites the request body sent to the server to be all uppercase.  */\nclass UppercaseRequestInterceptor : Interceptor {\n  @Throws(IOException::class)\n  override fun intercept(chain: Chain): Response = chain.proceed(uppercaseRequest(chain.request()))\n\n  /** Returns a request that transforms `request` to be all uppercase.  */\n  private fun uppercaseRequest(request: Request): Request {\n    val uppercaseBody: RequestBody =\n      object : ForwardingRequestBody(request.body) {\n        @Throws(IOException::class)\n        override fun writeTo(sink: BufferedSink) {\n          delegate().writeTo(uppercaseSink(sink).buffer())\n        }\n      }\n    return request\n      .newBuilder()\n      .method(request.method, uppercaseBody)\n      .build()\n  }\n\n  private fun uppercaseSink(sink: Sink): Sink =\n    object : ForwardingSink(sink) {\n      @Throws(IOException::class)\n      override fun write(\n        source: Buffer,\n        byteCount: Long,\n      ) {\n        val bytes = source.readByteString(byteCount)\n        delegate.write(\n          Buffer()\n            .write(bytes.toAsciiUppercase()),\n          byteCount,\n        )\n      }\n    }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/UppercaseResponseInterceptor.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport okhttp3.Interceptor.Chain\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ForwardingSource\nimport okio.buffer\n\n/** Rewrites the response body returned from the server to be all uppercase.  */\nclass UppercaseResponseInterceptor : Interceptor {\n  @Throws(IOException::class)\n  override fun intercept(chain: Chain): Response = uppercaseResponse(chain.proceed(chain.request()))\n\n  private fun uppercaseResponse(response: Response): Response {\n    val uppercaseBody: ResponseBody =\n      object : ForwardingResponseBody(response.body) {\n        override fun source(): BufferedSource = uppercaseSource(delegate().source()).buffer()\n      }\n    return response\n      .newBuilder()\n      .body(uppercaseBody)\n      .build()\n  }\n\n  private fun uppercaseSource(source: BufferedSource): ForwardingSource {\n    return object : ForwardingSource(source) {\n      @Throws(IOException::class)\n      override fun read(\n        sink: Buffer,\n        byteCount: Long,\n      ): Long {\n        val buffer = Buffer()\n        val read = delegate.read(buffer, byteCount)\n        if (read != -1L) {\n          sink.write(buffer.readByteString().toAsciiUppercase())\n        }\n        return read\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/internal/RecordingOkAuthenticator.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal\n\nimport java.io.IOException\nimport okhttp3.Authenticator\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.Route\n\nclass RecordingOkAuthenticator(\n  val credential: String?,\n  val scheme: String?,\n) : Authenticator {\n  val responses = mutableListOf<Response>()\n  val routes = mutableListOf<Route>()\n\n  fun onlyResponse() = responses.single()\n\n  fun onlyRoute() = routes.single()\n\n  @Throws(IOException::class)\n  override fun authenticate(\n    route: Route?,\n    response: Response,\n  ): Request? {\n    if (route == null) throw NullPointerException(\"route == null\")\n    responses += response\n    routes += route\n    if (!schemeMatches(response) || credential == null) return null\n    val header =\n      when (response.code) {\n        407 -> \"Proxy-Authorization\"\n        else -> \"Authorization\"\n      }\n    return response.request\n      .newBuilder()\n      .addHeader(header, credential)\n      .build()\n  }\n\n  private fun schemeMatches(response: Response): Boolean {\n    if (scheme == null) return true\n    return response.challenges().any { it.scheme.equals(scheme, ignoreCase = true) }\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/internal/concurrent/TaskFaker.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:Suppress(\n  \"CANNOT_OVERRIDE_INVISIBLE_MEMBER\",\n  \"INVISIBLE_MEMBER\",\n  \"INVISIBLE_REFERENCE\",\n)\n\npackage okhttp3.internal.concurrent\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport java.io.Closeable\nimport java.util.AbstractQueue\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.Executors\nimport java.util.concurrent.TimeUnit\nimport java.util.logging.Logger\nimport okhttp3.TestUtil.threadFactory\n\n/**\n * Runs a [TaskRunner] in a controlled environment so that everything is sequential and\n * deterministic.\n *\n * This class ensures that at most one thread is running at a time. This is initially the JUnit test\n * thread, which yields its execution privilege while calling [runTasks], [runNextTask], or\n * [advanceUntil]. These functions don't return until the task threads are all idle.\n *\n * Task threads release their execution privilege in these ways:\n *\n *  * By yielding in [TaskRunner.Backend.coordinatorWait].\n *  * By yielding in [BlockingQueue.poll].\n *  * By completing.\n */\nclass TaskFaker : Closeable {\n  val logger = Logger.getLogger(\"TaskFaker.\" + instance++)\n\n  /** Though this executor service may hold many threads, they are not executed concurrently. */\n  private val tasksExecutor = Executors.newCachedThreadPool(threadFactory(\"TaskFaker\"))\n\n  /**\n   * True if this task faker has ever had multiple tasks scheduled to run concurrently. Guarded by\n   * `this`.\n   */\n  var isParallel = false\n\n  /** Number of calls to [TaskRunner.Backend.execute]. Guarded by `this`. */\n  var executeCallCount = 0\n\n  /** Guarded by [taskRunner]. */\n  var nanoTime = 0L\n    private set\n\n  /** Backlog of tasks to run. Only one task runs at a time. Guarded by `this`. */\n  private val serialTaskQueue = ArrayDeque<SerialTask>()\n\n  /** The task that's currently executing. Guarded by `this`. */\n  private var currentTask: SerialTask = TestThreadSerialTask\n\n  /** The coordinator task if it's waiting, and how it will resume. Guarded by `this`. */\n  private var waitingCoordinatorTask: SerialTask? = null\n  private var waitingCoordinatorInterrupted = false\n  private var waitingCoordinatorNotified = false\n\n  /** How many times a new task has been started. Guarded by `this`. */\n  private var contextSwitchCount = 0\n\n  /** Guarded by `this`. */\n  private var activeThreads = 0\n\n  /** A task runner that posts tasks to this fake. Tasks won't be executed until requested. */\n  val taskRunner: TaskRunner =\n    TaskRunner(\n      object : TaskRunner.Backend {\n        override fun execute(\n          taskRunner: TaskRunner,\n          runnable: Runnable,\n        ) {\n          taskRunner.assertLockHeld()\n\n          val queuedTask = RunnableSerialTask(runnable)\n          serialTaskQueue += queuedTask\n          executeCallCount++\n          isParallel = serialTaskQueue.size > 1\n        }\n\n        override fun nanoTime() = nanoTime\n\n        override fun coordinatorNotify(taskRunner: TaskRunner) {\n          taskRunner.assertLockHeld()\n          check(waitingCoordinatorTask != null)\n\n          // Queue a task to resume the waiting coordinator.\n          serialTaskQueue +=\n            object : SerialTask {\n              override fun start() {\n                taskRunner.assertLockHeld()\n                val coordinatorTask = waitingCoordinatorTask\n                if (coordinatorTask != null) {\n                  waitingCoordinatorNotified = true\n                  currentTask = coordinatorTask\n                  taskRunner.notifyAll()\n                } else {\n                  startNextTask()\n                }\n              }\n            }\n        }\n\n        override fun coordinatorWait(\n          taskRunner: TaskRunner,\n          nanos: Long,\n        ) {\n          taskRunner.assertLockHeld()\n          check(waitingCoordinatorTask == null)\n          if (nanos == 0L) return\n\n          // Yield until notified, interrupted, or the duration elapses.\n          val waitUntil = nanoTime + nanos\n          val self = currentTask\n          waitingCoordinatorTask = self\n          waitingCoordinatorNotified = false\n          waitingCoordinatorInterrupted = false\n          yieldUntil {\n            waitingCoordinatorNotified || waitingCoordinatorInterrupted || nanoTime >= waitUntil\n          }\n\n          waitingCoordinatorTask = null\n          waitingCoordinatorNotified = false\n          if (waitingCoordinatorInterrupted) {\n            waitingCoordinatorInterrupted = false\n            throw InterruptedException()\n          }\n        }\n\n        override fun <T> decorate(queue: BlockingQueue<T>) = TaskFakerBlockingQueue(queue)\n      },\n      logger = logger,\n    )\n\n  /** Runs all tasks that are ready. Used by the test thread only. */\n  fun runTasks() {\n    advanceUntil(nanoTime)\n  }\n\n  /** Advance the simulated clock, then runs tasks that are ready. Used by the test thread only. */\n  fun advanceUntil(newTime: Long) {\n    taskRunner.assertLockNotHeld()\n\n    taskRunner.withLock {\n      check(currentTask == TestThreadSerialTask)\n      nanoTime = newTime\n      yieldUntil(ResumePriority.AfterOtherTasks)\n    }\n  }\n\n  /** Confirm all tasks have completed. Used by the test thread only. */\n  fun assertNoMoreTasks() {\n    taskRunner.assertLockNotHeld()\n\n    taskRunner.withLock {\n      assertThat(activeThreads).isEqualTo(0)\n    }\n  }\n\n  /** Unblock a waiting task thread. Used by the test thread only. */\n  fun interruptCoordinatorThread() {\n    taskRunner.assertLockNotHeld()\n    require(currentTask == TestThreadSerialTask)\n\n    // Queue a task to interrupt the waiting coordinator.\n    serialTaskQueue +=\n      object : SerialTask {\n        override fun start() {\n          taskRunner.assertLockHeld()\n          waitingCoordinatorInterrupted = true\n          val coordinatorTask = waitingCoordinatorTask ?: error(\"no coordinator waiting\")\n          currentTask = coordinatorTask\n          taskRunner.notifyAll()\n        }\n      }\n\n    // Let the coordinator process its interruption.\n    runTasks()\n  }\n\n  /** Ask a single task to proceed. Used by the test thread only. */\n  fun runNextTask() {\n    taskRunner.assertLockNotHeld()\n\n    taskRunner.withLock {\n      val contextSwitchCountBefore = contextSwitchCount\n      yieldUntil(ResumePriority.BeforeOtherTasks) {\n        contextSwitchCount > contextSwitchCountBefore\n      }\n    }\n  }\n\n  /** Sleep until [durationNanos] elapses. For use by the task threads. */\n  fun sleep(durationNanos: Long) {\n    taskRunner.withLock {\n      val sleepUntil = nanoTime + durationNanos\n      yieldUntil { nanoTime >= sleepUntil }\n    }\n  }\n\n  /**\n   * Artificially stall until manually resumed by the test thread with [runTasks]. Use this to\n   * simulate races in tasks that doesn't have a deterministic sequence.\n   */\n  fun yield() {\n    taskRunner.assertLockNotHeld()\n    taskRunner.withLock {\n      yieldUntil()\n    }\n  }\n\n  /** Process the queue until [condition] returns true. */\n  private tailrec fun yieldUntil(\n    strategy: ResumePriority = ResumePriority.AfterEnqueuedTasks,\n    condition: () -> Boolean = { true },\n  ) {\n    taskRunner.assertLockHeld()\n    val self = currentTask\n\n    val yieldCompleteTask =\n      object : SerialTask {\n        override fun isReady() = condition()\n\n        override fun start() {\n          taskRunner.assertLockHeld()\n          currentTask = self\n          taskRunner.notifyAll()\n        }\n      }\n\n    if (strategy == ResumePriority.BeforeOtherTasks) {\n      serialTaskQueue.addFirst(yieldCompleteTask)\n    } else {\n      serialTaskQueue.addLast(yieldCompleteTask)\n    }\n\n    val startedTask = startNextTask()\n    val otherTasksStarted = startedTask != yieldCompleteTask\n\n    try {\n      while (currentTask != self) {\n        taskRunner.wait()\n      }\n    } finally {\n      serialTaskQueue.remove(yieldCompleteTask)\n    }\n\n    // If we're yielding until we're exhausted and a task run, keep going until a task doesn't run.\n    if (strategy == ResumePriority.AfterOtherTasks && otherTasksStarted) {\n      return yieldUntil(strategy, condition)\n    }\n  }\n\n  private enum class ResumePriority {\n    /** Resumes as soon as the condition is satisfied. */\n    BeforeOtherTasks,\n\n    /** Resumes after the already-enqueued tasks. */\n    AfterEnqueuedTasks,\n\n    /** Resumes after all other tasks, including tasks enqueued while yielding. */\n    AfterOtherTasks,\n  }\n\n  /** Returns the task that was started, or null if there were no tasks to start. */\n  private fun startNextTask(): SerialTask? {\n    taskRunner.assertLockHeld()\n\n    val index = serialTaskQueue.indexOfFirst { it.isReady() }\n    if (index == -1) return null\n\n    val nextTask = serialTaskQueue.removeAt(index)\n    currentTask = nextTask\n    contextSwitchCount++\n    nextTask.start()\n    return nextTask\n  }\n\n  private interface SerialTask {\n    /** Returns true if this task is ready to start. */\n    fun isReady() = true\n\n    /** Do this task's work, and then start another, such as by calling [startNextTask]. */\n    fun start()\n  }\n\n  private object TestThreadSerialTask : SerialTask {\n    override fun start() = error(\"unexpected call\")\n  }\n\n  inner class RunnableSerialTask(\n    private val runnable: Runnable,\n  ) : SerialTask {\n    override fun start() {\n      taskRunner.assertLockHeld()\n      require(currentTask == this)\n      activeThreads++\n\n      tasksExecutor.execute {\n        taskRunner.assertLockNotHeld()\n        require(currentTask == this)\n        try {\n          runnable.run()\n          require(currentTask == this) { \"unexpected current task: $currentTask\" }\n        } finally {\n          taskRunner.withLock {\n            activeThreads--\n            startNextTask()\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * This blocking queue hooks into a fake clock rather than using regular JVM timing for functions\n   * like [poll]. It is only usable within task faker tasks.\n   */\n  private inner class TaskFakerBlockingQueue<T>(\n    val delegate: BlockingQueue<T>,\n  ) : AbstractQueue<T>(),\n    BlockingQueue<T> {\n    override val size: Int = delegate.size\n\n    private var editCount = 0\n\n    override fun poll(): T = delegate.poll()\n\n    override fun poll(\n      timeout: Long,\n      unit: TimeUnit,\n    ): T? {\n      return taskRunner.withLock {\n        val waitUntil = nanoTime + unit.toNanos(timeout)\n        while (true) {\n          val result = poll()\n          if (result != null) return@withLock result\n          if (nanoTime >= waitUntil) return@withLock null\n          val editCountBefore = editCount\n          yieldUntil { nanoTime >= waitUntil || editCount > editCountBefore }\n        }\n        // TODO report compiler bug\n        TODO(\"Can't get here\")\n      }\n    }\n\n    override fun put(element: T) {\n      taskRunner.withLock {\n        delegate.put(element)\n        editCount++\n      }\n    }\n\n    override fun iterator() = error(\"unsupported\")\n\n    override fun offer(e: T) = error(\"unsupported\")\n\n    override fun peek(): T = error(\"unsupported\")\n\n    override fun offer(\n      element: T,\n      timeout: Long,\n      unit: TimeUnit,\n    ) = error(\"unsupported\")\n\n    override fun take() = error(\"unsupported\")\n\n    override fun remainingCapacity() = error(\"unsupported\")\n\n    override fun drainTo(sink: MutableCollection<in T>) = error(\"unsupported\")\n\n    override fun drainTo(\n      sink: MutableCollection<in T>,\n      maxElements: Int,\n    ) = error(\"unsupported\")\n  }\n\n  /** Returns true if no tasks have been scheduled. This runs the coordinator for confirmation. */\n  fun isIdle() = taskRunner.activeQueues().isEmpty()\n\n  override fun close() {\n    tasksExecutor.shutdownNow()\n  }\n\n  companion object {\n    var instance = 0\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/internal/duplex/AsyncRequestBody.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.duplex\n\nimport java.util.concurrent.BlockingQueue\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.TimeUnit.SECONDS\nimport okhttp3.MediaType\nimport okhttp3.RequestBody\nimport okio.BufferedSink\nimport org.junit.jupiter.api.Assertions.assertTrue\n\n/** A duplex request body that keeps the provided sinks so they can be written to later.  */\nclass AsyncRequestBody : RequestBody() {\n  private val requestBodySinks: BlockingQueue<BufferedSink> = LinkedBlockingQueue()\n\n  override fun contentType(): MediaType? = null\n\n  override fun writeTo(sink: BufferedSink) {\n    requestBodySinks.add(sink)\n  }\n\n  override fun isDuplex(): Boolean = true\n\n  @Throws(InterruptedException::class)\n  fun takeSink(): BufferedSink = requestBodySinks.poll(5, SECONDS) ?: throw AssertionError(\"no sink to take\")\n\n  fun assertNoMoreSinks() {\n    assertTrue(requestBodySinks.isEmpty())\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/internal/duplex/MockSocketHandler.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.internal.duplex\n\nimport java.io.IOException\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.FutureTask\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.TimeUnit\nimport mockwebserver3.SocketHandler\nimport okhttp3.internal.connection.BufferedSocket\nimport okhttp3.internal.connection.asBufferedSocket\nimport okio.Socket\nimport okio.utf8Size\n\nprivate typealias Action = (BufferedSocket) -> Unit\n\n/**\n * A scriptable request/response conversation. Create the script by calling methods like\n * [receiveRequest] in the sequence they are run.\n */\nclass MockSocketHandler : SocketHandler {\n  private val actions = LinkedBlockingQueue<Action>()\n  private val results = LinkedBlockingQueue<FutureTask<Void>>()\n\n  fun receiveRequest(expected: String) =\n    apply {\n      actions += { stream ->\n        val actual = stream.source.readUtf8(expected.utf8Size())\n        if (actual != expected) throw AssertionError(\"$actual != $expected\")\n      }\n    }\n\n  fun exhaustRequest() =\n    apply {\n      actions += { stream ->\n        if (!stream.source.exhausted()) throw AssertionError(\"expected exhausted\")\n      }\n    }\n\n  fun cancelStream() =\n    apply {\n      actions += { stream -> stream.cancel() }\n    }\n\n  fun requestIOException() =\n    apply {\n      actions += { stream ->\n        try {\n          stream.source.exhausted()\n          throw AssertionError(\"expected IOException\")\n        } catch (expected: IOException) {\n        }\n      }\n    }\n\n  @JvmOverloads\n  fun sendResponse(\n    s: String,\n    responseSent: CountDownLatch = CountDownLatch(1),\n  ) = apply {\n    actions += { stream ->\n      stream.sink.writeUtf8(s)\n      stream.sink.flush()\n      responseSent.countDown()\n    }\n  }\n\n  fun exhaustResponse() =\n    apply {\n      actions += { stream -> stream.sink.close() }\n    }\n\n  fun sleep(\n    duration: Long,\n    unit: TimeUnit,\n  ) = apply {\n    actions += { Thread.sleep(unit.toMillis(duration)) }\n  }\n\n  override fun handle(socket: Socket) {\n    val task = serviceSocketTask(socket.asBufferedSocket())\n    results.add(task)\n    task.run()\n  }\n\n  /** Returns a task that processes both request and response from [socket]. */\n  private fun serviceSocketTask(socket: BufferedSocket): FutureTask<Void> {\n    return FutureTask<Void> {\n      socket.source.use {\n        socket.sink.use {\n          while (true) {\n            val action = actions.poll() ?: break\n            action(socket)\n          }\n        }\n      }\n      return@FutureTask null\n    }\n  }\n\n  /** Returns once all stream actions complete successfully. */\n  fun awaitSuccess() {\n    val futureTask =\n      results.poll(5, TimeUnit.SECONDS)\n        ?: throw AssertionError(\"no onRequest call received\")\n    futureTask.get(5, TimeUnit.SECONDS)\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/internal/http/RecordingProxySelector.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.internal.http\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport java.io.IOException\nimport java.net.InetSocketAddress\nimport java.net.Proxy\nimport java.net.ProxySelector\nimport java.net.SocketAddress\nimport java.net.URI\nimport okhttp3.internal.format\n\nclass RecordingProxySelector : ProxySelector() {\n  @JvmField val proxies = mutableListOf<Proxy>()\n\n  val requestedUris = mutableListOf<URI>()\n  val failures = mutableListOf<String>()\n\n  override fun select(uri: URI): List<Proxy> {\n    requestedUris.add(uri)\n    return proxies\n  }\n\n  fun assertRequests(vararg expectedUris: URI?) {\n    assertThat(requestedUris).containsExactly(*expectedUris)\n    requestedUris.clear()\n  }\n\n  override fun connectFailed(\n    uri: URI,\n    sa: SocketAddress,\n    ioe: IOException,\n  ) {\n    val socketAddress = sa as InetSocketAddress\n    failures.add(format(\"%s %s:%d %s\", uri, socketAddress, socketAddress.port, ioe.message!!))\n  }\n\n  override fun toString() = \"RecordingProxySelector\"\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/okio/LoggingFilesystem.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.okio\n\nimport okio.FileSystem\nimport okio.ForwardingFileSystem\nimport okio.Path\nimport okio.Sink\nimport okio.Source\n\nclass LoggingFilesystem(\n  fileSystem: FileSystem,\n) : ForwardingFileSystem(fileSystem) {\n  fun log(line: String) {\n    println(line)\n  }\n\n  override fun appendingSink(\n    file: Path,\n    mustExist: Boolean,\n  ): Sink {\n    log(\"appendingSink($file)\")\n\n    return super.appendingSink(file, mustExist)\n  }\n\n  override fun atomicMove(\n    source: Path,\n    target: Path,\n  ) {\n    log(\"atomicMove($source, $target)\")\n\n    super.atomicMove(source, target)\n  }\n\n  override fun createDirectory(\n    dir: Path,\n    mustCreate: Boolean,\n  ) {\n    log(\"createDirectory($dir)\")\n\n    super.createDirectory(dir, mustCreate)\n  }\n\n  override fun delete(\n    path: Path,\n    mustExist: Boolean,\n  ) {\n    log(\"delete($path)\")\n\n    super.delete(path, mustExist)\n  }\n\n  override fun sink(\n    file: Path,\n    mustCreate: Boolean,\n  ): Sink {\n    log(\"sink($file)\")\n\n    return super.sink(file, mustCreate)\n  }\n\n  override fun source(file: Path): Source {\n    log(\"source($file)\")\n\n    return super.source(file)\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/testing/Flaky.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.testing\n\n/**\n * Annotation marking a test as flaky, and requires extra logging and linking against\n * a known github issue.  This does not ignore the failure.\n */\n@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)\n@Retention(AnnotationRetention.RUNTIME)\nannotation class Flaky\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/testing/PlatformRule.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.testing\n\nimport android.os.Build\nimport com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider\nimport com.amazon.corretto.crypto.provider.SelfTestStatus\nimport java.lang.reflect.Method\nimport java.security.Security\nimport okhttp3.TestUtil\nimport okhttp3.internal.platform.ConscryptPlatform\nimport okhttp3.internal.platform.Jdk8WithJettyBootPlatform\nimport okhttp3.internal.platform.Jdk9Platform\nimport okhttp3.internal.platform.OpenJSSEPlatform\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.platform.PlatformRegistry\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport okhttp3.tls.internal.TlsUtil.localhost\nimport org.bouncycastle.jce.provider.BouncyCastleProvider\nimport org.bouncycastle.jsse.provider.BouncyCastleJsseProvider\nimport org.conscrypt.Conscrypt\nimport org.hamcrest.BaseMatcher\nimport org.hamcrest.CoreMatchers.anything\nimport org.hamcrest.Description\nimport org.hamcrest.Matcher\nimport org.hamcrest.StringDescription\nimport org.hamcrest.TypeSafeMatcher\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.Assumptions.assumeFalse\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.extension.AfterEachCallback\nimport org.junit.jupiter.api.extension.BeforeEachCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.InvocationInterceptor\nimport org.junit.jupiter.api.extension.ReflectiveInvocationContext\nimport org.openjsse.net.ssl.OpenJSSE\nimport org.opentest4j.TestAbortedException\n\n/**\n * Marks a test as Platform aware, before the test runs a consistent Platform will be\n * established e.g. SecurityProvider for Conscrypt installed.\n *\n * Also allows a test file to state general platform assumptions, or for individual test.\n */\n@Suppress(\"unused\", \"MemberVisibilityCanBePrivate\")\nopen class PlatformRule\n  @JvmOverloads\n  constructor(\n    val requiredPlatformName: String? = null,\n    val platform: Platform? = null,\n  ) : BeforeEachCallback,\n    AfterEachCallback,\n    InvocationInterceptor {\n    private val versionChecks = mutableListOf<Pair<Matcher<out Any>, Matcher<out Any>>>()\n\n    override fun beforeEach(context: ExtensionContext) {\n      setupPlatform()\n    }\n\n    override fun afterEach(context: ExtensionContext) {\n      resetPlatform()\n    }\n\n    override fun interceptTestMethod(\n      invocation: InvocationInterceptor.Invocation<Void>,\n      invocationContext: ReflectiveInvocationContext<Method>,\n      extensionContext: ExtensionContext,\n    ) {\n      var failed = false\n      try {\n        invocation.proceed()\n      } catch (e: TestAbortedException) {\n        throw e\n      } catch (e: Throwable) {\n        failed = true\n        rethrowIfNotExpected(e)\n      } finally {\n        resetPlatform()\n      }\n      if (!failed) {\n        failIfExpected()\n      }\n    }\n\n    fun setupPlatform() {\n      if (requiredPlatformName != null) {\n        assumeTrue(getPlatformSystemProperty() == requiredPlatformName)\n      }\n\n      if (platform != null) {\n        Platform.resetForTests(platform)\n      } else {\n        Platform.resetForTests()\n      }\n\n      if (requiredPlatformName != null) {\n        System.err.println(\"Running with ${Platform.get().javaClass.simpleName}\")\n      }\n    }\n\n    fun resetPlatform() {\n      if (platform != null) {\n        Platform.resetForTests()\n      }\n    }\n\n    fun expectFailureOnConscryptPlatform() {\n      expectFailure(platformMatches(CONSCRYPT_PROPERTY))\n    }\n\n    fun expectFailureOnCorrettoPlatform() {\n      expectFailure(platformMatches(CORRETTO_PROPERTY))\n    }\n\n    fun expectFailureOnOpenJSSEPlatform() {\n      expectFailure(platformMatches(OPENJSSE_PROPERTY))\n    }\n\n    fun expectFailureFromJdkVersion(majorVersion: Int) {\n      if (!TestUtil.isGraalVmImage) {\n        expectFailure(fromMajor(majorVersion))\n      }\n    }\n\n    fun expectFailureOnJdkVersion(majorVersion: Int) {\n      if (!TestUtil.isGraalVmImage) {\n        expectFailure(onMajor(majorVersion))\n      }\n    }\n\n    fun expectFailureOnLoomPlatform() {\n      expectFailure(platformMatches(LOOM_PROPERTY))\n    }\n\n    private fun expectFailure(\n      versionMatcher: Matcher<out Any>,\n      failureMatcher: Matcher<out Any> = anything(),\n    ) {\n      versionChecks.add(Pair(versionMatcher, failureMatcher))\n    }\n\n    fun platformMatches(platform: String): Matcher<Any> =\n      object : BaseMatcher<Any>() {\n        override fun describeTo(description: Description) {\n          description.appendText(platform)\n        }\n\n        override fun matches(item: Any?): Boolean = getPlatformSystemProperty() == platform\n      }\n\n    fun fromMajor(version: Int): Matcher<PlatformVersion> =\n      object : TypeSafeMatcher<PlatformVersion>() {\n        override fun describeTo(description: Description) {\n          description.appendText(\"JDK with version from $version\")\n        }\n\n        override fun matchesSafely(item: PlatformVersion): Boolean = item.majorVersion >= version\n      }\n\n    fun onMajor(version: Int): Matcher<PlatformVersion> =\n      object : TypeSafeMatcher<PlatformVersion>() {\n        override fun describeTo(description: Description) {\n          description.appendText(\"JDK with version $version\")\n        }\n\n        override fun matchesSafely(item: PlatformVersion): Boolean = item.majorVersion == version\n      }\n\n    fun rethrowIfNotExpected(e: Throwable) {\n      versionChecks.forEach { (versionMatcher, failureMatcher) ->\n        if (versionMatcher.matches(PlatformVersion) && failureMatcher.matches(e)) {\n          return\n        }\n      }\n\n      throw e\n    }\n\n    fun failIfExpected() {\n      versionChecks.forEach { (versionMatcher, failureMatcher) ->\n        if (versionMatcher.matches(PlatformVersion)) {\n          val description = StringDescription()\n          versionMatcher.describeTo(description)\n          description.appendText(\" expected to fail with exception that \")\n          failureMatcher.describeTo(description)\n\n          fail<Any>(description.toString())\n        }\n      }\n    }\n\n    fun isConscrypt() = getPlatformSystemProperty() == CONSCRYPT_PROPERTY\n\n    fun isJdk9() = getPlatformSystemProperty() == JDK9_PROPERTY\n\n    fun isJdk8() = getPlatformSystemProperty() == JDK8_PROPERTY\n\n    fun isJdk8Alpn() = getPlatformSystemProperty() == JDK8_ALPN_PROPERTY\n\n    fun isBouncyCastle() = getPlatformSystemProperty() == BOUNCYCASTLE_PROPERTY\n\n    fun isOpenJsse() = getPlatformSystemProperty() == OPENJSSE_PROPERTY\n\n    fun isLoom() = getPlatformSystemProperty() == LOOM_PROPERTY\n\n    fun isGraalVMImage() = TestUtil.isGraalVmImage\n\n    fun hasHttp2Support() = !isJdk8()\n\n    fun assumeConscrypt() {\n      assumeTrue(getPlatformSystemProperty() == CONSCRYPT_PROPERTY)\n    }\n\n    fun assumeJdk9() {\n      assumeTrue(getPlatformSystemProperty() == JDK9_PROPERTY)\n    }\n\n    fun assumeOpenJSSE() {\n      assumeTrue(getPlatformSystemProperty() == OPENJSSE_PROPERTY)\n    }\n\n    fun assumeJdk8() {\n      assumeTrue(getPlatformSystemProperty() == JDK8_PROPERTY)\n    }\n\n    fun assumeJdk8Alpn() {\n      assumeTrue(getPlatformSystemProperty() == JDK8_ALPN_PROPERTY)\n    }\n\n    fun assumeCorretto() {\n      assumeTrue(getPlatformSystemProperty() == CORRETTO_PROPERTY)\n    }\n\n    fun assumeBouncyCastle() {\n      assumeTrue(getPlatformSystemProperty() == BOUNCYCASTLE_PROPERTY)\n    }\n\n    fun assumeOpenJsse() {\n      assumeTrue(getPlatformSystemProperty() == OPENJSSE_PROPERTY)\n    }\n\n    fun assumeLoom() {\n      assumeTrue(getPlatformSystemProperty() == LOOM_PROPERTY)\n    }\n\n    fun assumeHttp2Support() {\n      assumeTrue(getPlatformSystemProperty() != JDK8_PROPERTY)\n    }\n\n    fun assumeAndroid() {\n      assumeTrue(Platform.isAndroid)\n    }\n\n    fun assumeGraalVMImage() {\n      assumeTrue(isGraalVMImage())\n    }\n\n    fun assumeNotConscrypt() {\n      assumeTrue(getPlatformSystemProperty() != CONSCRYPT_PROPERTY)\n    }\n\n    fun assumeNotJdk9() {\n      assumeTrue(getPlatformSystemProperty() != JDK9_PROPERTY)\n    }\n\n    fun assumeNotJdk8() {\n      assumeTrue(getPlatformSystemProperty() != JDK8_PROPERTY)\n    }\n\n    fun assumeNotJdk8Alpn() {\n      assumeTrue(getPlatformSystemProperty() != JDK8_ALPN_PROPERTY)\n    }\n\n    fun assumeNotOpenJSSE() {\n      assumeTrue(getPlatformSystemProperty() != OPENJSSE_PROPERTY)\n    }\n\n    fun assumeNotLoom() {\n      assumeTrue(getPlatformSystemProperty() != LOOM_PROPERTY)\n    }\n\n    fun assumeNotCorretto() {\n      assumeTrue(getPlatformSystemProperty() != CORRETTO_PROPERTY)\n    }\n\n    fun assumeNotBouncyCastle() {\n      // Most failures are with MockWebServer\n      // org.bouncycastle.tls.TlsFatalAlertReceived: handshake_failure(40)\n      //        at org.bouncycastle.tls.TlsProtocol.handleAlertMessage(TlsProtocol.java:241)\n      assumeTrue(getPlatformSystemProperty() != BOUNCYCASTLE_PROPERTY)\n    }\n\n    fun assumeNotOpenJsse() {\n      assumeTrue(getPlatformSystemProperty() != OPENJSSE_PROPERTY)\n    }\n\n    fun assumeNotHttp2Support() {\n      assumeTrue(getPlatformSystemProperty() == JDK8_PROPERTY)\n    }\n\n    fun assumeJettyBootEnabled() {\n      assumeTrue(isAlpnBootEnabled())\n    }\n\n    fun assumeNotAndroid() {\n      assumeFalse(Platform.isAndroid)\n    }\n\n    fun assumeNotGraalVMImage() {\n      assumeFalse(isGraalVMImage())\n    }\n\n    fun assumeJdkVersion(majorVersion: Int) {\n      assumeNotAndroid()\n      assumeNotGraalVMImage()\n      assumeTrue(PlatformVersion.majorVersion == majorVersion)\n    }\n\n    fun androidSdkVersion(): Int? =\n      if (Platform.isAndroid) {\n        Build.VERSION.SDK_INT\n      } else {\n        null\n      }\n\n    fun localhostHandshakeCertificates(): HandshakeCertificates =\n      when {\n        isBouncyCastle() -> localhostHandshakeCertificatesWithRsa2048\n        else -> localhost()\n      }\n\n    val isAndroid: Boolean\n      get() = Platform.Companion.isAndroid\n\n    companion object {\n      const val PROPERTY_NAME = \"okhttp.platform\"\n      const val CONSCRYPT_PROPERTY = \"conscrypt\"\n      const val CORRETTO_PROPERTY = \"corretto\"\n      const val JDK9_PROPERTY = \"jdk9\"\n      const val JDK8_ALPN_PROPERTY = \"jdk8alpn\"\n      const val JDK8_PROPERTY = \"jdk8\"\n      const val OPENJSSE_PROPERTY = \"openjsse\"\n      const val BOUNCYCASTLE_PROPERTY = \"bouncycastle\"\n      const val LOOM_PROPERTY = \"loom\"\n\n      /**\n       * For whatever reason our BouncyCastle provider doesn't work with ECDSA keys. Just configure it\n       * to use RSA-2048 instead.\n       *\n       * (We otherwise prefer ECDSA because it's faster.)\n       */\n      private val localhostHandshakeCertificatesWithRsa2048: HandshakeCertificates by lazy {\n        val heldCertificate =\n          HeldCertificate\n            .Builder()\n            .commonName(\"localhost\")\n            .addSubjectAlternativeName(\"localhost\")\n            .rsa2048()\n            .build()\n        return@lazy HandshakeCertificates\n          .Builder()\n          .heldCertificate(heldCertificate)\n          .addTrustedCertificate(heldCertificate.certificate)\n          .build()\n      }\n\n      init {\n        val platformSystemProperty = getPlatformSystemProperty()\n\n        if (platformSystemProperty == JDK9_PROPERTY) {\n          if (System.getProperty(\"javax.net.debug\") == null) {\n            System.setProperty(\"javax.net.debug\", \"\")\n          }\n        } else if (platformSystemProperty == CONSCRYPT_PROPERTY) {\n          if (Security.getProviders()[0].name != \"Conscrypt\") {\n            if (!Conscrypt.isAvailable()) {\n              System.err.println(\"Warning: Conscrypt not available\")\n            }\n\n            val provider =\n              Conscrypt\n                .newProviderBuilder()\n                .provideTrustManager(true)\n                .build()\n            Security.insertProviderAt(provider, 1)\n          }\n        } else if (platformSystemProperty == JDK8_ALPN_PROPERTY) {\n          if (!isAlpnBootEnabled()) {\n            System.err.println(\"Warning: ALPN Boot not enabled\")\n          }\n        } else if (platformSystemProperty == JDK8_PROPERTY) {\n          if (isAlpnBootEnabled()) {\n            System.err.println(\"Warning: ALPN Boot enabled unintentionally\")\n          }\n        } else if (platformSystemProperty == OPENJSSE_PROPERTY && Security.getProviders()[0].name != \"OpenJSSE\") {\n          if (!OpenJSSEPlatform.isSupported) {\n            System.err.println(\"Warning: OpenJSSE not available\")\n          }\n\n          if (System.getProperty(\"javax.net.debug\") == null) {\n            System.setProperty(\"javax.net.debug\", \"\")\n          }\n\n          Security.insertProviderAt(OpenJSSE(), 1)\n        } else if (platformSystemProperty == BOUNCYCASTLE_PROPERTY && Security.getProviders()[0].name != \"BC\") {\n          Security.insertProviderAt(BouncyCastleProvider(), 1)\n          Security.insertProviderAt(BouncyCastleJsseProvider(), 2)\n        } else if (platformSystemProperty == CORRETTO_PROPERTY) {\n          AmazonCorrettoCryptoProvider.install()\n\n          AmazonCorrettoCryptoProvider.INSTANCE.assertHealthy()\n        }\n\n        Platform.resetForTests()\n\n        System.err.println(\"Running Tests with ${Platform.get().javaClass.simpleName}\")\n      }\n\n      @JvmStatic\n      fun getPlatformSystemProperty(): String? {\n        var property: String? = System.getProperty(PROPERTY_NAME)\n\n        if (PlatformRegistry.isAndroid) {\n          // Platforms below are unavailable on Android\n          return null\n        }\n\n        if (property == null) {\n          property =\n            when (Platform.get()) {\n              is ConscryptPlatform -> {\n                CONSCRYPT_PROPERTY\n              }\n\n              is OpenJSSEPlatform -> {\n                OPENJSSE_PROPERTY\n              }\n\n              is Jdk8WithJettyBootPlatform -> {\n                CONSCRYPT_PROPERTY\n              }\n\n              is Jdk9Platform -> {\n                if (isCorrettoInstalled) CORRETTO_PROPERTY else JDK9_PROPERTY\n              }\n\n              else -> {\n                JDK8_PROPERTY\n              }\n            }\n        }\n\n        return property\n      }\n\n      @JvmStatic\n      fun conscrypt() = PlatformRule(CONSCRYPT_PROPERTY)\n\n      @JvmStatic\n      fun openjsse() = PlatformRule(OPENJSSE_PROPERTY)\n\n      @JvmStatic\n      fun jdk9() = PlatformRule(JDK9_PROPERTY)\n\n      @JvmStatic\n      fun jdk8() = PlatformRule(JDK8_PROPERTY)\n\n      @JvmStatic\n      fun jdk8alpn() = PlatformRule(JDK8_ALPN_PROPERTY)\n\n      @JvmStatic\n      fun bouncycastle() = PlatformRule(BOUNCYCASTLE_PROPERTY)\n\n      @JvmStatic\n      fun isAlpnBootEnabled(): Boolean =\n        try {\n          Class.forName(\"org.eclipse.jetty.alpn.ALPN\", true, null)\n          true\n        } catch (cnfe: ClassNotFoundException) {\n          false\n        }\n\n      val isCorrettoSupported: Boolean =\n        try {\n          // Trigger an early exception over a fatal error, prefer a RuntimeException over Error.\n          Class.forName(\"com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider\")\n\n          AmazonCorrettoCryptoProvider.INSTANCE.loadingError == null &&\n            AmazonCorrettoCryptoProvider.INSTANCE.runSelfTests() == SelfTestStatus.PASSED\n        } catch (e: ClassNotFoundException) {\n          false\n        }\n\n      val isCorrettoInstalled: Boolean =\n        isCorrettoSupported &&\n          Security\n            .getProviders()\n            .first()\n            .name == AmazonCorrettoCryptoProvider.PROVIDER_NAME\n    }\n  }\n"
  },
  {
    "path": "okhttp-testing-support/src/main/kotlin/okhttp3/testing/PlatformVersion.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.testing\n\nobject PlatformVersion {\n  val majorVersion: Int by lazy {\n    when (val jvmSpecVersion = getJvmSpecVersion()) {\n      \"1.8\" -> 8\n      else -> jvmSpecVersion.toInt()\n    }\n  }\n\n  fun getJvmSpecVersion(): String = System.getProperty(\"java.specification.version\", \"unknown\")\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/main/resources/META-INF/proguard/okhttp3.pro",
    "content": "# OkHttp test platform rules uses optional classes.\n-dontwarn **\n"
  },
  {
    "path": "okhttp-testing-support/src/test/kotlin/okhttp3/OkHttpClientTestRuleTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport assertk.assertThat\nimport assertk.assertions.hasMessage\nimport kotlin.test.assertFailsWith\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.BeforeEachCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass OkHttpClientTestRuleTest {\n  lateinit var extensionContext: ExtensionContext\n\n  @RegisterExtension @JvmField\n  val beforeEachCallback =\n    BeforeEachCallback { context ->\n      this@OkHttpClientTestRuleTest.extensionContext = context\n    }\n\n  @Test fun uncaughtException() {\n    val testRule = OkHttpClientTestRule()\n    testRule.beforeEach(extensionContext)\n\n    val thread =\n      object : Thread() {\n        override fun run(): Unit = throw RuntimeException(\"boom!\")\n      }\n    thread.start()\n    thread.join()\n\n    assertFailsWith<AssertionError> {\n      testRule.afterEach(extensionContext)\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"uncaught exception thrown during test\")\n      assertThat(expected.cause!!).hasMessage(\"boom!\")\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-testing-support/src/test/kotlin/okhttp3/testing/PlatformRuleTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.testing\n\nimport okhttp3.internal.platform.Platform\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n/**\n * Validates which environment is used by the IDE.\n */\nclass PlatformRuleTest {\n  @RegisterExtension @JvmField\n  val platform = PlatformRule()\n\n  @Test\n  fun testMode() {\n    println(PlatformRule.getPlatformSystemProperty())\n    println(Platform.get().javaClass.simpleName)\n  }\n\n  @Test\n  fun testGreenCase() {\n  }\n\n  @Test\n  fun testGreenCaseFailingOnLater() {\n    platform.expectFailureFromJdkVersion(PlatformVersion.majorVersion + 1)\n  }\n\n  @Test\n  fun failureCase() {\n    platform.expectFailureFromJdkVersion(PlatformVersion.majorVersion)\n\n    check(false)\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/Module.md",
    "content": "# Module okhttp-tls\n\nOkHttp Transport Layer Security (TLS) library.\n"
  },
  {
    "path": "okhttp-tls/README.md",
    "content": "OkHttp TLS\n==========\n\nApproachable APIs for using TLS.\n\nA [`HeldCertificate`][held_certificate] is a certificate and its private key. Use the\n[builder][held_certificate_builder] to create a self-signed certificate that a test server can use\nfor HTTPS:\n\n```java\nHeldCertificate localhostCertificate = new HeldCertificate.Builder()\n    .addSubjectAlternativeName(\"localhost\")\n    .build();\n```\n\n[`HandshakeCertificates`][handshake_certificates] keeps the certificates for a TLS handshake.\nUse its [builder][handshake_certificates_builder] to define which certificates the HTTPS server\nreturns to its clients. The returned instance can create an `SSLSocketFactory` that implements this\npolicy:\n\n```java\nHandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()\n    .heldCertificate(localhostCertificate)\n    .build();\nMockWebServer server = new MockWebServer();\nserver.useHttps(serverCertificates.sslSocketFactory(), false);\n```\n\n`HandshakeCertificates` also works for clients where its job is to define which root certificates\nto trust. In this simplified example we trust the server's self-signed certificate:\n\n```java\nHandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()\n    .addTrustedCertificate(localhostCertificate.certificate())\n    .build();\nOkHttpClient client = new OkHttpClient.Builder()\n    .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())\n    .build();\n```\n\nWith a server that holds a certificate and a client that trusts it we have enough for an HTTPS\nhandshake. The best part of this example is that we don't need to make our test code insecure with a\na fake `HostnameVerifier` or `X509TrustManager`.\n\nCertificate Authorities\n-----------------------\n\nThe above example uses a self-signed certificate. This is convenient for testing but not\nrepresentative of real-world HTTPS deployment. To get closer to that we can use `HeldCertificate`\nto generate a trusted root certificate, an intermediate certificate, and a server certificate.\nWe use `certificateAuthority(int)` to create certificates that can sign other certificates. The\nint specifies how many intermediate certificates are allowed beneath it in the chain.\n\n```java\nHeldCertificate rootCertificate = new HeldCertificate.Builder()\n    .certificateAuthority(1)\n    .build();\n\nHeldCertificate intermediateCertificate = new HeldCertificate.Builder()\n    .certificateAuthority(0)\n    .signedBy(rootCertificate)\n    .build();\n\nHeldCertificate serverCertificate = new HeldCertificate.Builder()\n    .addSubjectAlternativeName(\"localhost\")\n    .signedBy(intermediateCertificate)\n    .build();\n```\n\nTo serve this configuration the server needs to provide its clients with a chain of certificates\nstarting with its own and including everything up-to but not including the root. We don't need to\ninclude root certificates because the client already has them.\n\n```java\nHandshakeCertificates serverHandshakeCertificates = new HandshakeCertificates.Builder()\n    .heldCertificate(serverCertificate, intermediateCertificate.certificate())\n    .build();\n```\n\nThe client only needs to know the trusted root certificate. It checks the server's certificate by\nvalidating the signatures within the chain.\n\n```java\nHandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()\n    .addTrustedCertificate(rootCertificate.certificate())\n    .build();\nOkHttpClient client = new OkHttpClient.Builder()\n    .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())\n    .build();\n```\n\nClient Authentication\n---------------------\n\nThe above scenario is representative of most TLS set ups: the client uses certificates to validate\nthe identity of a server. The converse is also possible. Here we create a server that authenticates\na client and a client that authenticates a server.\n\n```java\n// Create the root for client and server to trust. We could also use different roots for each!\nHeldCertificate rootCertificate = new HeldCertificate.Builder()\n    .certificateAuthority(0)\n    .build();\n\n// Create a server certificate and a server that uses it.\nHeldCertificate serverCertificate = new HeldCertificate.Builder()\n    .commonName(\"ingen\")\n    .addSubjectAlternativeName(server.getHostName())\n    .signedBy(rootCertificate)\n    .build();\nHandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()\n    .addTrustedCertificate(rootCertificate.certificate())\n    .heldCertificate(serverCertificate)\n    .build();\nMockWebServer server = new MockWebServer();\nserver.useHttps(serverCertificates.sslSocketFactory(), false);\nserver.requestClientAuth();\nserver.enqueue(new MockResponse());\n\n// Create a client certificate and a client that uses it.\nHeldCertificate clientCertificate = new HeldCertificate.Builder()\n    .commonName(\"ianmalcolm\")\n    .signedBy(rootCertificate)\n    .build();\nHandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()\n    .addTrustedCertificate(rootCertificate.certificate())\n    .heldCertificate(clientCertificate)\n    .build();\nOkHttpClient client = new OkHttpClient.Builder()\n    .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())\n    .build();\n\n// Connect 'em all together. Certificates are exchanged in the handshake.\nCall call = client.newCall(new Request.Builder()\n    .url(server.url(\"/\"))\n    .build());\nResponse response = call.execute();\nSystem.out.println(response.handshake().peerPrincipal());\nRecordedRequest recordedRequest = server.takeRequest();\nSystem.out.println(recordedRequest.getHandshake().peerPrincipal());\n```\n\nThis handshake is successful because each party has prearranged to trust the root certificate that\nsigns the other party's chain.\n\nWell-Known Certificate Authorities\n----------------------------------\n\nIn these examples we've prearranged which root certificates to trust. But for regular HTTPS on the\nInternet this set of trusted root certificates is usually provided by default by the host platform.\nSuch a set typically includes many root certificates from well-known certificate authorities like\nEntrust and Verisign.\n\nThis is the behavior you'll get with your OkHttpClient if you don't specifically configure\n`HandshakeCertificates`. Or you can do it explicitly with `addPlatformTrustedCertificates()`:\n\n```java\nHandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()\n    .addPlatformTrustedCertificates()\n    .build();\nOkHttpClient client = new OkHttpClient.Builder()\n    .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())\n    .build();\n```\n\nPEM files\n---------\n\nYou can encode a `HeldCertificate` in PEM format:\n\n```java\nHeldCertificate heldCertificate = ...\nSystem.out.println(heldCertificate.certificatePem())\n```\n\n```\n-----BEGIN CERTIFICATE-----\nMIIBSjCB8aADAgECAgEBMAoGCCqGSM49BAMCMC8xLTArBgNVBAMTJDJiYWY3NzVl\nLWE4MzUtNDM5ZS1hYWE2LTgzNmNiNDlmMGM3MTAeFw0xODA3MTMxMjA0MzJaFw0x\nODA3MTQxMjA0MzJaMC8xLTArBgNVBAMTJDJiYWY3NzVlLWE4MzUtNDM5ZS1hYWE2\nLTgzNmNiNDlmMGM3MTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDmlOiZ3dxA2\nzw1KwqGNsKVUZbkUVj5cxV1jDbSTvTlOjSj6LR0Ovys9RFdrjcbbMLWvSvMQgHch\nk8Q50c6Kb34wCgYIKoZIzj0EAwIDSAAwRQIhAJkXiCbIR3zxuH5SQR5PEAPJ+ntg\nmsOSMaAKwAePESf+AiBlxbEu6YpHt1ZJoAhMAv6raYnwSp/A94eJGlJynQ0igQ==\n-----END CERTIFICATE-----\n```\n\nYou can also do so with the private key. Be careful with these!\n\n```java\nHeldCertificate heldCertificate = ...\nSystem.out.println(heldCertificate.privateKeyPkcs8Pem())\n```\n\n```\n-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgQbYDQiewSnregm9e\nIjXEHQgc6w3ELHdnH1houEUom9CgCgYIKoZIzj0DAQehRANCAAQ5pTomd3cQNs8N\nSsKhjbClVGW5FFY+XMVdYw20k705To0o+i0dDr8rPURXa43G2zC1r0rzEIB3IZPE\nOdHOim9+\n-----END PRIVATE KEY-----\n```\n\nRecommendations\n---------------\n\nTypically servers need a held certificate plus a chain of intermediates. Servers only need the\nprivate key for their own certificate. The chain served by a server doesn't need the root\ncertificate.\n\nThe trusted roots don't need to be the same for client and server when using client authentication.\nClients might rely on the platform certificates and servers might use a private\norganization-specific certificate authority.\n\nBy default `HeldCertificate` instances expire after 24 hours. Use `duration()` to adjust.\n\nBy default server certificates need to identify which hostnames they're trusted for. You may add as\nmany as necessary with `addSubjectAlternativeName()`. This mechanism also supports a very limited\nform of wildcards `*.example.com` where the `*` must be first and doesn't match nested subdomains.\n\nBy default certificates use fast and secure 256-bit ECDSA keys. For interoperability with very old\nclients use `HeldCertificate.Builder.rsa2048()`.\n\nDownload\n--------\n\n```kotlin\nimplementation(\"com.squareup.okhttp3:okhttp-tls:5.3.0\")\n```\n\n [held_certificate]: https://square.github.io/okhttp/5.x/okhttp-tls/okhttp3.tls/-held-certificate/\n [held_certificate_builder]: https://square.github.io/okhttp/5.x/okhttp-tls/okhttp3.tls/-held-certificate/-builder/\n [handshake_certificates]: https://square.github.io/okhttp/5.x/okhttp-tls/okhttp3.tls/-handshake-certificates/\n [handshake_certificates_builder]: https://square.github.io/okhttp/5.x/okhttp-tls/okhttp3.tls/-handshake-certificates/-builder/\n"
  },
  {
    "path": "okhttp-tls/api/okhttp-tls.api",
    "content": "public final class okhttp3/tls/Certificates {\n\tpublic static final fun certificatePem (Ljava/security/cert/X509Certificate;)Ljava/lang/String;\n\tpublic static final fun decodeCertificatePem (Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n}\n\npublic final class okhttp3/tls/HandshakeCertificates {\n\tpublic final fun -deprecated_keyManager ()Ljavax/net/ssl/X509KeyManager;\n\tpublic final fun -deprecated_trustManager ()Ljavax/net/ssl/X509TrustManager;\n\tpublic synthetic fun <init> (Ljavax/net/ssl/X509KeyManager;Ljavax/net/ssl/X509TrustManager;Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\tpublic final fun keyManager ()Ljavax/net/ssl/X509KeyManager;\n\tpublic final fun sslContext ()Ljavax/net/ssl/SSLContext;\n\tpublic final fun sslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;\n\tpublic final fun trustManager ()Ljavax/net/ssl/X509TrustManager;\n}\n\npublic final class okhttp3/tls/HandshakeCertificates$Builder {\n\tpublic fun <init> ()V\n\tpublic final fun addInsecureHost (Ljava/lang/String;)Lokhttp3/tls/HandshakeCertificates$Builder;\n\tpublic final fun addPlatformTrustedCertificates ()Lokhttp3/tls/HandshakeCertificates$Builder;\n\tpublic final fun addTrustedCertificate (Ljava/security/cert/X509Certificate;)Lokhttp3/tls/HandshakeCertificates$Builder;\n\tpublic final fun build ()Lokhttp3/tls/HandshakeCertificates;\n\tpublic final fun heldCertificate (Lokhttp3/tls/HeldCertificate;[Ljava/security/cert/X509Certificate;)Lokhttp3/tls/HandshakeCertificates$Builder;\n}\n\npublic final class okhttp3/tls/HeldCertificate {\n\tpublic static final field Companion Lokhttp3/tls/HeldCertificate$Companion;\n\tpublic final fun -deprecated_certificate ()Ljava/security/cert/X509Certificate;\n\tpublic final fun -deprecated_keyPair ()Ljava/security/KeyPair;\n\tpublic fun <init> (Ljava/security/KeyPair;Ljava/security/cert/X509Certificate;)V\n\tpublic final fun certificate ()Ljava/security/cert/X509Certificate;\n\tpublic final fun certificatePem ()Ljava/lang/String;\n\tpublic static final fun decode (Ljava/lang/String;)Lokhttp3/tls/HeldCertificate;\n\tpublic final fun keyPair ()Ljava/security/KeyPair;\n\tpublic final fun privateKeyPkcs1Pem ()Ljava/lang/String;\n\tpublic final fun privateKeyPkcs8Pem ()Ljava/lang/String;\n}\n\npublic final class okhttp3/tls/HeldCertificate$Builder {\n\tpublic static final field Companion Lokhttp3/tls/HeldCertificate$Builder$Companion;\n\tpublic fun <init> ()V\n\tpublic final fun addSubjectAlternativeName (Ljava/lang/String;)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun build ()Lokhttp3/tls/HeldCertificate;\n\tpublic final fun certificateAuthority (I)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun commonName (Ljava/lang/String;)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun duration (JLjava/util/concurrent/TimeUnit;)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun ecdsa256 ()Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun keyPair (Ljava/security/KeyPair;)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun keyPair (Ljava/security/PublicKey;Ljava/security/PrivateKey;)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun organizationalUnit (Ljava/lang/String;)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun rsa2048 ()Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun serialNumber (J)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun serialNumber (Ljava/math/BigInteger;)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun signedBy (Lokhttp3/tls/HeldCertificate;)Lokhttp3/tls/HeldCertificate$Builder;\n\tpublic final fun validityInterval (JJ)Lokhttp3/tls/HeldCertificate$Builder;\n}\n\npublic final class okhttp3/tls/HeldCertificate$Builder$Companion {\n}\n\npublic final class okhttp3/tls/HeldCertificate$Companion {\n\tpublic final fun decode (Ljava/lang/String;)Lokhttp3/tls/HeldCertificate;\n}\n\n"
  },
  {
    "path": "okhttp-tls/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Export-Package: okhttp3.tls\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.tls\",\n)\n\nproject.applyJavaModules(\"okhttp3.tls\")\n\ndependencies {\n  api(libs.square.okio)\n  \"friendsImplementation\"(projects.okhttp)\n  compileOnly(libs.animalsniffer.annotations)\n\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(projects.mockwebserver3Junit5)\n  testImplementation(libs.junit)\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n  testImplementation(libs.assertk)\n}\n\nanimalsniffer {\n  // InsecureExtendedTrustManager (API 24+)\n  ignore = listOf(\"javax.net.ssl.X509ExtendedTrustManager\")\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.tls {\n  requires okhttp3;\n  exports okhttp3.tls;\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/Certificates.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:JvmName(\"Certificates\")\n\npackage okhttp3.tls\n\nimport java.security.GeneralSecurityException\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.toByteString\n\n/**\n * Decodes a multiline string that contains a [certificate][certificatePem] which is\n * [PEM-encoded][rfc_7468]. A typical input string looks like this:\n *\n * ```\n * -----BEGIN CERTIFICATE-----\n * MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl\n * cmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAwMTAx\n * MDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2FzaC5h\n * cHAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASda8ChkQXxGELnrV/oBnIAx3dD\n * ocUOJfdz4pOJTP6dVQB9U3UBiW5uSX/MoOD0LL5zG3bVyL3Y6pDwKuYvfLNhoyAw\n * HjAcBgNVHREBAf8EEjAQhwQBAQEBgghjYXNoLmFwcDAKBggqhkjOPQQDAgNIADBF\n * AiAyHHg1N6YDDQiY920+cnI5XSZwEGhAtb9PYWO8bLmkcQIhAI2CfEZf3V/obmdT\n * yyaoEufLKVXhrTQhRfodTeigi4RX\n * -----END CERTIFICATE-----\n * ```\n */\nfun String.decodeCertificatePem(): X509Certificate {\n  try {\n    val certificateFactory = CertificateFactory.getInstance(\"X.509\")\n    val certificates =\n      certificateFactory\n        .generateCertificates(\n          Buffer().writeUtf8(this).inputStream(),\n        )\n\n    return certificates.single() as X509Certificate\n  } catch (nsee: NoSuchElementException) {\n    throw IllegalArgumentException(\"failed to decode certificate\", nsee)\n  } catch (iae: IllegalArgumentException) {\n    throw IllegalArgumentException(\"failed to decode certificate\", iae)\n  } catch (e: GeneralSecurityException) {\n    throw IllegalArgumentException(\"failed to decode certificate\", e)\n  }\n}\n\n/**\n * Returns the certificate encoded in [PEM format][rfc_7468].\n *\n * [rfc_7468]: https://tools.ietf.org/html/rfc7468\n */\nfun X509Certificate.certificatePem(): String =\n  buildString {\n    append(\"-----BEGIN CERTIFICATE-----\\n\")\n    encodeBase64Lines(encoded.toByteString())\n    append(\"-----END CERTIFICATE-----\\n\")\n  }\n\ninternal fun StringBuilder.encodeBase64Lines(data: ByteString) {\n  val base64 = data.base64()\n  for (i in 0 until base64.length step 64) {\n    append(base64, i, minOf(i + 64, base64.length)).append('\\n')\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/HandshakeCertificates.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.tls\n\nimport java.security.KeyStoreException\nimport java.security.SecureRandom\nimport java.security.cert.X509Certificate\nimport java.util.Collections\nimport javax.net.ssl.HostnameVerifier\nimport javax.net.ssl.KeyManager\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.SSLSocketFactory\nimport javax.net.ssl.TrustManager\nimport javax.net.ssl.X509KeyManager\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.CertificatePinner\nimport okhttp3.internal.platform.Platform\nimport okhttp3.internal.toImmutableList\nimport okhttp3.tls.internal.TlsUtil.newKeyManager\nimport okhttp3.tls.internal.TlsUtil.newTrustManager\n\n/**\n * Certificates to identify which peers to trust and also to earn the trust of those peers in kind.\n * Client and server exchange these certificates during the handshake phase of a TLS connection.\n *\n * ### Server Authentication\n *\n * This is the most common form of TLS authentication: clients verify that servers are trusted and\n * that they own the hostnames that they represent. Server authentication is required.\n *\n * To perform server authentication:\n *\n *  * The server's handshake certificates must have a [held certificate][HeldCertificate] (a\n *    certificate and its private key). The certificate's subject alternative names must match the\n *    server's hostname. The server must also have is a (possibly-empty) chain of intermediate\n *    certificates to establish trust from a root certificate to the server's certificate. The root\n *    certificate is not included in this chain.\n *  * The client's handshake certificates must include a set of trusted root certificates. They will\n *    be used to authenticate the server's certificate chain. Typically this is a set of well-known\n *    root certificates that is distributed with the HTTP client or its platform. It may be\n *    augmented by certificates private to an organization or service.\n *\n * ### Client Authentication\n *\n * This is authentication of the client by the server during the TLS handshake. Client\n * authentication is optional.\n *\n * To perform client authentication:\n *\n *  * The client's handshake certificates must have a [held certificate][HeldCertificate] (a\n *    certificate and its private key). The client must also have a (possibly-empty) chain of\n *    intermediate certificates to establish trust from a root certificate to the client's\n *    certificate. The root certificate is not included in this chain.\n *  * The server's handshake certificates must include a set of trusted root certificates. They\n *    will be used to authenticate the client's certificate chain. Typically this is not the same\n *    set of root certificates used in server authentication. Instead it will be a small set of\n *    roots private to an organization or service.\n */\nclass HandshakeCertificates private constructor(\n  @get:JvmName(\"keyManager\") val keyManager: X509KeyManager,\n  @get:JvmName(\"trustManager\") val trustManager: X509TrustManager,\n) {\n  @JvmName(\"-deprecated_keyManager\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"keyManager\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun keyManager(): X509KeyManager = keyManager\n\n  @JvmName(\"-deprecated_trustManager\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"trustManager\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun trustManager(): X509TrustManager = trustManager\n\n  fun sslSocketFactory(): SSLSocketFactory = sslContext().socketFactory\n\n  fun sslContext(): SSLContext =\n    Platform.get().newSSLContext().apply {\n      init(arrayOf<KeyManager>(keyManager), arrayOf<TrustManager>(trustManager), SecureRandom())\n    }\n\n  class Builder {\n    private var heldCertificate: HeldCertificate? = null\n    private var intermediates: Array<X509Certificate>? = null\n    private val trustedCertificates = mutableListOf<X509Certificate>()\n    private val insecureHosts = mutableListOf<String>()\n\n    /**\n     * Configure the certificate chain to use when being authenticated. The first certificate is\n     * the held certificate, further certificates are included in the handshake so the peer can\n     * build a trusted path to a trusted root certificate.\n     *\n     * The chain should include all intermediate certificates but does not need the root certificate\n     * that we expect to be known by the remote peer. The peer already has that certificate so\n     * transmitting it is unnecessary.\n     */\n    fun heldCertificate(\n      heldCertificate: HeldCertificate,\n      vararg intermediates: X509Certificate,\n    ) = apply {\n      this.heldCertificate = heldCertificate\n      this.intermediates = arrayOf(*intermediates) // Defensive copy.\n    }\n\n    /**\n     * Add a trusted root certificate to use when authenticating a peer. Peers must provide\n     * a chain of certificates whose root is one of these.\n     */\n    fun addTrustedCertificate(certificate: X509Certificate) =\n      apply {\n        this.trustedCertificates += certificate\n      }\n\n    /**\n     * Add all of the host platform's trusted root certificates. This set varies by platform\n     * (Android vs. Java), by platform release (Android 4.4 vs. Android 9), and with user\n     * customizations.\n     *\n     * Most TLS clients that connect to hosts on the public Internet should call this method.\n     * Otherwise it is necessary to manually prepare a comprehensive set of trusted roots.\n     *\n     * If the host platform is compromised or misconfigured this may contain untrustworthy root\n     * certificates. Applications that connect to a known set of servers may be able to mitigate\n     * this problem with [certificate pinning][CertificatePinner].\n     */\n    fun addPlatformTrustedCertificates() =\n      apply {\n        val platformTrustManager = Platform.get().platformTrustManager()\n        Collections.addAll(trustedCertificates, *platformTrustManager.acceptedIssuers)\n      }\n\n    /**\n     * Configures this to not authenticate the HTTPS server on to [hostname]. This makes the user\n     * vulnerable to man-in-the-middle attacks and should only be used only in private development\n     * environments and only to carry test data.\n     *\n     * The server’s TLS certificate **does not need to be signed** by a trusted certificate\n     * authority. Instead, it will trust any well-formed certificate, even if it is self-signed.\n     * This is necessary for testing against localhost or in development environments where a\n     * certificate authority is not possible.\n     *\n     * The server’s TLS certificate still must match the requested hostname. For example, if the\n     * certificate is issued to `example.com` and the request is to `localhost`, the connection will\n     * fail. Use a custom [HostnameVerifier] to ignore such problems.\n     *\n     * Other TLS features are still used but provide no security benefits in absence of the above\n     * gaps. For example, an insecure TLS connection is capable of negotiating HTTP/2 with ALPN and\n     * it also has a regular-looking handshake.\n     *\n     * **This feature is not supported on Android API levels less than 24.** Prior releases lacked\n     * a mechanism to trust some hosts and not others.\n     *\n     * @param hostname the exact hostname from the URL for insecure connections.\n     */\n    fun addInsecureHost(hostname: String) =\n      apply {\n        insecureHosts += hostname\n      }\n\n    fun build(): HandshakeCertificates {\n      val immutableInsecureHosts = insecureHosts.toImmutableList()\n\n      val heldCertificate = heldCertificate\n      if (heldCertificate != null && heldCertificate.keyPair.private.format == null) {\n        throw KeyStoreException(\"unable to support unencodable private key\")\n      }\n\n      val keyManager = newKeyManager(null, heldCertificate, *(intermediates ?: emptyArray()))\n      val trustManager = newTrustManager(null, trustedCertificates, immutableInsecureHosts)\n      return HandshakeCertificates(keyManager, trustManager)\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/HeldCertificate.kt",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls\n\nimport java.math.BigInteger\nimport java.net.InetAddress\nimport java.security.GeneralSecurityException\nimport java.security.KeyFactory\nimport java.security.KeyPair\nimport java.security.KeyPairGenerator\nimport java.security.PrivateKey\nimport java.security.PublicKey\nimport java.security.SecureRandom\nimport java.security.Signature\nimport java.security.cert.X509Certificate\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\nimport java.security.spec.PKCS8EncodedKeySpec\nimport java.util.UUID\nimport java.util.concurrent.TimeUnit\nimport okhttp3.internal.canParseAsIpAddress\nimport okhttp3.tls.internal.der.AlgorithmIdentifier\nimport okhttp3.tls.internal.der.AttributeTypeAndValue\nimport okhttp3.tls.internal.der.BasicConstraints\nimport okhttp3.tls.internal.der.BitString\nimport okhttp3.tls.internal.der.Certificate\nimport okhttp3.tls.internal.der.CertificateAdapters\nimport okhttp3.tls.internal.der.CertificateAdapters.generalNameDnsName\nimport okhttp3.tls.internal.der.CertificateAdapters.generalNameIpAddress\nimport okhttp3.tls.internal.der.Extension\nimport okhttp3.tls.internal.der.ObjectIdentifiers\nimport okhttp3.tls.internal.der.ObjectIdentifiers.BASIC_CONSTRAINTS\nimport okhttp3.tls.internal.der.ObjectIdentifiers.ORGANIZATIONAL_UNIT_NAME\nimport okhttp3.tls.internal.der.ObjectIdentifiers.SHA256_WITH_ECDSA\nimport okhttp3.tls.internal.der.ObjectIdentifiers.SHA256_WITH_RSA_ENCRYPTION\nimport okhttp3.tls.internal.der.ObjectIdentifiers.SUBJECT_ALTERNATIVE_NAME\nimport okhttp3.tls.internal.der.TbsCertificate\nimport okhttp3.tls.internal.der.Validity\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeBase64\nimport okio.ByteString.Companion.toByteString\n\n/**\n * A certificate and its private key. These are some properties of certificates that are used with\n * TLS:\n *\n *  * **A common name.** This is a string identifier for the certificate. It usually describes the\n *    purpose of the certificate like \"Entrust Root Certification Authority - G2\" or\n *    \"www.squareup.com\".\n *\n *  * **A set of hostnames.** These are in the certificate's subject alternative name (SAN)\n *    extension. A subject alternative name is either a literal hostname (`squareup.com`), a literal\n *    IP address (`74.122.190.80`), or a hostname pattern (`*.api.squareup.com`).\n *\n *  * **A validity interval.** A certificate should not be used before its validity interval starts\n *    or after it ends.\n *\n *  * **A public key.** This cryptographic key is used for asymmetric encryption digital signatures.\n *    Note that the private key is not a part of the certificate!\n *\n *  * **A signature issued by another certificate's private key.** This mechanism allows a trusted\n *    third-party to endorse a certificate. Third parties should only endorse certificates once\n *    they've confirmed that the owner of the private key is also the owner of the certificate's\n *    other properties.\n *\n * Certificates are signed by other certificates and a sequence of them is called a certificate\n * chain. The chain terminates in a self-signed \"root\" certificate. Signing certificates in the\n * middle of the chain are called \"intermediates\". Organizations that offer certificate signing are\n * called certificate authorities (CAs).\n *\n * Browsers and other HTTP clients need a set of trusted root certificates to authenticate their\n * peers. Sets of root certificates are managed by either the HTTP client (like Firefox), or the\n * host platform (like Android). In July 2018 Android had 134 trusted root certificates for its HTTP\n * clients to trust.\n *\n * For example, in order to establish a secure connection to `https://www.squareup.com/`,\n * these three certificates are used.\n *\n * ```\n * www.squareup.com certificate:\n *\n * Common Name: www.squareup.com\n * Subject Alternative Names: www.squareup.com, squareup.com, account.squareup.com...\n * Validity: 2018-07-03T20:18:17Z – 2019-08-01T20:48:15Z\n * Public Key: d107beecc17325f55da976bcbab207ba4df68bd3f8fce7c3b5850311128264fd53e1baa342f58d93...\n * Signature: 1fb0e66fac05322721fe3a3917f7c98dee1729af39c99eab415f22d8347b508acdf0bab91781c3720...\n *\n * signed by intermediate certificate:\n *\n * Common Name: Entrust Certification Authority - L1M\n * Subject Alternative Names: none\n * Validity: 2014-12-15T15:25:03Z – 2030-10-15T15:55:03Z\n * Public Key: d081c13923c2b1d1ecf757dd55243691202248f7fcca520ab0ab3f33b5b08407f6df4e7ab0fb9822...\n * Signature: b487c784221a29c0a478ecf54f1bb484976f77eed4cf59afa843962f1d58dea6f3155b2ed9439c4c4...\n *\n * signed by root certificate:\n *\n * Common Name: Entrust Root Certification Authority - G2\n * Subject Alternative Names: none\n * Validity: 2009-07-07T17:25:54Z – 2030-12-07T17:55:54Z\n * Public Key: ba84b672db9e0c6be299e93001a776ea32b895411ac9da614e5872cffef68279bf7361060aa527d8...\n * Self-signed Signature: 799f1d96c6b6793f228d87d3870304606a6b9a2e59897311ac43d1f513ff8d392bc0f...\n * ```\n *\n * In this example the HTTP client already knows and trusts the last certificate, \"Entrust Root\n * Certification Authority - G2\". That certificate is used to verify the signature of the\n * intermediate certificate, \"Entrust Certification Authority - L1M\". The intermediate certificate\n * is used to verify the signature of the \"www.squareup.com\" certificate.\n *\n * This roles are reversed for client authentication. In that case the client has a private key and\n * a chain of certificates. The server uses a set of trusted root certificates to authenticate the\n * client. Subject alternative names are not used for client authentication.\n */\n@Suppress(\"DEPRECATION\")\nclass HeldCertificate(\n  @get:JvmName(\"keyPair\") val keyPair: KeyPair,\n  @get:JvmName(\"certificate\") val certificate: X509Certificate,\n) {\n  @JvmName(\"-deprecated_certificate\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"certificate\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun certificate(): X509Certificate = certificate\n\n  @JvmName(\"-deprecated_keyPair\")\n  @Deprecated(\n    message = \"moved to val\",\n    replaceWith = ReplaceWith(expression = \"keyPair\"),\n    level = DeprecationLevel.ERROR,\n  )\n  fun keyPair(): KeyPair = keyPair\n\n  /**\n   * Returns the certificate encoded in [PEM format][rfc_7468].\n   *\n   * [rfc_7468]: https://tools.ietf.org/html/rfc7468\n   */\n  fun certificatePem(): String = certificate.certificatePem()\n\n  /**\n   * Returns the private key encoded in [PKCS #8][rfc_5208] [PEM format][rfc_7468].\n   *\n   * [rfc_5208]: https://tools.ietf.org/html/rfc5208\n   * [rfc_7468]: https://tools.ietf.org/html/rfc7468\n   */\n  fun privateKeyPkcs8Pem(): String =\n    buildString {\n      append(\"-----BEGIN PRIVATE KEY-----\\n\")\n      encodeBase64Lines(keyPair.private.encoded.toByteString())\n      append(\"-----END PRIVATE KEY-----\\n\")\n    }\n\n  /**\n   * Returns the RSA private key encoded in [PKCS #1][rfc_8017] [PEM format][rfc_7468].\n   *\n   * [rfc_8017]: https://tools.ietf.org/html/rfc8017\n   * [rfc_7468]: https://tools.ietf.org/html/rfc7468\n   */\n  fun privateKeyPkcs1Pem(): String {\n    check(keyPair.private is RSAPrivateKey) { \"PKCS1 only supports RSA keys\" }\n    return buildString {\n      append(\"-----BEGIN RSA PRIVATE KEY-----\\n\")\n      encodeBase64Lines(pkcs1Bytes())\n      append(\"-----END RSA PRIVATE KEY-----\\n\")\n    }\n  }\n\n  private fun pkcs1Bytes(): ByteString {\n    val decoded = CertificateAdapters.privateKeyInfo.fromDer(keyPair.private.encoded.toByteString())\n    return decoded.privateKey\n  }\n\n  /** Build a held certificate with reasonable defaults. */\n  class Builder {\n    private var notBefore = -1L\n    private var notAfter = -1L\n    private var commonName: String? = null\n    private var organizationalUnit: String? = null\n    private val altNames = mutableListOf<String>()\n    private var serialNumber: BigInteger? = null\n    private var keyPair: KeyPair? = null\n    private var signedBy: HeldCertificate? = null\n    private var maxIntermediateCas = -1\n    private var keyAlgorithm: String? = null\n    private var keySize: Int = 0\n\n    init {\n      ecdsa256()\n    }\n\n    /**\n     * Sets the certificate to be valid in ```[notBefore..notAfter]```. Both endpoints are specified\n     * in the format of [System.currentTimeMillis]. Specify -1L for both values to use the default\n     * interval, 24 hours starting when the certificate is created.\n     */\n    fun validityInterval(\n      notBefore: Long,\n      notAfter: Long,\n    ) = apply {\n      require(notBefore <= notAfter && notBefore == -1L == (notAfter == -1L)) {\n        \"invalid interval: $notBefore..$notAfter\"\n      }\n      this.notBefore = notBefore\n      this.notAfter = notAfter\n    }\n\n    /**\n     * Sets the certificate to be valid immediately and until the specified duration has elapsed.\n     * The precision of this field is seconds; further precision will be truncated.\n     */\n    fun duration(\n      duration: Long,\n      unit: TimeUnit,\n    ) = apply {\n      val now = System.currentTimeMillis()\n      validityInterval(now, now + unit.toMillis(duration))\n    }\n\n    /**\n     * Adds a subject alternative name (SAN) to the certificate. This is usually a literal hostname,\n     * a literal IP address, or a hostname pattern. If no subject alternative names are added that\n     * extension will be omitted.\n     */\n    fun addSubjectAlternativeName(altName: String) =\n      apply {\n        altNames += altName\n      }\n\n    /**\n     * Set this certificate's common name (CN). Historically this held the hostname of TLS\n     * certificate, but that practice was deprecated by [RFC 2818][rfc_2818] and replaced with\n     * [addSubjectAlternativeName]. If unset a random string will be used.\n     *\n     * [rfc_2818]: https://tools.ietf.org/html/rfc2818\n     */\n    fun commonName(cn: String) =\n      apply {\n        this.commonName = cn\n      }\n\n    /** Sets the certificate's organizational unit (OU). If unset this field will be omitted. */\n    fun organizationalUnit(ou: String) =\n      apply {\n        this.organizationalUnit = ou\n      }\n\n    /** Sets this certificate's serial number. If unset the serial number will be 1. */\n    fun serialNumber(serialNumber: BigInteger) =\n      apply {\n        this.serialNumber = serialNumber\n      }\n\n    /** Sets this certificate's serial number. If unset the serial number will be 1. */\n    fun serialNumber(serialNumber: Long) =\n      apply {\n        serialNumber(BigInteger.valueOf(serialNumber))\n      }\n\n    /**\n     * Sets the public/private key pair used for this certificate. If unset a key pair will be\n     * generated.\n     */\n    fun keyPair(keyPair: KeyPair) =\n      apply {\n        this.keyPair = keyPair\n      }\n\n    /**\n     * Sets the public/private key pair used for this certificate. If unset a key pair will be\n     * generated.\n     */\n    fun keyPair(\n      publicKey: PublicKey,\n      privateKey: PrivateKey,\n    ) = apply {\n      keyPair(KeyPair(publicKey, privateKey))\n    }\n\n    /**\n     * Set the certificate that will issue this certificate. If unset the certificate will be\n     * self-signed.\n     */\n    fun signedBy(signedBy: HeldCertificate?) =\n      apply {\n        this.signedBy = signedBy\n      }\n\n    /**\n     * Set this certificate to be a signing certificate, with up to `maxIntermediateCas`\n     * intermediate signing certificates beneath it.\n     *\n     * By default this certificate cannot not sign other certificates. Set this to 0 so this\n     * certificate can sign other certificates (but those certificates cannot themselves sign\n     * certificates). Set this to 1 so this certificate can sign intermediate certificates that can\n     * themselves sign certificates. Add one for each additional layer of intermediates to permit.\n     */\n    fun certificateAuthority(maxIntermediateCas: Int) =\n      apply {\n        require(maxIntermediateCas >= 0) {\n          \"maxIntermediateCas < 0: $maxIntermediateCas\"\n        }\n        this.maxIntermediateCas = maxIntermediateCas\n      }\n\n    /**\n     * Configure the certificate to generate a 256-bit ECDSA key, which provides about 128 bits of\n     * security. ECDSA keys are noticeably faster than RSA keys.\n     *\n     * This is the default configuration and has been since this API was introduced in OkHttp\n     * 3.11.0. Note that the default may change in future releases.\n     */\n    fun ecdsa256() =\n      apply {\n        keyAlgorithm = \"EC\"\n        keySize = 256\n      }\n\n    /**\n     * Configure the certificate to generate a 2048-bit RSA key, which provides about 112 bits of\n     * security. RSA keys are interoperable with very old clients that don't support ECDSA.\n     */\n    fun rsa2048() =\n      apply {\n        keyAlgorithm = \"RSA\"\n        keySize = 2048\n      }\n\n    fun build(): HeldCertificate {\n      // Subject keys & identity.\n      val subjectKeyPair = keyPair ?: generateKeyPair()\n      val subjectPublicKeyInfo =\n        CertificateAdapters.subjectPublicKeyInfo.fromDer(\n          subjectKeyPair.public.encoded.toByteString(),\n        )\n      val subject: List<List<AttributeTypeAndValue>> = subject()\n\n      // Issuer/signer keys & identity. May be the subject if it is self-signed.\n      val issuerKeyPair: KeyPair\n      val issuer: List<List<AttributeTypeAndValue>>\n      if (signedBy != null) {\n        issuerKeyPair = signedBy!!.keyPair\n        issuer =\n          CertificateAdapters.rdnSequence.fromDer(\n            signedBy!!\n              .certificate.subjectX500Principal.encoded\n              .toByteString(),\n          )\n      } else {\n        issuerKeyPair = subjectKeyPair\n        issuer = subject\n      }\n      val signatureAlgorithm = signatureAlgorithm(issuerKeyPair)\n\n      // Subset of certificate data that's covered by the signature.\n      val tbsCertificate =\n        TbsCertificate(\n          // v3:\n          version = 2L,\n          serialNumber = serialNumber ?: BigInteger.ONE,\n          signature = signatureAlgorithm,\n          issuer = issuer,\n          validity = validity(),\n          subject = subject,\n          subjectPublicKeyInfo = subjectPublicKeyInfo,\n          issuerUniqueID = null,\n          subjectUniqueID = null,\n          extensions = extensions(),\n        )\n\n      // Signature.\n      val signature =\n        Signature.getInstance(tbsCertificate.signatureAlgorithmName).run {\n          initSign(issuerKeyPair.private)\n          update(CertificateAdapters.tbsCertificate.toDer(tbsCertificate).toByteArray())\n          sign().toByteString()\n        }\n\n      // Complete signed certificate.\n      val certificate =\n        Certificate(\n          tbsCertificate = tbsCertificate,\n          signatureAlgorithm = signatureAlgorithm,\n          signatureValue =\n            BitString(\n              byteString = signature,\n              unusedBitsCount = 0,\n            ),\n        )\n\n      return HeldCertificate(subjectKeyPair, certificate.toX509Certificate())\n    }\n\n    private fun subject(): List<List<AttributeTypeAndValue>> {\n      val result = mutableListOf<List<AttributeTypeAndValue>>()\n\n      if (organizationalUnit != null) {\n        result +=\n          listOf(\n            AttributeTypeAndValue(\n              type = ORGANIZATIONAL_UNIT_NAME,\n              value = organizationalUnit,\n            ),\n          )\n      }\n\n      result +=\n        listOf(\n          AttributeTypeAndValue(\n            type = ObjectIdentifiers.COMMON_NAME,\n            value = commonName ?: UUID.randomUUID().toString(),\n          ),\n        )\n\n      return result\n    }\n\n    private fun validity(): Validity {\n      val notBefore = if (notBefore != -1L) notBefore else System.currentTimeMillis()\n      val notAfter = if (notAfter != -1L) notAfter else notBefore + DEFAULT_DURATION_MILLIS\n      return Validity(\n        notBefore = notBefore,\n        notAfter = notAfter,\n      )\n    }\n\n    private fun extensions(): MutableList<Extension> {\n      val result = mutableListOf<Extension>()\n\n      if (maxIntermediateCas != -1) {\n        result +=\n          Extension(\n            id = BASIC_CONSTRAINTS,\n            critical = true,\n            value =\n              BasicConstraints(\n                ca = true,\n                maxIntermediateCas = maxIntermediateCas.toLong(),\n              ),\n          )\n      }\n\n      if (altNames.isNotEmpty()) {\n        val extensionValue =\n          altNames.map {\n            when {\n              it.canParseAsIpAddress() -> {\n                generalNameIpAddress to InetAddress.getByName(it).address.toByteString()\n              }\n\n              else -> {\n                generalNameDnsName to it\n              }\n            }\n          }\n        result +=\n          Extension(\n            id = SUBJECT_ALTERNATIVE_NAME,\n            critical = true,\n            value = extensionValue,\n          )\n      }\n\n      return result\n    }\n\n    private fun signatureAlgorithm(signedByKeyPair: KeyPair): AlgorithmIdentifier =\n      when (signedByKeyPair.private) {\n        is RSAPrivateKey -> {\n          AlgorithmIdentifier(\n            algorithm = SHA256_WITH_RSA_ENCRYPTION,\n            parameters = null,\n          )\n        }\n\n        else -> {\n          AlgorithmIdentifier(\n            algorithm = SHA256_WITH_ECDSA,\n            parameters = ByteString.EMPTY,\n          )\n        }\n      }\n\n    private fun generateKeyPair(): KeyPair =\n      KeyPairGenerator.getInstance(keyAlgorithm).run {\n        initialize(keySize, SecureRandom())\n        generateKeyPair()\n      }\n\n    companion object {\n      private const val DEFAULT_DURATION_MILLIS = 1000L * 60 * 60 * 24 // 24 hours.\n    }\n  }\n\n  companion object {\n    private val PEM_REGEX = Regex(\"\"\"-----BEGIN ([!-,.-~ ]*)-----([^-]*)-----END \\1-----\"\"\")\n\n    /**\n     * Decodes a multiline string that contains both a [certificate][certificatePem] and a\n     * [private key][privateKeyPkcs8Pem], both [PEM-encoded][rfc_7468]. A typical input string looks\n     * like this:\n     *\n     * ```\n     * -----BEGIN CERTIFICATE-----\n     * MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl\n     * cmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAwMTAx\n     * MDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2FzaC5h\n     * cHAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASda8ChkQXxGELnrV/oBnIAx3dD\n     * ocUOJfdz4pOJTP6dVQB9U3UBiW5uSX/MoOD0LL5zG3bVyL3Y6pDwKuYvfLNhoyAw\n     * HjAcBgNVHREBAf8EEjAQhwQBAQEBgghjYXNoLmFwcDAKBggqhkjOPQQDAgNIADBF\n     * AiAyHHg1N6YDDQiY920+cnI5XSZwEGhAtb9PYWO8bLmkcQIhAI2CfEZf3V/obmdT\n     * yyaoEufLKVXhrTQhRfodTeigi4RX\n     * -----END CERTIFICATE-----\n     * -----BEGIN PRIVATE KEY-----\n     * MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCA7ODT0xhGSNn4ESj6J\n     * lu/GJQZoU9lDrCPeUcQ28tzOWw==\n     * -----END PRIVATE KEY-----\n     * ```\n     *\n     * The string should contain exactly one certificate and one private key in [PKCS #8][rfc_5208]\n     * format. It should not contain any other PEM-encoded blocks, but it may contain other text\n     * which will be ignored.\n     *\n     * Encode a held certificate into this format by concatenating the results of\n     * [certificatePem()][certificatePem] and [privateKeyPkcs8Pem()][privateKeyPkcs8Pem].\n     *\n     * [rfc_7468]: https://tools.ietf.org/html/rfc7468\n     * [rfc_5208]: https://tools.ietf.org/html/rfc5208\n     */\n    @JvmStatic\n    fun decode(certificateAndPrivateKeyPem: String): HeldCertificate {\n      var certificatePem: String? = null\n      var pkcs8Base64: String? = null\n      for (match in PEM_REGEX.findAll(certificateAndPrivateKeyPem)) {\n        when (val label = match.groups[1]!!.value) {\n          \"CERTIFICATE\" -> {\n            require(certificatePem == null) { \"string includes multiple certificates\" }\n            certificatePem = match.groups[0]!!.value // Keep --BEGIN-- and --END-- for certificates.\n          }\n\n          \"PRIVATE KEY\" -> {\n            require(pkcs8Base64 == null) { \"string includes multiple private keys\" }\n            pkcs8Base64 = match.groups[2]!!.value // Include the contents only for PKCS8.\n          }\n\n          else -> {\n            throw IllegalArgumentException(\"unexpected type: $label\")\n          }\n        }\n      }\n      require(certificatePem != null) { \"string does not include a certificate\" }\n      require(pkcs8Base64 != null) { \"string does not include a private key\" }\n\n      return decode(certificatePem, pkcs8Base64)\n    }\n\n    private fun decode(\n      certificatePem: String,\n      pkcs8Base64Text: String,\n    ): HeldCertificate {\n      val certificate = certificatePem.decodeCertificatePem()\n\n      val pkcs8Bytes =\n        pkcs8Base64Text.decodeBase64()\n          ?: throw IllegalArgumentException(\"failed to decode private key\")\n\n      // The private key doesn't tell us its type but it's okay because the certificate knows!\n      val keyType =\n        when (certificate.publicKey) {\n          is ECPublicKey -> \"EC\"\n          is RSAPublicKey -> \"RSA\"\n          else -> throw IllegalArgumentException(\"unexpected key type: ${certificate.publicKey}\")\n        }\n\n      val privateKey = decodePkcs8(pkcs8Bytes, keyType)\n\n      val keyPair = KeyPair(certificate.publicKey, privateKey)\n      return HeldCertificate(keyPair, certificate)\n    }\n\n    private fun decodePkcs8(\n      data: ByteString,\n      keyAlgorithm: String,\n    ): PrivateKey {\n      try {\n        val keyFactory = KeyFactory.getInstance(keyAlgorithm)\n        return keyFactory.generatePrivate(PKCS8EncodedKeySpec(data.toByteArray()))\n      } catch (e: GeneralSecurityException) {\n        throw IllegalArgumentException(\"failed to decode private key\", e)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/InsecureAndroidTrustManager.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal\n\nimport java.lang.reflect.InvocationTargetException\nimport java.lang.reflect.Method\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateException\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.X509TrustManager\n\n/** This extends [X509TrustManager] for Android to disable verification for a set of hosts. */\ninternal class InsecureAndroidTrustManager(\n  private val delegate: X509TrustManager,\n  private val insecureHosts: List<String>,\n) : X509TrustManager {\n  private val checkServerTrustedMethod: Method? =\n    try {\n      delegate::class.java.getMethod(\n        \"checkServerTrusted\",\n        Array<X509Certificate>::class.java,\n        String::class.java,\n        String::class.java,\n      )\n    } catch (_: NoSuchMethodException) {\n      null\n    }\n\n  /** Android method to clean and sort certificates, called via reflection. */\n  @Suppress(\"unused\", \"UNCHECKED_CAST\")\n  fun checkServerTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String,\n    host: String,\n  ): List<Certificate> {\n    if (host in insecureHosts) return listOf()\n    try {\n      val method =\n        checkServerTrustedMethod\n          ?: throw CertificateException(\"Failed to call checkServerTrusted\")\n      return method.invoke(delegate, chain, authType, host) as List<Certificate>\n    } catch (e: InvocationTargetException) {\n      throw e.targetException\n    }\n  }\n\n  override fun getAcceptedIssuers(): Array<X509Certificate> = delegate.acceptedIssuers\n\n  override fun checkClientTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String?,\n  ) = throw CertificateException(\"Unsupported operation\")\n\n  override fun checkServerTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String,\n  ) = throw CertificateException(\"Unsupported operation\")\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/InsecureExtendedTrustManager.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3.tls.internal\n\nimport java.net.InetSocketAddress\nimport java.net.Socket\nimport java.security.cert.CertificateException\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.SSLEngine\nimport javax.net.ssl.X509ExtendedTrustManager\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n\n/**\n * This extends [X509ExtendedTrustManager] to disable verification for a set of hosts.\n *\n * Note that the superclass [X509ExtendedTrustManager] isn't available on Android until version 7\n * (API level 24).\n */\n@IgnoreJRERequirement\ninternal class InsecureExtendedTrustManager(\n  private val delegate: X509ExtendedTrustManager,\n  private val insecureHosts: List<String>,\n) : X509ExtendedTrustManager() {\n  override fun getAcceptedIssuers(): Array<X509Certificate> = delegate.acceptedIssuers\n\n  override fun checkServerTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String,\n    socket: Socket,\n  ) {\n    if (socket.peerName() !in insecureHosts) {\n      delegate.checkServerTrusted(chain, authType, socket)\n    }\n  }\n\n  override fun checkServerTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String,\n    engine: SSLEngine,\n  ) {\n    if (engine.peerHost !in insecureHosts) {\n      delegate.checkServerTrusted(chain, authType, engine)\n    }\n  }\n\n  override fun checkServerTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String,\n  ) = throw CertificateException(\"Unsupported operation\")\n\n  override fun checkClientTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String?,\n  ) = throw CertificateException(\"Unsupported operation\")\n\n  override fun checkClientTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String,\n    engine: SSLEngine?,\n  ) = throw CertificateException(\"Unsupported operation\")\n\n  override fun checkClientTrusted(\n    chain: Array<out X509Certificate>,\n    authType: String,\n    socket: Socket?,\n  ) = throw CertificateException(\"Unsupported operation\")\n\n  private fun Socket.peerName(): String {\n    val address = remoteSocketAddress\n    return if (address is InetSocketAddress) address.hostName else address.toString()\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/TlsUtil.kt",
    "content": "/*\n * Copyright (C) 2012 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal\n\nimport java.io.InputStream\nimport java.security.KeyStore\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\nimport javax.net.ssl.KeyManagerFactory\nimport javax.net.ssl.TrustManagerFactory\nimport javax.net.ssl.X509ExtendedTrustManager\nimport javax.net.ssl.X509KeyManager\nimport javax.net.ssl.X509TrustManager\nimport okhttp3.internal.platform.Platform\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.HeldCertificate\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n\nobject TlsUtil {\n  val password = \"password\".toCharArray()\n\n  private val localhost: HandshakeCertificates by lazy {\n    // Generate a self-signed cert for the server to serve and the client to trust.\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"localhost\")\n        .addSubjectAlternativeName(\"localhost\")\n        .addSubjectAlternativeName(\"localhost.localdomain\")\n        .build()\n    return@lazy HandshakeCertificates\n      .Builder()\n      .heldCertificate(heldCertificate)\n      .addTrustedCertificate(heldCertificate.certificate)\n      .build()\n  }\n\n  /** Returns an SSL client for this host's localhost address. */\n  @JvmStatic\n  fun localhost(): HandshakeCertificates = localhost\n\n  /** Returns a trust manager that trusts `trustedCertificates`. */\n  @JvmStatic @IgnoreJRERequirement\n  fun newTrustManager(\n    keyStoreType: String?,\n    trustedCertificates: List<X509Certificate>,\n    insecureHosts: List<String>,\n  ): X509TrustManager {\n    val trustStore = newEmptyKeyStore(keyStoreType)\n    for (i in trustedCertificates.indices) {\n      trustStore.setCertificateEntry(\"cert_$i\", trustedCertificates[i])\n    }\n\n    val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())\n    factory.init(trustStore)\n    val result = factory.trustManagers!!\n    check(result.size == 1 && result[0] is X509TrustManager) {\n      \"Unexpected trust managers: ${result.contentToString()}\"\n    }\n\n    val trustManager = result[0] as X509TrustManager\n\n    return when {\n      insecureHosts.isEmpty() -> trustManager\n      Platform.isAndroid -> InsecureAndroidTrustManager(trustManager, insecureHosts)\n      else -> InsecureExtendedTrustManager(trustManager as X509ExtendedTrustManager, insecureHosts)\n    }\n  }\n\n  /**\n   * Returns a key manager for the held certificate and its chain. Returns an empty key manager if\n   * `heldCertificate` is null.\n   */\n  @JvmStatic\n  fun newKeyManager(\n    keyStoreType: String?,\n    heldCertificate: HeldCertificate?,\n    vararg intermediates: X509Certificate,\n  ): X509KeyManager {\n    val keyStore = newEmptyKeyStore(keyStoreType)\n    if (heldCertificate != null) {\n      val chain = arrayOfNulls<Certificate>(1 + intermediates.size)\n      chain[0] = heldCertificate.certificate\n      intermediates.copyInto(chain, 1)\n      keyStore.setKeyEntry(\"private\", heldCertificate.keyPair.private, password, chain)\n    }\n\n    // https://github.com/bcgit/bc-java/issues/1160\n    val isBouncyCastle = keyStore.provider.name == \"BC\"\n    val algorithm = if (isBouncyCastle) \"PKIX\" else KeyManagerFactory.getDefaultAlgorithm()\n\n    val factory = KeyManagerFactory.getInstance(algorithm)\n    factory.init(keyStore, password)\n    val result = factory.keyManagers!!\n    check(result.size == 1 && result[0] is X509KeyManager) {\n      \"Unexpected key managers:${result.contentToString()}\"\n    }\n\n    return result[0] as X509KeyManager\n  }\n\n  private fun newEmptyKeyStore(keyStoreType: String?): KeyStore =\n    KeyStore.getInstance(keyStoreType ?: KeyStore.getDefaultType()).apply {\n      val inputStream: InputStream? = null // By convention, 'null' creates an empty key store.\n      load(inputStream, password)\n    }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/Adapters.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport java.math.BigInteger\nimport java.net.ProtocolException\nimport java.text.ParseException\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.TimeZone\nimport kotlin.reflect.KClass\nimport okio.ByteString\n\n/**\n * Built-in adapters for reading standard ASN.1 types.\n */\ninternal object Adapters {\n  val BOOLEAN =\n    BasicDerAdapter(\n      name = \"BOOLEAN\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 1L,\n      codec =\n        object : BasicDerAdapter.Codec<Boolean> {\n          override fun decode(reader: DerReader): Boolean = reader.readBoolean()\n\n          override fun encode(\n            writer: DerWriter,\n            value: Boolean,\n          ) = writer.writeBoolean(value)\n        },\n    )\n\n  val INTEGER_AS_LONG =\n    BasicDerAdapter(\n      name = \"INTEGER\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 2L,\n      codec =\n        object : BasicDerAdapter.Codec<Long> {\n          override fun decode(reader: DerReader): Long = reader.readLong()\n\n          override fun encode(\n            writer: DerWriter,\n            value: Long,\n          ) = writer.writeLong(value)\n        },\n    )\n\n  val INTEGER_AS_BIG_INTEGER =\n    BasicDerAdapter(\n      name = \"INTEGER\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 2L,\n      codec =\n        object : BasicDerAdapter.Codec<BigInteger> {\n          override fun decode(reader: DerReader): BigInteger = reader.readBigInteger()\n\n          override fun encode(\n            writer: DerWriter,\n            value: BigInteger,\n          ) = writer.writeBigInteger(value)\n        },\n    )\n\n  val BIT_STRING =\n    BasicDerAdapter(\n      name = \"BIT STRING\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 3L,\n      codec =\n        object : BasicDerAdapter.Codec<BitString> {\n          override fun decode(reader: DerReader): BitString = reader.readBitString()\n\n          override fun encode(\n            writer: DerWriter,\n            value: BitString,\n          ) = writer.writeBitString(value)\n        },\n    )\n\n  val OCTET_STRING =\n    BasicDerAdapter(\n      name = \"OCTET STRING\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 4L,\n      codec =\n        object : BasicDerAdapter.Codec<ByteString> {\n          override fun decode(reader: DerReader): ByteString = reader.readOctetString()\n\n          override fun encode(\n            writer: DerWriter,\n            value: ByteString,\n          ) = writer.writeOctetString(value)\n        },\n    )\n\n  val NULL =\n    BasicDerAdapter(\n      name = \"NULL\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 5L,\n      codec =\n        object : BasicDerAdapter.Codec<Unit?> {\n          override fun decode(reader: DerReader): Unit? = null\n\n          override fun encode(\n            writer: DerWriter,\n            value: Unit?,\n          ) {\n          }\n        },\n    )\n\n  val OBJECT_IDENTIFIER =\n    BasicDerAdapter(\n      name = \"OBJECT IDENTIFIER\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 6L,\n      codec =\n        object : BasicDerAdapter.Codec<String> {\n          override fun decode(reader: DerReader): String = reader.readObjectIdentifier()\n\n          override fun encode(\n            writer: DerWriter,\n            value: String,\n          ) = writer.writeObjectIdentifier(value)\n        },\n    )\n\n  val UTF8_STRING =\n    BasicDerAdapter(\n      name = \"UTF8\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 12L,\n      codec =\n        object : BasicDerAdapter.Codec<String> {\n          override fun decode(reader: DerReader): String = reader.readUtf8String()\n\n          override fun encode(\n            writer: DerWriter,\n            value: String,\n          ) = writer.writeUtf8(value)\n        },\n    )\n\n  /**\n   * Permits alphanumerics, spaces, and these:\n   *\n   * ```\n   *   ' () + , - . / : = ?\n   * ```\n   *\n   * TODO(jwilson): constrain to printable string characters.\n   */\n  val PRINTABLE_STRING =\n    BasicDerAdapter(\n      name = \"PRINTABLE STRING\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 19L,\n      codec =\n        object : BasicDerAdapter.Codec<String> {\n          override fun decode(reader: DerReader): String = reader.readUtf8String()\n\n          override fun encode(\n            writer: DerWriter,\n            value: String,\n          ) = writer.writeUtf8(value)\n        },\n    )\n\n  /**\n   * Based on International Alphabet No. 5. Note that there are bytes that IA5 and US-ASCII\n   * disagree on interpretation.\n   *\n   * TODO(jwilson): constrain to IA5 characters.\n   */\n  val IA5_STRING =\n    BasicDerAdapter(\n      name = \"IA5 STRING\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 22L,\n      codec =\n        object : BasicDerAdapter.Codec<String> {\n          override fun decode(reader: DerReader): String = reader.readUtf8String()\n\n          override fun encode(\n            writer: DerWriter,\n            value: String,\n          ) = writer.writeUtf8(value)\n        },\n    )\n\n  /**\n   * A timestamp like \"191216030210Z\" or \"191215190210-0800\" for 2019-12-15T19:02:10-08:00. The\n   * cutoff of the 2-digit year is 1950-01-01T00:00:00Z.\n   */\n  val UTC_TIME =\n    BasicDerAdapter(\n      name = \"UTC TIME\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 23L,\n      codec =\n        object : BasicDerAdapter.Codec<Long> {\n          override fun decode(reader: DerReader): Long {\n            val string = reader.readUtf8String()\n            return parseUtcTime(string)\n          }\n\n          override fun encode(\n            writer: DerWriter,\n            value: Long,\n          ) {\n            val string = formatUtcTime(value)\n            return writer.writeUtf8(string)\n          }\n        },\n    )\n\n  internal fun parseUtcTime(string: String): Long {\n    val utc = TimeZone.getTimeZone(\"GMT\")\n    val dateFormat =\n      SimpleDateFormat(\"yyMMddHHmmss'Z'\").apply {\n        timeZone = utc\n        set2DigitYearStart(Date(-631152000000L)) // 1950-01-01T00:00:00Z.\n      }\n\n    try {\n      val parsed = dateFormat.parse(string)\n      return parsed.time\n    } catch (e: ParseException) {\n      throw ProtocolException(\"Failed to parse UTCTime $string\")\n    }\n  }\n\n  internal fun formatUtcTime(date: Long): String {\n    val utc = TimeZone.getTimeZone(\"GMT\")\n    val dateFormat =\n      SimpleDateFormat(\"yyMMddHHmmss'Z'\").apply {\n        timeZone = utc\n        set2DigitYearStart(Date(-631152000000L)) // 1950-01-01T00:00:00Z.\n      }\n\n    return dateFormat.format(date)\n  }\n\n  /**\n   * A timestamp like \"191216030210Z\" or \"20191215190210-0800\" for 2019-12-15T19:02:10-08:00. This\n   * is the same as [UTC_TIME] with the exception of the 4-digit year.\n   */\n  val GENERALIZED_TIME =\n    BasicDerAdapter(\n      name = \"GENERALIZED TIME\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 24L,\n      codec =\n        object : BasicDerAdapter.Codec<Long> {\n          override fun decode(reader: DerReader): Long {\n            val string = reader.readUtf8String()\n            return parseGeneralizedTime(string)\n          }\n\n          override fun encode(\n            writer: DerWriter,\n            value: Long,\n          ) {\n            val string = formatGeneralizedTime(value)\n            return writer.writeUtf8(string)\n          }\n        },\n    )\n\n  /** Decodes any value without interpretation as [AnyValue]. */\n  val ANY_VALUE =\n    object : DerAdapter<AnyValue> {\n      override fun matches(header: DerHeader): Boolean = true\n\n      override fun fromDer(reader: DerReader): AnyValue {\n        reader.read(\"ANY\") { header ->\n          val bytes = reader.readUnknown()\n          return AnyValue(\n            tagClass = header.tagClass,\n            tag = header.tag,\n            constructed = header.constructed,\n            length = header.length,\n            bytes = bytes,\n          )\n        }\n      }\n\n      override fun toDer(\n        writer: DerWriter,\n        value: AnyValue,\n      ) {\n        writer.write(\"ANY\", value.tagClass, value.tag) {\n          writer.writeOctetString(value.bytes)\n          writer.constructed = value.constructed\n        }\n      }\n    }\n\n  internal fun parseGeneralizedTime(string: String): Long {\n    val utc = TimeZone.getTimeZone(\"GMT\")\n    val dateFormat =\n      SimpleDateFormat(\"yyyyMMddHHmmss'Z'\").apply {\n        timeZone = utc\n      }\n\n    try {\n      val parsed = dateFormat.parse(string)\n      return parsed.time\n    } catch (e: ParseException) {\n      throw ProtocolException(\"Failed to parse GeneralizedTime $string\")\n    }\n  }\n\n  internal fun formatGeneralizedTime(date: Long): String {\n    val utc = TimeZone.getTimeZone(\"GMT\")\n    val dateFormat =\n      SimpleDateFormat(\"yyyyMMddHHmmss'Z'\").apply {\n        timeZone = utc\n      }\n\n    return dateFormat.format(date)\n  }\n\n  /**\n   * Returns a composite adapter for a struct or data class. This may be used for both SEQUENCE and\n   * SET types.\n   *\n   * The fields are specified as a list of member adapters. When decoding, a value for each\n   * non-optional member but be included in sequence.\n   *\n   * TODO: for sets, sort by tag when encoding.\n   * TODO: for set ofs, sort by encoded value when encoding.\n   */\n  fun <T> sequence(\n    name: String,\n    vararg members: DerAdapter<*>,\n    decompose: (T) -> List<*>,\n    construct: (List<*>) -> T,\n  ): BasicDerAdapter<T> {\n    val codec =\n      object : BasicDerAdapter.Codec<T> {\n        override fun decode(reader: DerReader): T {\n          return reader.withTypeHint {\n            val list = mutableListOf<Any?>()\n\n            while (list.size < members.size) {\n              val member = members[list.size]\n              list += member.fromDer(reader)\n            }\n\n            if (reader.hasNext()) {\n              throw ProtocolException(\"unexpected ${reader.peekHeader()} at $reader\")\n            }\n\n            return@withTypeHint construct(list)\n          }\n        }\n\n        override fun encode(\n          writer: DerWriter,\n          value: T,\n        ) {\n          val list = decompose(value)\n          writer.withTypeHint {\n            for (i in list.indices) {\n              val adapter = members[i] as DerAdapter<Any?>\n              adapter.toDer(writer, list[i])\n            }\n          }\n        }\n      }\n\n    return BasicDerAdapter(\n      name = name,\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 16L,\n      codec = codec,\n    )\n  }\n\n  /** Returns an adapter that decodes as the first of a list of available types. */\n  fun choice(vararg choices: DerAdapter<*>): DerAdapter<Pair<DerAdapter<*>, Any?>> {\n    return object : DerAdapter<Pair<DerAdapter<*>, Any?>> {\n      override fun matches(header: DerHeader): Boolean = true\n\n      override fun fromDer(reader: DerReader): Pair<DerAdapter<*>, Any?> {\n        val peekedHeader =\n          reader.peekHeader()\n            ?: throw ProtocolException(\"expected a value at $reader\")\n\n        val choice =\n          choices.firstOrNull { it.matches(peekedHeader) }\n            ?: throw ProtocolException(\n              \"expected a matching choice but was $peekedHeader at $reader\",\n            )\n\n        return choice to choice.fromDer(reader)\n      }\n\n      override fun toDer(\n        writer: DerWriter,\n        value: Pair<DerAdapter<*>, Any?>,\n      ) {\n        val (adapter, v) = value\n        (adapter as DerAdapter<Any?>).toDer(writer, v)\n      }\n\n      override fun toString(): String = choices.joinToString(separator = \" OR \")\n    }\n  }\n\n  /**\n   * This decodes a value into its contents using a preceding member of the same SEQUENCE. For\n   * example, extensions type IDs specify what types to use for the corresponding values.\n   *\n   * If the hint is unknown [chooser] should return null which will cause the value to be decoded as\n   * an opaque byte string.\n   *\n   * This may optionally wrap the contents in a tag.\n   */\n  fun usingTypeHint(chooser: (Any?) -> DerAdapter<*>?): DerAdapter<Any?> {\n    return object : DerAdapter<Any?> {\n      override fun matches(header: DerHeader): Boolean = true\n\n      override fun toDer(\n        writer: DerWriter,\n        value: Any?,\n      ) {\n        // If we don't understand this hint, encode the body as a byte string. The byte string\n        // will include a tag and length header as a prefix.\n        val adapter = chooser(writer.typeHint) as DerAdapter<Any?>?\n        when {\n          adapter != null -> adapter.toDer(writer, value)\n          else -> writer.writeOctetString(value as ByteString)\n        }\n      }\n\n      override fun fromDer(reader: DerReader): Any? {\n        val adapter = chooser(reader.typeHint) as DerAdapter<Any?>?\n        return when {\n          adapter != null -> adapter.fromDer(reader)\n          else -> reader.readUnknown()\n        }\n      }\n    }\n  }\n\n  /**\n   * Object class to adapter type. This approach limits us to one adapter per Kotlin class, which\n   * might be too few for values like UTF_STRING and OBJECT_IDENTIFIER that share a Kotlin class but\n   * have very different ASN.1 interpretations.\n   */\n  private val defaultAnyChoices =\n    listOf(\n      Boolean::class to BOOLEAN,\n      BigInteger::class to INTEGER_AS_BIG_INTEGER,\n      BitString::class to BIT_STRING,\n      ByteString::class to OCTET_STRING,\n      Unit::class to NULL,\n      Nothing::class to OBJECT_IDENTIFIER,\n      Nothing::class to UTF8_STRING,\n      String::class to PRINTABLE_STRING,\n      Nothing::class to IA5_STRING,\n      Nothing::class to UTC_TIME,\n      Long::class to GENERALIZED_TIME,\n      AnyValue::class to ANY_VALUE,\n    )\n\n  fun any(\n    vararg choices: Pair<KClass<*>, DerAdapter<*>> = defaultAnyChoices.toTypedArray(),\n    isOptional: Boolean = false,\n    optionalValue: Any? = null,\n  ): DerAdapter<Any?> {\n    return object : DerAdapter<Any?> {\n      override fun matches(header: DerHeader): Boolean = true\n\n      override fun toDer(\n        writer: DerWriter,\n        value: Any?,\n      ) {\n        when {\n          isOptional && value == optionalValue -> {\n            // Write nothing.\n          }\n\n          else -> {\n            for ((type, adapter) in choices) {\n              if (type.isInstance(value) || (value == null && type == Unit::class)) {\n                (adapter as DerAdapter<Any?>).toDer(writer, value)\n                return\n              }\n            }\n          }\n        }\n      }\n\n      override fun fromDer(reader: DerReader): Any? {\n        if (isOptional && !reader.hasNext()) return optionalValue\n\n        val peekedHeader =\n          reader.peekHeader()\n            ?: throw ProtocolException(\"expected a value at $reader\")\n        for ((_, adapter) in choices) {\n          if (adapter.matches(peekedHeader)) {\n            return adapter.fromDer(reader)\n          }\n        }\n\n        throw ProtocolException(\"expected any but was $peekedHeader at $reader\")\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/AnyValue.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport okio.ByteString\n\n/**\n * A value whose type is not specified statically. Use this with [Adapters.any] which will attempt\n * to resolve a concrete type.\n */\ninternal data class AnyValue(\n  var tagClass: Int,\n  var tag: Long,\n  var constructed: Boolean = false,\n  var length: Long = -1L,\n  val bytes: ByteString,\n) {\n  // Avoid Long.hashCode(long) which isn't available on Android 5.\n  override fun hashCode(): Int {\n    var result = 0\n    result = 31 * result + tagClass\n    result = 31 * result + tag.toInt()\n    result = 31 * result + (if (constructed) 0 else 1)\n    result = 31 * result + length.toInt()\n    result = 31 * result + bytes.hashCode()\n    return result\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/BasicDerAdapter.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport java.net.ProtocolException\n\n/**\n * Handles basic types that always use the same tag. This supports optional types and may set a type\n * hint for further adapters to process.\n *\n * Types like ANY and CHOICE that don't have a consistent tag cannot use this.\n */\ninternal data class BasicDerAdapter<T>(\n  private val name: String,\n  /** The tag class this adapter expects, or -1 to match any tag class. */\n  val tagClass: Int,\n  /** The tag this adapter expects, or -1 to match any tag. */\n  val tag: Long,\n  /** Encode and decode the value once tags are handled. */\n  private val codec: Codec<T>,\n  /** True if the default value should be used if this value is absent during decoding. */\n  val isOptional: Boolean = false,\n  /** The value to return if this value is absent. Undefined unless this is optional. */\n  val defaultValue: T? = null,\n  /** True to set the encoded or decoded value as the type hint for the current SEQUENCE. */\n  private val typeHint: Boolean = false,\n) : DerAdapter<T> {\n  init {\n    require(tagClass >= 0)\n    require(tag >= 0)\n  }\n\n  override fun matches(header: DerHeader): Boolean = header.tagClass == tagClass && header.tag == tag\n\n  override fun fromDer(reader: DerReader): T {\n    val peekedHeader = reader.peekHeader()\n    if (peekedHeader == null || peekedHeader.tagClass != tagClass || peekedHeader.tag != tag) {\n      if (isOptional) return defaultValue as T\n      throw ProtocolException(\"expected $this but was $peekedHeader at $reader\")\n    }\n\n    val result =\n      reader.read(name) {\n        codec.decode(reader)\n      }\n\n    if (typeHint) {\n      reader.typeHint = result\n    }\n\n    return result\n  }\n\n  override fun toDer(\n    writer: DerWriter,\n    value: T,\n  ) {\n    if (typeHint) {\n      writer.typeHint = value\n    }\n\n    if (isOptional && value == defaultValue) {\n      // Nothing to write!\n      return\n    }\n\n    writer.write(name, tagClass, tag) {\n      codec.encode(writer, value)\n    }\n  }\n\n  /**\n   * Returns a copy with a context tag. This should be used when the type is ambiguous on its own.\n   * For example, the tags in this schema are 0 and 1:\n   *\n   * ```\n   * Point ::= SEQUENCE {\n   *   x [0] INTEGER OPTIONAL,\n   *   y [1] INTEGER OPTIONAL\n   * }\n   * ```\n   *\n   * You may also specify a tag class like [DerHeader.TAG_CLASS_APPLICATION]. The default tag class\n   * is [DerHeader.TAG_CLASS_CONTEXT_SPECIFIC].\n   *\n   * ```\n   * Point ::= SEQUENCE {\n   *   x [APPLICATION 0] INTEGER OPTIONAL,\n   *   y [APPLICATION 1] INTEGER OPTIONAL\n   * }\n   * ```\n   */\n  fun withTag(\n    tagClass: Int = DerHeader.TAG_CLASS_CONTEXT_SPECIFIC,\n    tag: Long,\n  ): BasicDerAdapter<T> = copy(tagClass = tagClass, tag = tag)\n\n  /** Returns a copy of this adapter that doesn't encode values equal to [defaultValue]. */\n  fun optional(defaultValue: T? = null): BasicDerAdapter<T> = copy(isOptional = true, defaultValue = defaultValue)\n\n  /**\n   * Returns a copy of this adapter that sets the encoded or decoded value as the type hint for the\n   * other adapters on this SEQUENCE to interrogate.\n   */\n  fun asTypeHint(): BasicDerAdapter<T> = copy(typeHint = true)\n\n  // Avoid Long.hashCode(long) which isn't available on Android 5.\n  override fun hashCode(): Int {\n    var result = 0\n    result = 31 * result + name.hashCode()\n    result = 31 * result + tagClass\n    result = 31 * result + tag.toInt()\n    result = 31 * result + codec.hashCode()\n    result = 31 * result + (if (isOptional) 1 else 0)\n    result = 31 * result + defaultValue.hashCode()\n    result = 31 * result + (if (typeHint) 1 else 0)\n    return result\n  }\n\n  override fun toString(): String = \"$name [$tagClass/$tag]\"\n\n  /** Reads and writes values without knowledge of the enclosing tag, length, or defaults. */\n  interface Codec<T> {\n    fun decode(reader: DerReader): T\n\n    fun encode(\n      writer: DerWriter,\n      value: T,\n    )\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/BitString.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport okio.ByteString\n\n/**\n * Like a [ByteString], but whose bits are not necessarily a strict multiple of 8.\n */\ninternal data class BitString(\n  val byteString: ByteString,\n  /** 0-7 unused bits in the last byte. */\n  val unusedBitsCount: Int,\n) {\n  // Avoid Long.hashCode(long) which isn't available on Android 5.\n  override fun hashCode(): Int {\n    var result = 0\n    result = 31 * result + byteString.hashCode()\n    result = 31 * result + unusedBitsCount\n    return result\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/Certificate.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport java.math.BigInteger\nimport java.security.GeneralSecurityException\nimport java.security.PublicKey\nimport java.security.Signature\nimport java.security.SignatureException\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\nimport okio.Buffer\nimport okio.ByteString\nimport org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n\ninternal data class Certificate(\n  val tbsCertificate: TbsCertificate,\n  val signatureAlgorithm: AlgorithmIdentifier,\n  val signatureValue: BitString,\n) {\n  val commonName: Any?\n    get() {\n      return tbsCertificate.subject\n        .flatten()\n        .firstOrNull { it.type == ObjectIdentifiers.COMMON_NAME }\n        ?.value\n    }\n\n  val organizationalUnitName: Any?\n    get() {\n      return tbsCertificate.subject\n        .flatten()\n        .firstOrNull { it.type == ObjectIdentifiers.ORGANIZATIONAL_UNIT_NAME }\n        ?.value\n    }\n\n  val subjectAlternativeNames: Extension?\n    get() {\n      return tbsCertificate.extensions.firstOrNull {\n        it.id == ObjectIdentifiers.SUBJECT_ALTERNATIVE_NAME\n      }\n    }\n\n  val basicConstraints: Extension\n    get() {\n      return tbsCertificate.extensions.first {\n        it.id == ObjectIdentifiers.BASIC_CONSTRAINTS\n      }\n    }\n\n  /** Returns true if the certificate was signed by [issuer]. */\n  @Throws(SignatureException::class)\n  fun checkSignature(issuer: PublicKey): Boolean {\n    val signedData = CertificateAdapters.tbsCertificate.toDer(tbsCertificate)\n\n    return Signature.getInstance(tbsCertificate.signatureAlgorithmName).run {\n      initVerify(issuer)\n      update(signedData.toByteArray())\n      verify(signatureValue.byteString.toByteArray())\n    }\n  }\n\n  fun toX509Certificate(): X509Certificate {\n    val data = CertificateAdapters.certificate.toDer(this)\n    try {\n      val certificateFactory = CertificateFactory.getInstance(\"X.509\")\n      val certificates = certificateFactory.generateCertificates(Buffer().write(data).inputStream())\n      return certificates.single() as X509Certificate\n    } catch (e: NoSuchElementException) {\n      throw IllegalArgumentException(\"failed to decode certificate\", e)\n    } catch (e: IllegalArgumentException) {\n      throw IllegalArgumentException(\"failed to decode certificate\", e)\n    } catch (e: GeneralSecurityException) {\n      throw IllegalArgumentException(\"failed to decode certificate\", e)\n    }\n  }\n}\n\ninternal data class TbsCertificate(\n  /** This is a integer enum. Use 0L for v1, 1L for v2, and 2L for v3. */\n  val version: Long,\n  val serialNumber: BigInteger,\n  val signature: AlgorithmIdentifier,\n  val issuer: List<List<AttributeTypeAndValue>>,\n  val validity: Validity,\n  val subject: List<List<AttributeTypeAndValue>>,\n  val subjectPublicKeyInfo: SubjectPublicKeyInfo,\n  val issuerUniqueID: BitString?,\n  val subjectUniqueID: BitString?,\n  val extensions: List<Extension>,\n) {\n  /**\n   * Returns the standard name of this certificate's signature algorithm as specified by\n   * [Signature.getInstance]. Typical values are like \"SHA256WithRSA\".\n   */\n  val signatureAlgorithmName: String\n    get() {\n      return when (signature.algorithm) {\n        ObjectIdentifiers.SHA256_WITH_RSA_ENCRYPTION -> \"SHA256WithRSA\"\n        ObjectIdentifiers.SHA256_WITH_ECDSA -> \"SHA256withECDSA\"\n        else -> error(\"unexpected signature algorithm: ${signature.algorithm}\")\n      }\n    }\n\n  // Avoid Long.hashCode(long) which isn't available on Android 5.\n  override fun hashCode(): Int {\n    var result = 0\n    result = 31 * result + version.toInt()\n    result = 31 * result + serialNumber.hashCode()\n    result = 31 * result + signature.hashCode()\n    result = 31 * result + issuer.hashCode()\n    result = 31 * result + validity.hashCode()\n    result = 31 * result + subject.hashCode()\n    result = 31 * result + subjectPublicKeyInfo.hashCode()\n    result = 31 * result + (issuerUniqueID?.hashCode() ?: 0)\n    result = 31 * result + (subjectUniqueID?.hashCode() ?: 0)\n    result = 31 * result + extensions.hashCode()\n    return result\n  }\n}\n\ninternal data class AlgorithmIdentifier(\n  /** An OID string like \"1.2.840.113549.1.1.11\" for sha256WithRSAEncryption. */\n  val algorithm: String,\n  /** Parameters of a type implied by [algorithm]. */\n  val parameters: Any?,\n)\n\ninternal data class AttributeTypeAndValue(\n  /** An OID string like \"2.5.4.11\" for organizationalUnitName. */\n  val type: String,\n  val value: Any?,\n)\n\ninternal data class Validity(\n  val notBefore: Long,\n  val notAfter: Long,\n) {\n  // Avoid Long.hashCode(long) which isn't available on Android 5.\n  override fun hashCode(): Int {\n    var result = 0\n    result = 31 * result + notBefore.toInt()\n    result = 31 * result + notAfter.toInt()\n    return result\n  }\n}\n\ninternal data class SubjectPublicKeyInfo(\n  val algorithm: AlgorithmIdentifier,\n  val subjectPublicKey: BitString,\n)\n\n@IgnoreJRERequirement // As of AGP 3.4.1, D8 desugars API 24 hashCode methods.\ninternal data class Extension(\n  val id: String,\n  val critical: Boolean,\n  val value: Any?,\n)\n\n@IgnoreJRERequirement // As of AGP 3.4.1, D8 desugars API 24 hashCode methods.\ninternal data class BasicConstraints(\n  /** True if this certificate can be used as a Certificate Authority (CA). */\n  val ca: Boolean,\n  /** The maximum number of intermediate CAs between this and leaf certificates. */\n  val maxIntermediateCas: Long?,\n)\n\n/** A private key. Note that this class doesn't support attributes or an embedded public key. */\ninternal data class PrivateKeyInfo(\n  // v1(0), v2(1).\n  val version: Long,\n  val algorithmIdentifier: AlgorithmIdentifier,\n  val privateKey: ByteString,\n) {\n  // Avoid Long.hashCode(long) which isn't available on Android 5.\n  override fun hashCode(): Int {\n    var result = 0\n    result = 31 * result + version.toInt()\n    result = 31 * result + algorithmIdentifier.hashCode()\n    result = 31 * result + privateKey.hashCode()\n    return result\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/CertificateAdapters.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport java.math.BigInteger\nimport java.net.ProtocolException\nimport okio.ByteString\n\n/**\n * ASN.1 adapters adapted from the specifications in [RFC 5280][rfc_5280].\n *\n * [rfc_5280]: https://tools.ietf.org/html/rfc5280\n */\n@Suppress(\"UNCHECKED_CAST\") // This needs to cast decoded collections.\ninternal object CertificateAdapters {\n  /**\n   * ```\n   * Time ::= CHOICE {\n   *   utcTime        UTCTime,\n   *   generalTime    GeneralizedTime\n   * }\n   * ```\n   *\n   * RFC 5280, section 4.1.2.5:\n   *\n   * > CAs conforming to this profile MUST always encode certificate validity dates through the year\n   * > 2049 as UTCTime; certificate validity dates in 2050 or later MUST be encoded as\n   * > GeneralizedTime.\n   */\n  internal val time: DerAdapter<Long> =\n    object : DerAdapter<Long> {\n      override fun matches(header: DerHeader): Boolean = Adapters.UTC_TIME.matches(header) || Adapters.GENERALIZED_TIME.matches(header)\n\n      override fun fromDer(reader: DerReader): Long {\n        val peekHeader =\n          reader.peekHeader()\n            ?: throw ProtocolException(\"expected time but was exhausted at $reader\")\n\n        return when {\n          peekHeader.tagClass == Adapters.UTC_TIME.tagClass &&\n            peekHeader.tag == Adapters.UTC_TIME.tag -> {\n            Adapters.UTC_TIME.fromDer(reader)\n          }\n\n          peekHeader.tagClass == Adapters.GENERALIZED_TIME.tagClass &&\n            peekHeader.tag == Adapters.GENERALIZED_TIME.tag -> {\n            Adapters.GENERALIZED_TIME.fromDer(reader)\n          }\n\n          else -> {\n            throw ProtocolException(\"expected time but was $peekHeader at $reader\")\n          }\n        }\n      }\n\n      override fun toDer(\n        writer: DerWriter,\n        value: Long,\n      ) {\n        // [1950-01-01T00:00:00..2050-01-01T00:00:00Z)\n        if (value in -631_152_000_000L until 2_524_608_000_000L) {\n          Adapters.UTC_TIME.toDer(writer, value)\n        } else {\n          Adapters.GENERALIZED_TIME.toDer(writer, value)\n        }\n      }\n    }\n\n  /**\n   * ```\n   * Validity ::= SEQUENCE {\n   *   notBefore      Time,\n   *   notAfter       Time\n   * }\n   * ```\n   */\n  private val validity: BasicDerAdapter<Validity> =\n    Adapters.sequence(\n      \"Validity\",\n      time,\n      time,\n      decompose = {\n        listOf(\n          it.notBefore,\n          it.notAfter,\n        )\n      },\n      construct = {\n        Validity(\n          notBefore = it[0] as Long,\n          notAfter = it[1] as Long,\n        )\n      },\n    )\n\n  /** The type of the parameters depends on the algorithm that precedes it. */\n  private val algorithmParameters: DerAdapter<Any?> =\n    Adapters.usingTypeHint { typeHint ->\n      when (typeHint) {\n        // This type is pretty strange. The spec says that for certain algorithms we must encode null\n        // when it is present, and for others we must omit it!\n        // https://tools.ietf.org/html/rfc4055#section-2.1\n        ObjectIdentifiers.SHA256_WITH_RSA_ENCRYPTION -> Adapters.NULL\n\n        ObjectIdentifiers.RSA_ENCRYPTION -> Adapters.NULL\n\n        ObjectIdentifiers.EC_PUBLIC_KEY -> Adapters.OBJECT_IDENTIFIER\n\n        else -> null\n      }\n    }\n\n  /**\n   * ```\n   * AlgorithmIdentifier ::= SEQUENCE  {\n   *   algorithm      OBJECT IDENTIFIER,\n   *   parameters     ANY DEFINED BY algorithm OPTIONAL\n   * }\n   * ```\n   */\n  internal val algorithmIdentifier: BasicDerAdapter<AlgorithmIdentifier> =\n    Adapters.sequence(\n      \"AlgorithmIdentifier\",\n      Adapters.OBJECT_IDENTIFIER.asTypeHint(),\n      algorithmParameters,\n      decompose = {\n        listOf(\n          it.algorithm,\n          it.parameters,\n        )\n      },\n      construct = {\n        AlgorithmIdentifier(\n          algorithm = it[0] as String,\n          parameters = it[1],\n        )\n      },\n    )\n\n  /**\n   * ```\n   * BasicConstraints ::= SEQUENCE {\n   *   cA                      BOOLEAN DEFAULT FALSE,\n   *   pathLenConstraint       INTEGER (0..MAX) OPTIONAL\n   * }\n   * ```\n   */\n  private val basicConstraints: BasicDerAdapter<BasicConstraints> =\n    Adapters.sequence(\n      \"BasicConstraints\",\n      Adapters.BOOLEAN.optional(defaultValue = false),\n      Adapters.INTEGER_AS_LONG.optional(),\n      decompose = {\n        listOf(\n          it.ca,\n          it.maxIntermediateCas,\n        )\n      },\n      construct = {\n        BasicConstraints(\n          ca = it[0] as Boolean,\n          maxIntermediateCas = it[1] as Long?,\n        )\n      },\n    )\n\n  /**\n   * Note that only a subset of available choices are implemented.\n   *\n   * ```\n   * GeneralName ::= CHOICE {\n   *   otherName                       [0]     OtherName,\n   *   rfc822Name                      [1]     IA5String,\n   *   dNSName                         [2]     IA5String,\n   *   x400Address                     [3]     ORAddress,\n   *   directoryName                   [4]     Name,\n   *   ediPartyName                    [5]     EDIPartyName,\n   *   uniformResourceIdentifier       [6]     IA5String,\n   *   iPAddress                       [7]     OCTET STRING,\n   *   registeredID                    [8]     OBJECT IDENTIFIER\n   * }\n   * ```\n   *\n   * The first property of the pair is the adapter that was used, the second property is the value.\n   */\n  internal val generalNameDnsName = Adapters.IA5_STRING.withTag(tag = 2L)\n  internal val generalNameIpAddress = Adapters.OCTET_STRING.withTag(tag = 7L)\n  internal val generalName: DerAdapter<Pair<DerAdapter<*>, Any?>> =\n    Adapters.choice(\n      generalNameDnsName,\n      generalNameIpAddress,\n      Adapters.ANY_VALUE,\n    )\n\n  /**\n   * ```\n   * SubjectAltName ::= GeneralNames\n   *\n   * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName\n   * ```\n   */\n  private val subjectAlternativeName: BasicDerAdapter<List<Pair<DerAdapter<*>, Any?>>> =\n    generalName.asSequenceOf()\n\n  /**\n   * This uses the preceding extension ID to select which adapter to use for the extension value\n   * that follows.\n   */\n  private val extensionValue: BasicDerAdapter<Any?> =\n    Adapters\n      .usingTypeHint { typeHint ->\n        when (typeHint) {\n          ObjectIdentifiers.SUBJECT_ALTERNATIVE_NAME -> subjectAlternativeName\n          ObjectIdentifiers.BASIC_CONSTRAINTS -> basicConstraints\n          else -> null\n        }\n      }.withExplicitBox(\n        tagClass = Adapters.OCTET_STRING.tagClass,\n        tag = Adapters.OCTET_STRING.tag,\n        forceConstructed = false,\n      )\n\n  /**\n   * ```\n   * Extension ::= SEQUENCE  {\n   *   extnID      OBJECT IDENTIFIER,\n   *   critical    BOOLEAN DEFAULT FALSE,\n   *   extnValue   OCTET STRING\n   *     -- contains the DER encoding of an ASN.1 value\n   *     -- corresponding to the extension type identified\n   *     -- by extnID\n   * }\n   * ```\n   */\n  internal val extension: BasicDerAdapter<Extension> =\n    Adapters.sequence(\n      \"Extension\",\n      Adapters.OBJECT_IDENTIFIER.asTypeHint(),\n      Adapters.BOOLEAN.optional(defaultValue = false),\n      extensionValue,\n      decompose = {\n        listOf(\n          it.id,\n          it.critical,\n          it.value,\n        )\n      },\n      construct = {\n        Extension(\n          id = it[0] as String,\n          critical = it[1] as Boolean,\n          value = it[2],\n        )\n      },\n    )\n\n  /**\n   * ```\n   * AttributeTypeAndValue ::= SEQUENCE {\n   *   type     AttributeType,\n   *   value    AttributeValue\n   * }\n   *\n   * AttributeType ::= OBJECT IDENTIFIER\n   *\n   * AttributeValue ::= ANY -- DEFINED BY AttributeType\n   * ```\n   */\n  private val attributeTypeAndValue: BasicDerAdapter<AttributeTypeAndValue> =\n    Adapters.sequence(\n      \"AttributeTypeAndValue\",\n      Adapters.OBJECT_IDENTIFIER,\n      Adapters.any(\n        String::class to Adapters.UTF8_STRING,\n        Nothing::class to Adapters.PRINTABLE_STRING,\n        AnyValue::class to Adapters.ANY_VALUE,\n      ),\n      decompose = {\n        listOf(\n          it.type,\n          it.value,\n        )\n      },\n      construct = {\n        AttributeTypeAndValue(\n          type = it[0] as String,\n          value = it[1],\n        )\n      },\n    )\n\n  /**\n   * ```\n   * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName\n   *\n   * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue\n   * ```\n   */\n  internal val rdnSequence: BasicDerAdapter<List<List<AttributeTypeAndValue>>> =\n    attributeTypeAndValue.asSetOf().asSequenceOf()\n\n  /**\n   * ```\n   * Name ::= CHOICE {\n   *   -- only one possibility for now --\n   *   rdnSequence  RDNSequence\n   * }\n   * ```\n   */\n  internal val name: DerAdapter<Pair<DerAdapter<*>, Any?>> =\n    Adapters.choice(\n      rdnSequence,\n    )\n\n  /**\n   * ```\n   * SubjectPublicKeyInfo ::= SEQUENCE  {\n   *   algorithm            AlgorithmIdentifier,\n   *   subjectPublicKey     BIT STRING\n   * }\n   * ```\n   */\n  internal val subjectPublicKeyInfo: BasicDerAdapter<SubjectPublicKeyInfo> =\n    Adapters.sequence(\n      \"SubjectPublicKeyInfo\",\n      algorithmIdentifier,\n      Adapters.BIT_STRING,\n      decompose = {\n        listOf(\n          it.algorithm,\n          it.subjectPublicKey,\n        )\n      },\n      construct = {\n        SubjectPublicKeyInfo(\n          algorithm = it[0] as AlgorithmIdentifier,\n          subjectPublicKey = it[1] as BitString,\n        )\n      },\n    )\n\n  /**\n   * ```\n   * TBSCertificate ::= SEQUENCE  {\n   *   version         [0]  EXPLICIT Version DEFAULT v1,\n   *   serialNumber         CertificateSerialNumber,\n   *   signature            AlgorithmIdentifier,\n   *   issuer               Name,\n   *   validity             Validity,\n   *   subject              Name,\n   *   subjectPublicKeyInfo SubjectPublicKeyInfo,\n   *   issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3\n   *   subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3\n   *   extensions      [3]  EXPLICIT Extensions OPTIONAL -- If present, version MUST be v3\n   * }\n   * ```\n   */\n  internal val tbsCertificate: BasicDerAdapter<TbsCertificate> =\n    Adapters.sequence(\n      \"TBSCertificate\",\n      Adapters.INTEGER_AS_LONG\n        .withExplicitBox(tag = 0L)\n        // v1 == 0.\n        .optional(defaultValue = 0),\n      Adapters.INTEGER_AS_BIG_INTEGER,\n      algorithmIdentifier,\n      name,\n      validity,\n      name,\n      subjectPublicKeyInfo,\n      Adapters.BIT_STRING.withTag(tag = 1L).optional(),\n      Adapters.BIT_STRING.withTag(tag = 2L).optional(),\n      extension.asSequenceOf().withExplicitBox(tag = 3).optional(defaultValue = listOf()),\n      decompose = {\n        listOf(\n          it.version,\n          it.serialNumber,\n          it.signature,\n          rdnSequence to it.issuer,\n          it.validity,\n          rdnSequence to it.subject,\n          it.subjectPublicKeyInfo,\n          it.issuerUniqueID,\n          it.subjectUniqueID,\n          it.extensions,\n        )\n      },\n      construct = {\n        TbsCertificate(\n          version = it[0] as Long,\n          serialNumber = it[1] as BigInteger,\n          signature = it[2] as AlgorithmIdentifier,\n          issuer = (it[3] as Pair<*, *>).second as List<List<AttributeTypeAndValue>>,\n          validity = it[4] as Validity,\n          subject = (it[5] as Pair<*, *>).second as List<List<AttributeTypeAndValue>>,\n          subjectPublicKeyInfo = it[6] as SubjectPublicKeyInfo,\n          issuerUniqueID = it[7] as BitString?,\n          subjectUniqueID = it[8] as BitString?,\n          extensions = it[9] as List<Extension>,\n        )\n      },\n    )\n\n  /**\n   * ```\n   * Certificate ::= SEQUENCE  {\n   *   tbsCertificate       TBSCertificate,\n   *   signatureAlgorithm   AlgorithmIdentifier,\n   *   signatureValue       BIT STRING\n   * }\n   * ```\n   */\n  internal val certificate: BasicDerAdapter<Certificate> =\n    Adapters.sequence(\n      \"Certificate\",\n      tbsCertificate,\n      algorithmIdentifier,\n      Adapters.BIT_STRING,\n      decompose = {\n        listOf(\n          it.tbsCertificate,\n          it.signatureAlgorithm,\n          it.signatureValue,\n        )\n      },\n      construct = {\n        Certificate(\n          tbsCertificate = it[0] as TbsCertificate,\n          signatureAlgorithm = it[1] as AlgorithmIdentifier,\n          signatureValue = it[2] as BitString,\n        )\n      },\n    )\n\n  /**\n   * ```\n   * Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2)\n   *\n   * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier\n   *\n   * PrivateKey ::= OCTET STRING\n   *\n   * OneAsymmetricKey ::= SEQUENCE {\n   *   version                   Version,\n   *   privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,\n   *   privateKey                PrivateKey,\n   *   attributes            [0] Attributes OPTIONAL,\n   *   ...,\n   *   [[2: publicKey        [1] PublicKey OPTIONAL ]],\n   *   ...\n   * }\n   *\n   * PrivateKeyInfo ::= OneAsymmetricKey\n   * ```\n   */\n  internal val privateKeyInfo: BasicDerAdapter<PrivateKeyInfo> =\n    Adapters.sequence(\n      \"PrivateKeyInfo\",\n      Adapters.INTEGER_AS_LONG,\n      algorithmIdentifier,\n      Adapters.OCTET_STRING,\n      decompose = {\n        listOf(\n          it.version,\n          it.algorithmIdentifier,\n          it.privateKey,\n        )\n      },\n      construct = {\n        PrivateKeyInfo(\n          version = it[0] as Long,\n          algorithmIdentifier = it[1] as AlgorithmIdentifier,\n          privateKey = it[2] as ByteString,\n        )\n      },\n    )\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/DerAdapter.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport okio.Buffer\nimport okio.ByteString\n\n/**\n * Encode and decode a model object like a [Long] or [Certificate] as DER bytes.\n */\ninternal interface DerAdapter<T> {\n  /** Returns true if this adapter can read [header] in a choice. */\n  fun matches(header: DerHeader): Boolean\n\n  /**\n   * Returns a value from this adapter.\n   *\n   * This must always return a value, though it doesn't necessarily need to consume data from\n   * [reader]. For example, if the reader's peeked tag isn't readable by this adapter, it may return\n   * a default value.\n   *\n   * If this does read a value, it starts with the tag and length, and reads an entire value,\n   * including any potential composed values.\n   *\n   * If there's nothing to read and no default value, this will throw an exception.\n   */\n  fun fromDer(reader: DerReader): T\n\n  fun fromDer(byteString: ByteString): T {\n    val buffer = Buffer().write(byteString)\n    val reader = DerReader(buffer)\n    return fromDer(reader)\n  }\n\n  /**\n   * Writes [value] to this adapter, unless it is the default value and can be safely omitted.\n   *\n   * If this does write a value, it will write a tag and a length and a full value.\n   */\n  fun toDer(\n    writer: DerWriter,\n    value: T,\n  )\n\n  fun toDer(value: T): ByteString {\n    val buffer = Buffer()\n    val writer = DerWriter(buffer)\n    toDer(writer, value)\n    return buffer.readByteString()\n  }\n\n  /**\n   * Returns an adapter that expects this value wrapped by another value. Typically this occurs\n   * when a value has both a context or application tag and a universal tag.\n   *\n   * Use this for EXPLICIT tag types:\n   *\n   * ```\n   * [5] EXPLICIT UTF8String\n   * ```\n   *\n   * @param forceConstructed non-null to set the constructed bit to the specified value, even if the\n   *     writing process sets something else. This is used to encode SEQUENCES in values that are\n   *     declared to have non-constructed values, like OCTET STRING values.\n   */\n  @Suppress(\"UNCHECKED_CAST\") // read() produces a single element of the expected type.\n  fun withExplicitBox(\n    tagClass: Int = DerHeader.TAG_CLASS_CONTEXT_SPECIFIC,\n    tag: Long,\n    forceConstructed: Boolean? = null,\n  ): BasicDerAdapter<T> {\n    val codec =\n      object : BasicDerAdapter.Codec<T> {\n        override fun decode(reader: DerReader): T = fromDer(reader)\n\n        override fun encode(\n          writer: DerWriter,\n          value: T,\n        ) {\n          toDer(writer, value)\n          if (forceConstructed != null) {\n            writer.constructed = forceConstructed\n          }\n        }\n      }\n\n    return BasicDerAdapter(\n      name = \"EXPLICIT\",\n      tagClass = tagClass,\n      tag = tag,\n      codec = codec,\n    )\n  }\n\n  /** Returns an adapter that returns a list of values of this type. */\n  fun asSequenceOf(\n    name: String = \"SEQUENCE OF\",\n    tagClass: Int = DerHeader.TAG_CLASS_UNIVERSAL,\n    tag: Long = 16L,\n  ): BasicDerAdapter<List<T>> {\n    val codec =\n      object : BasicDerAdapter.Codec<List<T>> {\n        override fun encode(\n          writer: DerWriter,\n          value: List<T>,\n        ) {\n          for (v in value) {\n            toDer(writer, v)\n          }\n        }\n\n        override fun decode(reader: DerReader): List<T> {\n          val result = mutableListOf<T>()\n          while (reader.hasNext()) {\n            result += fromDer(reader)\n          }\n          return result\n        }\n      }\n\n    return BasicDerAdapter(name, tagClass, tag, codec)\n  }\n\n  /** Returns an adapter that returns a set of values of this type. */\n  fun asSetOf(): BasicDerAdapter<List<T>> =\n    asSequenceOf(\n      name = \"SET OF\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 17L,\n    )\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/DerHeader.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\n/**\n * The first two bytes of each value is a header that includes its tag (field ID) and length.\n */\ninternal data class DerHeader(\n  /**\n   * Namespace of the tag.\n   *\n   * This value is encoded in bits 7 and 8 of the first byte of each value.\n   *\n   * ```\n   * 0b00xxxxxx Universal\n   * 0b01xxxxxx Application\n   * 0b10xxxxxx Context-Specific\n   * 0b11xxxxxx Private\n   * ```\n   */\n  var tagClass: Int,\n  /** Identifies which member in the ASN.1 schema the field holds. */\n  var tag: Long,\n  /**\n   * If the constructed bit is set it indicates that the value is composed of other values that have\n   * their own headers.\n   *\n   * This value is encoded in bit 6 of the first byte of each value.\n   *\n   * ```\n   * 0bxx0xxxxx Primitive\n   * 0bxx1xxxxx Constructed\n   * ```\n   */\n  var constructed: Boolean,\n  /** Length of the message in bytes, or -1L if its length is unknown at the time of encoding. */\n  var length: Long,\n) {\n  val isEndOfData: Boolean\n    get() = tagClass == TAG_CLASS_UNIVERSAL && tag == TAG_END_OF_CONTENTS\n\n  // Avoid Long.hashCode(long) which isn't available on Android 5.\n  override fun hashCode(): Int {\n    var result = 0\n    result = 31 * result + tagClass\n    result = 31 * result + tag.toInt()\n    result = 31 * result + (if (constructed) 0 else 1)\n    result = 31 * result + length.toInt()\n    return result\n  }\n\n  override fun toString(): String = \"$tagClass/$tag\"\n\n  companion object {\n    const val TAG_CLASS_UNIVERSAL = 0b0000_0000\n    const val TAG_CLASS_APPLICATION = 0b0100_0000\n    const val TAG_CLASS_CONTEXT_SPECIFIC = 0b1000_0000\n    const val TAG_CLASS_PRIVATE = 0b1100_0000\n\n    const val TAG_END_OF_CONTENTS = 0L\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/DerReader.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport java.math.BigInteger\nimport java.net.ProtocolException\nimport okio.Buffer\nimport okio.BufferedSource\nimport okio.ByteString\nimport okio.ForwardingSource\nimport okio.Source\nimport okio.buffer\n\n/**\n * Streaming decoder of data encoded following Abstract Syntax Notation One (ASN.1). There are\n * multiple variants of ASN.1, including:\n *\n *  * DER: Distinguished Encoding Rules. This further constrains ASN.1 for deterministic encoding.\n *  * BER: Basic Encoding Rules.\n *\n * This class was implemented according to the [X.690 spec][[x690]], and under the advice of\n * [Lets Encrypt's ASN.1 and DER][asn1_and_der] guide.\n *\n * [x690]: https://www.itu.int/rec/T-REC-X.690\n * [asn1_and_der]: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/\n */\ninternal class DerReader(\n  source: Source,\n) {\n  private val countingSource: CountingSource = CountingSource(source)\n  private val source: BufferedSource = countingSource.buffer()\n\n  /** Total bytes read thus far. */\n  private val byteCount: Long\n    get() = countingSource.bytesRead - source.buffer.size\n\n  /** How many bytes to read before [peekHeader] should return false, or -1L for no limit. */\n  private var limit = -1L\n\n  /** Type hints scoped to the call stack, manipulated with [withTypeHint]. */\n  private val typeHintStack = mutableListOf<Any?>()\n\n  /**\n   * The type hint for the current object. Used to pick adapters based on other fields, such as\n   * in extensions which have different types depending on their extension ID.\n   */\n  var typeHint: Any?\n    get() = typeHintStack.lastOrNull()\n    set(value) {\n      typeHintStack[typeHintStack.size - 1] = value\n    }\n\n  /** Names leading to the current location in the ASN.1 document. */\n  private val path = mutableListOf<String>()\n\n  private var constructed = false\n\n  private var peekedHeader: DerHeader? = null\n\n  private val bytesLeft: Long\n    get() = if (limit == -1L) -1L else (limit - byteCount)\n\n  fun hasNext(): Boolean = peekHeader() != null\n\n  /**\n   * Returns the next header to process unless this scope is exhausted.\n   *\n   * This returns null if:\n   *\n   *  * The stream is exhausted.\n   *  * We've read all of the bytes of an object whose length is known.\n   *  * We've reached the [DerHeader.TAG_END_OF_CONTENTS] of an object whose length is unknown.\n   */\n  fun peekHeader(): DerHeader? {\n    var result = peekedHeader\n\n    if (result == null) {\n      result = readHeader()\n      peekedHeader = result\n    }\n\n    if (result.isEndOfData) return null\n\n    return result\n  }\n\n  /**\n   * Consume the next header in the stream and return it. If there is no header to read because we\n   * have reached a limit, this returns [END_OF_DATA].\n   */\n  internal fun readHeader(): DerHeader {\n    require(peekedHeader == null)\n\n    // We've hit a local limit.\n    if (byteCount == limit) return END_OF_DATA\n\n    // We've exhausted the source stream.\n    if (limit == -1L && source.exhausted()) return END_OF_DATA\n\n    // Read the tag.\n    val tagAndClass = source.readByte().toInt() and 0xff\n    val tagClass = tagAndClass and 0b1100_0000\n    val constructed = (tagAndClass and 0b0010_0000) == 0b0010_0000\n    val tag =\n      when (val tag0 = tagAndClass and 0b0001_1111) {\n        0b0001_1111 -> readVariableLengthLong()\n        else -> tag0.toLong()\n      }\n\n    // Read the length.\n    val length0 = source.readByte().toInt() and 0xff\n    val length =\n      when {\n        length0 == 0b1000_0000 -> {\n          throw ProtocolException(\"indefinite length not permitted for DER\")\n        }\n\n        (length0 and 0b1000_0000) == 0b1000_0000 -> {\n          // Length specified over multiple bytes.\n          val lengthBytes = length0 and 0b0111_1111\n          if (lengthBytes > 8) {\n            throw ProtocolException(\"length encoded with more than 8 bytes is not supported\")\n          }\n\n          var lengthBits = source.readByte().toLong() and 0xff\n          if (lengthBits == 0L || lengthBytes == 1 && lengthBits and 0b1000_0000 == 0L) {\n            throw ProtocolException(\"invalid encoding for length\")\n          }\n\n          for (i in 1 until lengthBytes) {\n            lengthBits = lengthBits shl 8\n            lengthBits += source.readByte().toInt() and 0xff\n          }\n\n          if (lengthBits < 0) throw ProtocolException(\"length > Long.MAX_VALUE\")\n\n          lengthBits\n        }\n\n        else -> {\n          // Length is 127 or fewer bytes.\n          (length0 and 0b0111_1111).toLong()\n        }\n      }\n\n    // Note that this may be be an encoded \"end of data\" header.\n    return DerHeader(tagClass, tag, constructed, length)\n  }\n\n  /**\n   * Consume a header and execute [block], which should consume the entire value described by the\n   * header. It is an error to not consume a full value in [block].\n   */\n  internal inline fun <T> read(\n    name: String?,\n    block: (DerHeader) -> T,\n  ): T {\n    if (!hasNext()) throw ProtocolException(\"expected a value\")\n\n    val header = peekedHeader!!\n    peekedHeader = null\n\n    val pushedLimit = limit\n    val pushedConstructed = constructed\n\n    val newLimit = if (header.length != -1L) byteCount + header.length else -1L\n    if (pushedLimit != -1L && newLimit > pushedLimit) {\n      throw ProtocolException(\"enclosed object too large\")\n    }\n\n    limit = newLimit\n    constructed = header.constructed\n    if (name != null) path += name\n    try {\n      val result = block(header)\n\n      // The object processed bytes beyond its range.\n      if (newLimit != -1L && byteCount > newLimit) {\n        throw ProtocolException(\"unexpected byte count at $this\")\n      }\n\n      return result\n    } finally {\n      peekedHeader = null\n      limit = pushedLimit\n      constructed = pushedConstructed\n      if (name != null) path.removeAt(path.size - 1)\n    }\n  }\n\n  /**\n   * Execute [block] with a new namespace for type hints. Type hints from the enclosing type are no\n   * longer usable by the current type's members.\n   */\n  fun <T> withTypeHint(block: () -> T): T {\n    typeHintStack.add(null)\n    try {\n      return block()\n    } finally {\n      typeHintStack.removeAt(typeHintStack.size - 1)\n    }\n  }\n\n  fun readBoolean(): Boolean {\n    if (bytesLeft != 1L) throw ProtocolException(\"unexpected length: $bytesLeft at $this\")\n    return source.readByte().toInt() != 0\n  }\n\n  fun readBigInteger(): BigInteger {\n    if (bytesLeft == 0L) throw ProtocolException(\"unexpected length: $bytesLeft at $this\")\n    val byteArray = source.readByteArray(bytesLeft)\n    return BigInteger(byteArray)\n  }\n\n  fun readLong(): Long {\n    if (bytesLeft !in 1..8) throw ProtocolException(\"unexpected length: $bytesLeft at $this\")\n\n    var result = source.readByte().toLong() // No \"and 0xff\" because this is a signed value!\n    while (byteCount < limit) {\n      result = result shl 8\n      result += source.readByte().toInt() and 0xff\n    }\n    return result\n  }\n\n  fun readBitString(): BitString {\n    if (bytesLeft == -1L || constructed) {\n      throw ProtocolException(\"constructed bit strings not supported for DER\")\n    }\n    if (bytesLeft < 1) {\n      throw ProtocolException(\"malformed bit string\")\n    }\n    val unusedBitCount = source.readByte().toInt() and 0xff\n    val byteString = source.readByteString(bytesLeft)\n    return BitString(byteString, unusedBitCount)\n  }\n\n  fun readOctetString(): ByteString {\n    if (bytesLeft == -1L || constructed) {\n      throw ProtocolException(\"constructed octet strings not supported for DER\")\n    }\n    return source.readByteString(bytesLeft)\n  }\n\n  fun readUtf8String(): String {\n    if (bytesLeft == -1L || constructed) {\n      throw ProtocolException(\"constructed strings not supported for DER\")\n    }\n    return source.readUtf8(bytesLeft)\n  }\n\n  fun readObjectIdentifier(): String {\n    val result = Buffer()\n    val dot = '.'.code.toByte().toInt()\n    when (val xy = readVariableLengthLong()) {\n      in 0L until 40L -> {\n        result.writeDecimalLong(0)\n        result.writeByte(dot)\n        result.writeDecimalLong(xy)\n      }\n\n      in 40L until 80L -> {\n        result.writeDecimalLong(1)\n        result.writeByte(dot)\n        result.writeDecimalLong(xy - 40L)\n      }\n\n      else -> {\n        result.writeDecimalLong(2)\n        result.writeByte(dot)\n        result.writeDecimalLong(xy - 80L)\n      }\n    }\n    while (byteCount < limit) {\n      result.writeByte(dot)\n      result.writeDecimalLong(readVariableLengthLong())\n    }\n    return result.readUtf8()\n  }\n\n  fun readRelativeObjectIdentifier(): String {\n    val result = Buffer()\n    val dot = '.'.code.toByte().toInt()\n    while (byteCount < limit) {\n      if (result.size > 0) {\n        result.writeByte(dot)\n      }\n      result.writeDecimalLong(readVariableLengthLong())\n    }\n    return result.readUtf8()\n  }\n\n  /** Used for tags and subidentifiers. */\n  private fun readVariableLengthLong(): Long {\n    // TODO(jwilson): detect overflow.\n    var result = 0L\n    while (true) {\n      val byteN = source.readByte().toLong() and 0xff\n      if ((byteN and 0b1000_0000L) == 0b1000_0000L) {\n        result = (result + (byteN and 0b0111_1111)) shl 7\n      } else {\n        return result + byteN\n      }\n    }\n  }\n\n  /** Read a value as bytes without interpretation of its contents. */\n  fun readUnknown(): ByteString = source.readByteString(bytesLeft)\n\n  override fun toString(): String = path.joinToString(separator = \" / \")\n\n  companion object {\n    /**\n     * A synthetic value that indicates there's no more bytes. Values with equivalent data may also\n     * show up in ASN.1 streams to also indicate the end of SEQUENCE, SET or other constructed\n     * value.\n     */\n    private val END_OF_DATA =\n      DerHeader(\n        tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n        tag = DerHeader.TAG_END_OF_CONTENTS,\n        constructed = false,\n        length = -1L,\n      )\n  }\n\n  /** A source that keeps track of how many bytes it's consumed. */\n  private class CountingSource(\n    source: Source,\n  ) : ForwardingSource(source) {\n    var bytesRead = 0L\n\n    override fun read(\n      sink: Buffer,\n      byteCount: Long,\n    ): Long {\n      val result = delegate.read(sink, byteCount)\n      if (result == -1L) return -1L\n      bytesRead += result\n      return result\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/DerWriter.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport java.math.BigInteger\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.ByteString\n\ninternal class DerWriter(\n  sink: BufferedSink,\n) {\n  /** A stack of buffers that will be concatenated once we know the length of each. */\n  private val stack = mutableListOf(sink)\n\n  /** Type hints scoped to the call stack, manipulated with [pushTypeHint] and [popTypeHint]. */\n  private val typeHintStack = mutableListOf<Any?>()\n\n  /**\n   * The type hint for the current object. Used to pick adapters based on other fields, such as\n   * in extensions which have different types depending on their extension ID.\n   */\n  var typeHint: Any?\n    get() = typeHintStack.lastOrNull()\n    set(value) {\n      typeHintStack[typeHintStack.size - 1] = value\n    }\n\n  /** Names leading to the current location in the ASN.1 document. */\n  private val path = mutableListOf<String>()\n\n  /**\n   * False unless we made a recursive call to [write] at the current stack frame. The explicit box\n   * adapter can clear this to synthesize non-constructed values that are embedded in octet strings.\n   */\n  var constructed = false\n\n  fun write(\n    name: String,\n    tagClass: Int,\n    tag: Long,\n    block: (BufferedSink) -> Unit,\n  ) {\n    val constructedBit: Int\n    val content = Buffer()\n\n    stack.add(content)\n    constructed = false // The enclosed object written in block() is not constructed.\n    path += name\n    try {\n      block(content)\n      constructedBit = if (constructed) 0b0010_0000 else 0\n      constructed = true // The enclosing object is constructed.\n    } finally {\n      stack.removeAt(stack.size - 1)\n      path.removeAt(path.size - 1)\n    }\n\n    val sink = sink()\n\n    // Write the tagClass, tag, and constructed bit. This takes 1 byte if tag is less than 31.\n    if (tag < 31) {\n      val byte0 = tagClass or constructedBit or tag.toInt()\n      sink.writeByte(byte0)\n    } else {\n      val byte0 = tagClass or constructedBit or 0b0001_1111\n      sink.writeByte(byte0)\n      writeVariableLengthLong(tag)\n    }\n\n    // Write the length. This takes 1 byte if length is less than 128.\n    val length = content.size\n    if (length < 128) {\n      sink.writeByte(length.toInt())\n    } else {\n      // count how many bytes we'll need to express the length.\n      val lengthBitCount = 64 - java.lang.Long.numberOfLeadingZeros(length)\n      val lengthByteCount = (lengthBitCount + 7) / 8\n      sink.writeByte(0b1000_0000 or lengthByteCount)\n      for (shift in (lengthByteCount - 1) * 8 downTo 0 step 8) {\n        sink.writeByte((length shr shift).toInt())\n      }\n    }\n\n    // Write the payload.\n    sink.writeAll(content)\n  }\n\n  /**\n   * Execute [block] with a new namespace for type hints. Type hints from the enclosing type are no\n   * longer usable by the current type's members.\n   */\n  fun <T> withTypeHint(block: () -> T): T {\n    typeHintStack.add(null)\n    try {\n      return block()\n    } finally {\n      typeHintStack.removeAt(typeHintStack.size - 1)\n    }\n  }\n\n  private fun sink(): BufferedSink = stack[stack.size - 1]\n\n  fun writeBoolean(b: Boolean) {\n    sink().writeByte(if (b) -1 else 0)\n  }\n\n  fun writeBigInteger(value: BigInteger) {\n    sink().write(value.toByteArray())\n  }\n\n  fun writeLong(v: Long) {\n    val sink = sink()\n\n    val lengthBitCount: Int =\n      if (v < 0L) {\n        65 - java.lang.Long.numberOfLeadingZeros(v xor -1L)\n      } else {\n        65 - java.lang.Long.numberOfLeadingZeros(v)\n      }\n\n    val lengthByteCount = (lengthBitCount + 7) / 8\n    for (shift in (lengthByteCount - 1) * 8 downTo 0 step 8) {\n      sink.writeByte((v shr shift).toInt())\n    }\n  }\n\n  fun writeBitString(bitString: BitString) {\n    val sink = sink()\n    sink.writeByte(bitString.unusedBitsCount)\n    sink.write(bitString.byteString)\n  }\n\n  fun writeOctetString(byteString: ByteString) {\n    sink().write(byteString)\n  }\n\n  fun writeUtf8(value: String) {\n    sink().writeUtf8(value)\n  }\n\n  fun writeObjectIdentifier(s: String) {\n    val utf8 = Buffer().writeUtf8(s)\n    val v1 = utf8.readDecimalLong()\n    require(utf8.readByte() == '.'.code.toByte())\n    val v2 = utf8.readDecimalLong()\n    writeVariableLengthLong(v1 * 40 + v2)\n\n    while (!utf8.exhausted()) {\n      require(utf8.readByte() == '.'.code.toByte())\n      val vN = utf8.readDecimalLong()\n      writeVariableLengthLong(vN)\n    }\n  }\n\n  fun writeRelativeObjectIdentifier(s: String) {\n    // Add a leading dot so each subidentifier has a dot prefix.\n    val utf8 =\n      Buffer()\n        .writeByte('.'.code.toByte().toInt())\n        .writeUtf8(s)\n\n    while (!utf8.exhausted()) {\n      require(utf8.readByte() == '.'.code.toByte())\n      val vN = utf8.readDecimalLong()\n      writeVariableLengthLong(vN)\n    }\n  }\n\n  /** Used for tags and subidentifiers. */\n  private fun writeVariableLengthLong(v: Long) {\n    val sink = sink()\n    val bitCount = 64 - java.lang.Long.numberOfLeadingZeros(v)\n    val byteCount = (bitCount + 6) / 7\n    for (shift in (byteCount - 1) * 7 downTo 0 step 7) {\n      val lastBit = if (shift == 0) 0 else 0b1000_0000\n      sink.writeByte(((v shr shift) and 0b0111_1111).toInt() or lastBit)\n    }\n  }\n\n  override fun toString(): String = path.joinToString(separator = \" / \")\n}\n"
  },
  {
    "path": "okhttp-tls/src/main/kotlin/okhttp3/tls/internal/der/ObjectIdentifiers.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\n/** ASN.1 object identifiers used internally by this implementation. */\ninternal object ObjectIdentifiers {\n  const val EC_PUBLIC_KEY = \"1.2.840.10045.2.1\"\n  const val SHA256_WITH_ECDSA = \"1.2.840.10045.4.3.2\"\n  const val RSA_ENCRYPTION = \"1.2.840.113549.1.1.1\"\n  const val SHA256_WITH_RSA_ENCRYPTION = \"1.2.840.113549.1.1.11\"\n  const val SUBJECT_ALTERNATIVE_NAME = \"2.5.29.17\"\n  const val BASIC_CONSTRAINTS = \"2.5.29.19\"\n  const val COMMON_NAME = \"2.5.4.3\"\n  const val ORGANIZATIONAL_UNIT_NAME = \"2.5.4.11\"\n}\n"
  },
  {
    "path": "okhttp-tls/src/test/java/okhttp3/tls/CertificatesJavaTest.java",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls;\n\nimport java.security.cert.X509Certificate;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\npublic class CertificatesJavaTest {\n  @Test\n  public void testRoundtrip() {\n    String certificateString = \"\"\n      + \"-----BEGIN CERTIFICATE-----\\n\"\n      + \"MIIBmjCCAQOgAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhjYXNo\\n\"\n      + \"LmFwcDAeFw03MDAxMDEwMDAwMDBaFw03MDAxMDEwMDAwMDFaMBMxETAPBgNVBAMT\\n\"\n      + \"CGNhc2guYXBwMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCApFHhtrLan28q\\n\"\n      + \"+oMolZuaTfWBA0V5aMIvq32BsloQu6LlvX1wJ4YEoUCjDlPOtpht7XLbUmBnbIzN\\n\"\n      + \"89XK4UJVM6Sqp3K88Km8z7gMrdrfTom/274wL25fICR+yDEQ5fUVYBmJAKXZF1ao\\n\"\n      + \"I0mIoEx0xFsQhIJ637v2MxJDupd61wIDAQABMA0GCSqGSIb3DQEBCwUAA4GBADam\\n\"\n      + \"UVwKh5Ry7es3OxtY3IgQunPUoLc0Gw71gl9Z+7t2FJ5VkcI5gWfutmdxZ2bDXCI8\\n\"\n      + \"8V0vxo1pHXnbBrnxhS/Z3TBerw8RyQqcaWOdp+pBXyIWmR+jHk9cHZCqQveTIBsY\\n\"\n      + \"jaA9VEhgdaVhxBsT2qzUNDsXlOzGsliznDfoqETb\\n\"\n      + \"-----END CERTIFICATE-----\\n\";\n\n    X509Certificate certificate =\n        Certificates.decodeCertificatePem(certificateString);\n\n    assertEquals(certificateString, Certificates.certificatePem(certificate));\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/test/java/okhttp3/tls/CertificatesTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\nclass CertificatesTest {\n  @Test fun testRoundtrip() {\n    val certificateString =\n      \"\"\"\n      -----BEGIN CERTIFICATE-----\n      MIIBmjCCAQOgAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhjYXNo\n      LmFwcDAeFw03MDAxMDEwMDAwMDBaFw03MDAxMDEwMDAwMDFaMBMxETAPBgNVBAMT\n      CGNhc2guYXBwMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCApFHhtrLan28q\n      +oMolZuaTfWBA0V5aMIvq32BsloQu6LlvX1wJ4YEoUCjDlPOtpht7XLbUmBnbIzN\n      89XK4UJVM6Sqp3K88Km8z7gMrdrfTom/274wL25fICR+yDEQ5fUVYBmJAKXZF1ao\n      I0mIoEx0xFsQhIJ637v2MxJDupd61wIDAQABMA0GCSqGSIb3DQEBCwUAA4GBADam\n      UVwKh5Ry7es3OxtY3IgQunPUoLc0Gw71gl9Z+7t2FJ5VkcI5gWfutmdxZ2bDXCI8\n      8V0vxo1pHXnbBrnxhS/Z3TBerw8RyQqcaWOdp+pBXyIWmR+jHk9cHZCqQveTIBsY\n      jaA9VEhgdaVhxBsT2qzUNDsXlOzGsliznDfoqETb\n      -----END CERTIFICATE-----\n      \n      \"\"\".trimIndent()\n    val certificate = certificateString.decodeCertificatePem()\n    assertEquals(certificateString, certificate.certificatePem())\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/test/java/okhttp3/tls/HandshakeCertificatesTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.matchesPredicate\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.ServerSocket\nimport java.security.PrivateKey\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport java.util.concurrent.Future\nimport javax.net.ServerSocketFactory\nimport javax.net.SocketFactory\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Handshake\nimport okhttp3.Handshake.Companion.handshake\nimport okhttp3.TestUtil.threadFactory\nimport okhttp3.internal.closeQuietly\nimport okhttp3.testing.PlatformRule\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass HandshakeCertificatesTest {\n  @RegisterExtension\n  var platform = PlatformRule()\n\n  private lateinit var executorService: ExecutorService\n\n  private var serverSocket: ServerSocket? = null\n\n  @BeforeEach fun setUp() {\n    executorService = Executors.newCachedThreadPool(threadFactory(\"HandshakeCertificatesTest\"))\n  }\n\n  @AfterEach fun tearDown() {\n    executorService.shutdown()\n    serverSocket?.closeQuietly()\n  }\n\n  @Test fun clientAndServer() {\n    platform.assumeNotConscrypt()\n    platform.assumeNotBouncyCastle()\n\n    val clientRoot =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(1)\n        .build()\n    val clientIntermediate =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .signedBy(clientRoot)\n        .build()\n    val clientCertificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(clientIntermediate)\n        .build()\n    val serverRoot =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(1)\n        .build()\n    val serverIntermediate =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .signedBy(serverRoot)\n        .build()\n    val serverCertificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(serverIntermediate)\n        .build()\n    val server =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(clientRoot.certificate)\n        .heldCertificate(serverCertificate, serverIntermediate.certificate)\n        .build()\n    val client =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(serverRoot.certificate)\n        .heldCertificate(clientCertificate, clientIntermediate.certificate)\n        .build()\n    val serverAddress = startTlsServer()\n    val serverHandshakeFuture = doServerHandshake(server)\n    val clientHandshakeFuture = doClientHandshake(client, serverAddress)\n    val serverHandshake = serverHandshakeFuture.get()\n    assertThat(listOf(clientCertificate.certificate, clientIntermediate.certificate))\n      .isEqualTo(serverHandshake.peerCertificates)\n    assertThat(listOf(serverCertificate.certificate, serverIntermediate.certificate))\n      .isEqualTo(serverHandshake.localCertificates)\n    val clientHandshake = clientHandshakeFuture.get()\n    assertThat(listOf(serverCertificate.certificate, serverIntermediate.certificate))\n      .isEqualTo(clientHandshake.peerCertificates)\n    assertThat(listOf(clientCertificate.certificate, clientIntermediate.certificate))\n      .isEqualTo(clientHandshake.localCertificates)\n  }\n\n  @Test fun keyManager() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(1)\n        .build()\n    val intermediate =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .signedBy(root)\n        .build()\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(intermediate)\n        .build()\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(root.certificate) // BouncyCastle requires at least one\n        .heldCertificate(certificate, intermediate.certificate)\n        .build()\n    assertPrivateKeysEquals(\n      certificate.keyPair.private,\n      handshakeCertificates.keyManager.getPrivateKey(\"private\"),\n    )\n    assertThat(handshakeCertificates.keyManager.getCertificateChain(\"private\").toList())\n      .isEqualTo(listOf(certificate.certificate, intermediate.certificate))\n  }\n\n  @Test fun platformTrustedCertificates() {\n    val handshakeCertificates =\n      HandshakeCertificates\n        .Builder()\n        .addPlatformTrustedCertificates()\n        .build()\n    val acceptedIssuers = handshakeCertificates.trustManager.acceptedIssuers\n    val names =\n      acceptedIssuers\n        .map { it.subjectDN.name }\n        .toSet()\n\n    // It's safe to assume all platforms will have a major Internet certificate issuer.\n    val majorIssuers =\n      listOf(\n        \"DigiCert\",\n        \"Let's Encrypt\",\n        \"ISRG\", // Internet Security Research Group (Let's Encrypt parent)\n        \"GlobalSign\",\n        \"Comodo\",\n        \"Sectigo\",\n        \"GeoTrust\",\n        \"Entrust\",\n      )\n    assertThat(names).matchesPredicate { strings ->\n      strings.any { name -> majorIssuers.any { issuer -> name.contains(issuer, ignoreCase = true) } }\n    }\n  }\n\n  private fun startTlsServer(): InetSocketAddress {\n    val serverSocketFactory = ServerSocketFactory.getDefault()\n    serverSocket = serverSocketFactory.createServerSocket()\n    val serverAddress = InetAddress.getByName(\"localhost\")\n    serverSocket!!.bind(InetSocketAddress(serverAddress, 0), 50)\n    return InetSocketAddress(serverAddress, serverSocket!!.localPort)\n  }\n\n  private fun doServerHandshake(server: HandshakeCertificates): Future<Handshake> {\n    return executorService.submit<Handshake> {\n      serverSocket!!.accept().use { rawSocket ->\n        val sslSocket =\n          server.sslSocketFactory().createSocket(\n            rawSocket,\n            rawSocket.inetAddress.hostAddress,\n            rawSocket.port,\n            true,\n          ) as SSLSocket\n        sslSocket.use {\n          sslSocket.useClientMode = false\n          sslSocket.wantClientAuth = true\n          sslSocket.startHandshake()\n          return@submit sslSocket.session.handshake()\n        }\n      }\n    }\n  }\n\n  private fun doClientHandshake(\n    client: HandshakeCertificates,\n    serverAddress: InetSocketAddress,\n  ): Future<Handshake> {\n    return executorService.submit<Handshake> {\n      SocketFactory.getDefault().createSocket().use { rawSocket ->\n        rawSocket.connect(serverAddress)\n        val sslSocket =\n          client.sslSocketFactory().createSocket(\n            rawSocket,\n            rawSocket.inetAddress.hostAddress,\n            rawSocket.port,\n            true,\n          ) as SSLSocket\n        sslSocket.use {\n          sslSocket.startHandshake()\n          return@submit sslSocket.session.handshake()\n        }\n      }\n    }\n  }\n\n  private fun assertPrivateKeysEquals(\n    expected: PrivateKey,\n    actual: PrivateKey,\n  ) {\n    assertThat(actual.encoded.toByteString()).isEqualTo(expected.encoded.toByteString())\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/test/java/okhttp3/tls/HeldCertificateTest.kt",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactly\nimport assertk.assertions.isCloseTo\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.matches\nimport java.math.BigInteger\nimport java.security.KeyFactory\nimport java.security.spec.PKCS8EncodedKeySpec\nimport java.security.spec.X509EncodedKeySpec\nimport java.util.concurrent.TimeUnit\nimport okhttp3.testing.PlatformRule\nimport okhttp3.tls.HeldCertificate.Companion.decode\nimport okio.ByteString.Companion.decodeBase64\nimport org.bouncycastle.asn1.x509.GeneralName\nimport org.junit.jupiter.api.Assertions.fail\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\nclass HeldCertificateTest {\n  @RegisterExtension\n  var platform = PlatformRule()\n\n  @Test\n  fun defaultCertificate() {\n    val now = System.currentTimeMillis()\n    val heldCertificate = HeldCertificate.Builder().build()\n    val certificate = heldCertificate.certificate\n    assertThat(certificate.getSubjectX500Principal().name, \"self-signed\")\n      .isEqualTo(certificate.getIssuerX500Principal().name)\n    assertThat(certificate.getIssuerX500Principal().name).matches(Regex(\"CN=[0-9a-f-]{36}\"))\n    assertThat(certificate.serialNumber).isEqualTo(BigInteger.ONE)\n    assertThat(certificate.subjectAlternativeNames).isNull()\n    val deltaMillis = 1000.0\n    val durationMillis = TimeUnit.MINUTES.toMillis((60 * 24).toLong())\n    assertThat(certificate.notBefore.time.toDouble())\n      .isCloseTo(now.toDouble(), deltaMillis)\n    assertThat(certificate.notAfter.time.toDouble())\n      .isCloseTo(now.toDouble() + durationMillis, deltaMillis)\n  }\n\n  @Test\n  fun customInterval() {\n    // 5 seconds starting on 1970-01-01.\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .validityInterval(5000L, 10000L)\n        .build()\n    val certificate = heldCertificate.certificate\n    assertThat(certificate.notBefore.time).isEqualTo(5000L)\n    assertThat(certificate.notAfter.time).isEqualTo(10000L)\n  }\n\n  @Test\n  fun customDuration() {\n    val now = System.currentTimeMillis()\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .duration(5, TimeUnit.SECONDS)\n        .build()\n    val certificate = heldCertificate.certificate\n    val deltaMillis = 1000.0\n    val durationMillis = 5000L\n    assertThat(certificate.notBefore.time.toDouble())\n      .isCloseTo(now.toDouble(), deltaMillis)\n    assertThat(certificate.notAfter.time.toDouble())\n      .isCloseTo(now.toDouble() + durationMillis, deltaMillis)\n  }\n\n  @Test\n  fun subjectAlternativeNames() {\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .addSubjectAlternativeName(\"1.1.1.1\")\n        .addSubjectAlternativeName(\"cash.app\")\n        .build()\n    val certificate = heldCertificate.certificate\n    assertThat(certificate.subjectAlternativeNames.toList()).containsExactly(\n      listOf(GeneralName.iPAddress, \"1.1.1.1\"),\n      listOf(GeneralName.dNSName, \"cash.app\"),\n    )\n  }\n\n  @Test\n  fun commonName() {\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"cash.app\")\n        .build()\n    val certificate = heldCertificate.certificate\n    assertThat(certificate.getSubjectX500Principal().name).isEqualTo(\"CN=cash.app\")\n  }\n\n  @Test\n  fun organizationalUnit() {\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .commonName(\"cash.app\")\n        .organizationalUnit(\"cash\")\n        .build()\n    val certificate = heldCertificate.certificate\n    assertThat(certificate.getSubjectX500Principal().name).isEqualTo(\n      \"CN=cash.app,OU=cash\",\n    )\n  }\n\n  /** Confirm golden values of encoded PEMs.  */\n  @Test\n  fun pems() {\n    val keyFactory = KeyFactory.getInstance(\"RSA\")\n    val publicKeyBytes =\n      (\n        \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCApFHhtrLan28q+oMolZuaTfWBA0V5aM\" +\n          \"Ivq32BsloQu6LlvX1wJ4YEoUCjDlPOtpht7XLbUmBnbIzN89XK4UJVM6Sqp3K88Km8z7gMrdrfTom/274wL25fICR+\" +\n          \"yDEQ5fUVYBmJAKXZF1aoI0mIoEx0xFsQhIJ637v2MxJDupd61wIDAQAB\"\n      ).decodeBase64()!!\n    val publicKey =\n      keyFactory.generatePublic(\n        X509EncodedKeySpec(publicKeyBytes.toByteArray()),\n      )\n    val privateKeyBytes =\n      (\n        \"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAICkUeG2stqfbyr6gyiVm\" +\n          \"5pN9YEDRXlowi+rfYGyWhC7ouW9fXAnhgShQKMOU862mG3tcttSYGdsjM3z1crhQlUzpKqncrzwqbzPuAyt2t9Oib/\" +\n          \"bvjAvbl8gJH7IMRDl9RVgGYkApdkXVqgjSYigTHTEWxCEgnrfu/YzEkO6l3rXAgMBAAECgYB99mhnB6piADOuddXv6\" +\n          \"26NzUBTr4xbsYRTgSxHzwf50oFTTBSDuW+1IOBVyTWu94SSPyt0LllPbC8Di3sQSTnVGpSqAvEXknBMzIc0UO74Rn9\" +\n          \"p3gZjEenPt1l77fIBa2nK06/rdsJCoE/1P1JSfM9w7LU1RsTmseYMLeJl5F79gQJBAO/BbAKqg1yzK7VijygvBoUrr\" +\n          \"+rt2lbmKgcUQ/rxu8IIQk0M/xgJqSkXDXuOnboGM7sQSKfJAZUtT7xozvLzV7ECQQCJW59w7NIM0qZ/gIX2gcNZr1B\" +\n          \"/V3zcGlolTDciRm+fnKGNt2EEDKnVL3swzbEfTCa48IT0QKgZJqpXZERa26UHAkBLXmiP5f5pk8F3wcXzAeVw06z3k\" +\n          \"1IB41Tu6MX+CyPU+TeudRlz+wV8b0zDvK+EnRKCCbptVFj1Bkt8lQ4JfcnhAkAk2Y3Gz+HySrkcT7Cg12M/NkdUQnZ\" +\n          \"e3jr88pt/+IGNwomc6Wt/mJ4fcWONTkGMcfOZff1NQeNXDAZ6941XCsIVAkASOg02PlVHLidU7mIE65swMM5/RNhS4\" +\n          \"aFjez/MwxFNOHaxc9VgCwYPXCLOtdf7AVovdyG0XWgbUXH+NyxKwboE\"\n      ).decodeBase64()!!\n    val privateKey =\n      keyFactory.generatePrivate(\n        PKCS8EncodedKeySpec(privateKeyBytes.toByteArray()),\n      )\n    val heldCertificate =\n      HeldCertificate\n        .Builder()\n        .keyPair(publicKey, privateKey)\n        .commonName(\"cash.app\")\n        .validityInterval(0L, 1000L)\n        .rsa2048()\n        .build()\n    assertThat(\n      \"\"\"\n      |-----BEGIN CERTIFICATE-----\n      |MIIBmjCCAQOgAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhjYXNo\n      |LmFwcDAeFw03MDAxMDEwMDAwMDBaFw03MDAxMDEwMDAwMDFaMBMxETAPBgNVBAMM\n      |CGNhc2guYXBwMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCApFHhtrLan28q\n      |+oMolZuaTfWBA0V5aMIvq32BsloQu6LlvX1wJ4YEoUCjDlPOtpht7XLbUmBnbIzN\n      |89XK4UJVM6Sqp3K88Km8z7gMrdrfTom/274wL25fICR+yDEQ5fUVYBmJAKXZF1ao\n      |I0mIoEx0xFsQhIJ637v2MxJDupd61wIDAQABMA0GCSqGSIb3DQEBCwUAA4GBADHT\n      |vcjwl9Z4I5Cb2R1y7aaa860HkY2k3ThaDK5OJt6GYqJTA9P3LtX7VwQtL1TWqXGc\n      |+OEfl3zhm0PUqcbckMzhJtqIa7NkDSjNm71BKd843pIhGcEri69DcL/cR8T+eMex\n      |hadh7aGM9OjeL8gznLeq27Ly6Dj7Vkp5OmOrSKfn\n      |-----END CERTIFICATE-----\n      |\n      \"\"\".trimMargin(),\n    ).isEqualTo(heldCertificate.certificatePem())\n    assertThat(\n      \"\"\"\n      |-----BEGIN RSA PRIVATE KEY-----\n      |MIICWwIBAAKBgQCApFHhtrLan28q+oMolZuaTfWBA0V5aMIvq32BsloQu6LlvX1w\n      |J4YEoUCjDlPOtpht7XLbUmBnbIzN89XK4UJVM6Sqp3K88Km8z7gMrdrfTom/274w\n      |L25fICR+yDEQ5fUVYBmJAKXZF1aoI0mIoEx0xFsQhIJ637v2MxJDupd61wIDAQAB\n      |AoGAffZoZweqYgAzrnXV7+tujc1AU6+MW7GEU4EsR88H+dKBU0wUg7lvtSDgVck1\n      |rveEkj8rdC5ZT2wvA4t7EEk51RqUqgLxF5JwTMyHNFDu+EZ/ad4GYxHpz7dZe+3y\n      |AWtpytOv63bCQqBP9T9SUnzPcOy1NUbE5rHmDC3iZeRe/YECQQDvwWwCqoNcsyu1\n      |Yo8oLwaFK6/q7dpW5ioHFEP68bvCCEJNDP8YCakpFw17jp26BjO7EEinyQGVLU+8\n      |aM7y81exAkEAiVufcOzSDNKmf4CF9oHDWa9Qf1d83BpaJUw3IkZvn5yhjbdhBAyp\n      |1S97MM2xH0wmuPCE9ECoGSaqV2REWtulBwJAS15oj+X+aZPBd8HF8wHlcNOs95NS\n      |AeNU7ujF/gsj1Pk3rnUZc/sFfG9Mw7yvhJ0Sggm6bVRY9QZLfJUOCX3J4QJAJNmN\n      |xs/h8kq5HE+woNdjPzZHVEJ2Xt46/PKbf/iBjcKJnOlrf5ieH3FjjU5BjHHzmX39\n      |TUHjVwwGeveNVwrCFQJAEjoNNj5VRy4nVO5iBOubMDDOf0TYUuGhY3s/zMMRTTh2\n      |sXPVYAsGD1wizrXX+wFaL3chtF1oG1Fx/jcsSsG6BA==\n      |-----END RSA PRIVATE KEY-----\n      |\n      \"\"\".trimMargin(),\n    ).isEqualTo(heldCertificate.privateKeyPkcs1Pem())\n    assertThat(\n      \"\"\"\n      |-----BEGIN PRIVATE KEY-----\n      |MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAICkUeG2stqfbyr6\n      |gyiVm5pN9YEDRXlowi+rfYGyWhC7ouW9fXAnhgShQKMOU862mG3tcttSYGdsjM3z\n      |1crhQlUzpKqncrzwqbzPuAyt2t9Oib/bvjAvbl8gJH7IMRDl9RVgGYkApdkXVqgj\n      |SYigTHTEWxCEgnrfu/YzEkO6l3rXAgMBAAECgYB99mhnB6piADOuddXv626NzUBT\n      |r4xbsYRTgSxHzwf50oFTTBSDuW+1IOBVyTWu94SSPyt0LllPbC8Di3sQSTnVGpSq\n      |AvEXknBMzIc0UO74Rn9p3gZjEenPt1l77fIBa2nK06/rdsJCoE/1P1JSfM9w7LU1\n      |RsTmseYMLeJl5F79gQJBAO/BbAKqg1yzK7VijygvBoUrr+rt2lbmKgcUQ/rxu8II\n      |Qk0M/xgJqSkXDXuOnboGM7sQSKfJAZUtT7xozvLzV7ECQQCJW59w7NIM0qZ/gIX2\n      |gcNZr1B/V3zcGlolTDciRm+fnKGNt2EEDKnVL3swzbEfTCa48IT0QKgZJqpXZERa\n      |26UHAkBLXmiP5f5pk8F3wcXzAeVw06z3k1IB41Tu6MX+CyPU+TeudRlz+wV8b0zD\n      |vK+EnRKCCbptVFj1Bkt8lQ4JfcnhAkAk2Y3Gz+HySrkcT7Cg12M/NkdUQnZe3jr8\n      |8pt/+IGNwomc6Wt/mJ4fcWONTkGMcfOZff1NQeNXDAZ6941XCsIVAkASOg02PlVH\n      |LidU7mIE65swMM5/RNhS4aFjez/MwxFNOHaxc9VgCwYPXCLOtdf7AVovdyG0XWgb\n      |UXH+NyxKwboE\n      |-----END PRIVATE KEY-----\n      |\n      \"\"\".trimMargin(),\n    ).isEqualTo(heldCertificate.privateKeyPkcs8Pem())\n  }\n\n  @Test\n  fun ecdsaSignedByRsa() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .rsa2048()\n        .build()\n    val leaf =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .ecdsa256()\n        .signedBy(root)\n        .build()\n    assertThat(root.certificate.sigAlgName).isEqualTo(\"SHA256WITHRSA\", ignoreCase = true)\n    assertThat(leaf.certificate.sigAlgName).isEqualTo(\"SHA256WITHRSA\", ignoreCase = true)\n  }\n\n  @Test\n  fun rsaSignedByEcdsa() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .ecdsa256()\n        .build()\n    val leaf =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .rsa2048()\n        .signedBy(root)\n        .build()\n    assertThat(root.certificate.sigAlgName).isEqualTo(\"SHA256WITHECDSA\", ignoreCase = true)\n    assertThat(leaf.certificate.sigAlgName).isEqualTo(\"SHA256WITHECDSA\", ignoreCase = true)\n  }\n\n  @Test\n  fun decodeEcdsa256() {\n    // The certificate + private key below was generated programmatically:\n    //\n    // HeldCertificate heldCertificate = new HeldCertificate.Builder()\n    //     .validityInterval(5_000L, 10_000L)\n    //     .addSubjectAlternativeName(\"1.1.1.1\")\n    //     .addSubjectAlternativeName(\"cash.app\")\n    //     .serialNumber(42L)\n    //     .commonName(\"cash.app\")\n    //     .organizationalUnit(\"engineering\")\n    //     .ecdsa256()\n    //     .build();\n    val certificatePem =\n      \"\"\"\n      |-----BEGIN CERTIFICATE-----\n      |MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl\n      |cmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAwMTAx\n      |MDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2FzaC5h\n      |cHAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASda8ChkQXxGELnrV/oBnIAx3dD\n      |ocUOJfdz4pOJTP6dVQB9U3UBiW5uSX/MoOD0LL5zG3bVyL3Y6pDwKuYvfLNhoyAw\n      |HjAcBgNVHREBAf8EEjAQhwQBAQEBgghjYXNoLmFwcDAKBggqhkjOPQQDAgNIADBF\n      |AiAyHHg1N6YDDQiY920+cnI5XSZwEGhAtb9PYWO8bLmkcQIhAI2CfEZf3V/obmdT\n      |yyaoEufLKVXhrTQhRfodTeigi4RX\n      |-----END CERTIFICATE-----\n      |\n      \"\"\".trimMargin()\n    val pkcs8Pem =\n      \"\"\"\n      |-----BEGIN PRIVATE KEY-----\n      |MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCA7ODT0xhGSNn4ESj6J\n      |lu/GJQZoU9lDrCPeUcQ28tzOWw==\n      |-----END PRIVATE KEY-----\n      |\n      \"\"\".trimMargin()\n    val bcPkcs8Pem =\n      \"\"\"\n      |-----BEGIN PRIVATE KEY-----\n      |ME0CAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEMzAxAgEBBCA7ODT0xhGSNn4ESj6J\n      |lu/GJQZoU9lDrCPeUcQ28tzOW6AKBggqhkjOPQMBBw==\n      |-----END PRIVATE KEY-----\n      |\n      \"\"\".trimMargin()\n    val heldCertificate = decode(certificatePem + pkcs8Pem)\n    assertThat(heldCertificate.certificatePem()).isEqualTo(certificatePem)\n\n    // Slightly different encoding\n    if (platform.isBouncyCastle()) {\n      assertThat(heldCertificate.privateKeyPkcs8Pem()).isEqualTo(bcPkcs8Pem)\n    } else {\n      assertThat(heldCertificate.privateKeyPkcs8Pem()).isEqualTo(pkcs8Pem)\n    }\n    val certificate = heldCertificate.certificate\n    assertThat(certificate.notBefore.time).isEqualTo(5000L)\n    assertThat(certificate.notAfter.time).isEqualTo(10000L)\n    assertThat(certificate.subjectAlternativeNames.toList()).containsExactly(\n      listOf(GeneralName.iPAddress, \"1.1.1.1\"),\n      listOf(GeneralName.dNSName, \"cash.app\"),\n    )\n    assertThat(certificate.getSubjectX500Principal().name)\n      .isEqualTo(\"CN=cash.app,OU=engineering\")\n  }\n\n  @Test\n  fun decodeRsa512() {\n    // The certificate + private key below was generated with OpenSSL. Never generate certificates\n    // with MD5 or 512-bit RSA; that's insecure!\n    //\n    // openssl req \\\n    //   -x509 \\\n    //   -md5 \\\n    //   -nodes \\\n    //   -days 1 \\\n    //   -newkey rsa:512 \\\n    //   -keyout privateKey.key \\\n    //   -out certificate.crt\n    val certificatePem =\n      \"\"\"\n      |-----BEGIN CERTIFICATE-----\n      |MIIBFzCBwgIJAIVAqagcVN7/MA0GCSqGSIb3DQEBBAUAMBMxETAPBgNVBAMMCGNh\n      |c2guYXBwMB4XDTE5MDkwNzAyMjg0NFoXDTE5MDkwODAyMjg0NFowEzERMA8GA1UE\n      |AwwIY2FzaC5hcHAwXDANBgkqhkiG9w0BAQEFAANLADBIAkEA8qAeoubm4mBTD9/J\n      |ujLQkfk/fuJt/T5pVQ1vUEqxfcMw0zYgszQ5C2MiIl7M6JkTRKU01q9hVFCR83wX\n      |zIdrLQIDAQABMA0GCSqGSIb3DQEBBAUAA0EAO1UpwhrkW3Ho1nZK/taoUQOoqz/n\n      |HFVMtyEkm5gBDgz8nJXwb3zbegclQyH+kVou02S8zC5WWzEtd0R8S0LsTA==\n      |-----END CERTIFICATE-----\n      |\n      \"\"\".trimMargin()\n    val pkcs8Pem =\n      \"\"\"\n      |-----BEGIN PRIVATE KEY-----\n      |MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA8qAeoubm4mBTD9/J\n      |ujLQkfk/fuJt/T5pVQ1vUEqxfcMw0zYgszQ5C2MiIl7M6JkTRKU01q9hVFCR83wX\n      |zIdrLQIDAQABAkEA7dEA9o/5k77y68ZhRv9z7QEwucBcKzQ3rsSCbWMpYqg924F9\n      |L8Z76kzSedSO2PN8mg6y/OLL+qBuTeUK/yiowQIhAP0cknFMbqeNX6uvj/S+V7in\n      |bIhQkhcSdJjRw8fxMnJpAiEA9WTp9wzJpn+9etZo0jJ8wkM0+LTMNELo47Ctz7l1\n      |kiUCIQCi34vslD5wWyzBEcwUtZdFH5dbcF1Rs3KMFA9jzfWkYQIgHtiWiFV1K5a3\n      |DK/S8UkjYY/tIq4nVRJsD+LvlkLrwnkCIECcz4yF4HQgv+Tbzj/gGSBl1VIliTcB\n      |Rc5RUQ0mZJQF\n      |-----END PRIVATE KEY-----\n      |\n      \"\"\".trimMargin()\n    val heldCertificate = decode(pkcs8Pem + certificatePem)\n    assertThat(heldCertificate.certificatePem()).isEqualTo(certificatePem)\n    assertThat(heldCertificate.privateKeyPkcs8Pem()).isEqualTo(pkcs8Pem)\n    val certificate = heldCertificate.certificate\n    assertThat(certificate.getSubjectX500Principal().name)\n      .isEqualTo(\"CN=cash.app\")\n  }\n\n  @Test\n  fun decodeRsa2048() {\n    // The certificate + private key below was generated programmatically:\n    //\n    // HeldCertificate heldCertificate = new HeldCertificate.Builder()\n    //     .validityInterval(5_000L, 10_000L)\n    //     .addSubjectAlternativeName(\"1.1.1.1\")\n    //     .addSubjectAlternativeName(\"cash.app\")\n    //     .serialNumber(42L)\n    //     .commonName(\"cash.app\")\n    //     .organizationalUnit(\"engineering\")\n    //     .rsa2048()\n    //     .build();\n    val certificatePem =\n      \"\"\"\n      |-----BEGIN CERTIFICATE-----\n      |MIIC7TCCAdWgAwIBAgIBKjANBgkqhkiG9w0BAQsFADApMRQwEgYDVQQLEwtlbmdp\n      |bmVlcmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAw\n      |MTAxMDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2Fz\n      |aC5hcHAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaU+vrUPL0APGI\n      |SXIuRX4xRrigXmGKx+GRPnWDWvGJwOm23Vpq/eZxQx6PbSUB1+QZzAwge20RpNAp\n      |2lt5/qFtgUpEon2j06rd/0+ODqqVJX+6d3SpmF1fPfKUB6AOZbxEkaJpBSTavoTg\n      |G2M/NMdjZjrcB3quNQcLg54mmI3HJm1zOd/8i2fZjvoiyVY30Inn2SmQsAotXw1u\n      |aE/319bnR2sQlnkp6MJU0eLEtKyRif/IODvY+mtRYYdkFtoeT6qQPMIh+gF/H3to\n      |5tjs3g59QC8k2TJDop4EFYUOwdrtnb8wUiBnLyURD1szASE2IO2Ftk1zaNOPKtrv\n      |VeJuB/mpAgMBAAGjIDAeMBwGA1UdEQEB/wQSMBCHBAEBAQGCCGNhc2guYXBwMA0G\n      |CSqGSIb3DQEBCwUAA4IBAQAPm7vfk+rxSucxxbFiimmFKBw+ymieLY/kznNh0lHJ\n      |q15fsMYK7TTTt2FFqyfVXhhRZegLrkrGb3+4Dz1uNtcRrjT4qo+T/JOuZGxtBLbm\n      |4/hkFSYavtd2FW+/CK7EnQKUyabgLOblb21IHOlcPwpSe6KkJjpwq0TV/ozzfk/q\n      |kGRA7/Ubn5TMRYyHWnod2SS14+BkItcWN03Z7kvyMYrpNZpu6vQRYsqJJFMcmpGZ\n      |sZQW31gO2arPmfNotkQdFdNL12c9YZKkJGhyK6NcpffD2l6O9NS5SRD5RnkvBxQw\n      |fX5DamL8je/YKSLQ4wgUA/5iVKlCiJGQi6fYIJ0kxayO\n      |-----END CERTIFICATE-----\n      |\n      \"\"\".trimMargin()\n    val pkcs8Pem =\n      \"\"\"\n      |-----BEGIN PRIVATE KEY-----\n      |MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCaU+vrUPL0APGI\n      |SXIuRX4xRrigXmGKx+GRPnWDWvGJwOm23Vpq/eZxQx6PbSUB1+QZzAwge20RpNAp\n      |2lt5/qFtgUpEon2j06rd/0+ODqqVJX+6d3SpmF1fPfKUB6AOZbxEkaJpBSTavoTg\n      |G2M/NMdjZjrcB3quNQcLg54mmI3HJm1zOd/8i2fZjvoiyVY30Inn2SmQsAotXw1u\n      |aE/319bnR2sQlnkp6MJU0eLEtKyRif/IODvY+mtRYYdkFtoeT6qQPMIh+gF/H3to\n      |5tjs3g59QC8k2TJDop4EFYUOwdrtnb8wUiBnLyURD1szASE2IO2Ftk1zaNOPKtrv\n      |VeJuB/mpAgMBAAECggEAOlOXaYNZn1Cv+INRrR1EmVkSNEIXeX0bymohvbhka1zG\n      |t/8myiMVsh7c8PYeM3kl034j4y7ixPVWW0sUoaHT3vArYo9LDtzTyj1REu6GGAJp\n      |KM82/1X/jBx8jufm3SokIoIsMKbqC+ZPj+ep9dx7sxyTCE+nVSnjdL2Uyx+DDg3o\n      |na237HTScWIi+tMv5QGEwqLHS2q+NZYfjgnSxNY8BRw4XZCcIZRko9niuB5gUjj/\n      |y01HwvOCWuOMaSKZak1OdOaz3427/TkhYIqf6ft0ELF+ASRk3BLQA06pRt88H3u2\n      |3vsHJsWr2rkCN0h9uDp2o50ZQ5fvlxqG0QIZmvkIkQKBgQDOHeZKvXO5IxQ+S8ed\n      |09bC5SKiclCdW+Ry7N2x1MBfrxc4TTTTNaUN9Qdc6RXANG9bX2CJv0Dkh/0yH3z9\n      |Bdq6YcoP6DFCX46jwhCKvxMX9h9PFLvY7l2VSe7NfboGzvYLCy8ErsGuio8u9MHZ\n      |osX2ch6Gdhn1xUwLCw+T7rNwjQKBgQC/rWb0sWfgbKhEqV+u5oov+fFjooWmTQlQ\n      |jcj+lMWUOkitnPmX9TsH5JDa8I89Y0gJGu7Lfg8XSH+4FCCfX3mSLYwVH5vAIvmr\n      |TjMqRwSahQuTr/g+lx7alpcUHYv3z6b3WYIXFPPr3t7grWNJ14wMv9DnItWOg84H\n      |LlxAvXXsjQKBgQCRPPhdignVVyaYjwVl7TPTuWoiVbMAbxQW91lwSZ4UzmfqQF0M\n      |xyw7HYHGsmelPE2LcTWxWpb7cee0PgPwtwNdejLL6q1rO7JjKghF/EYUCFYff1iu\n      |j6hZ3fLr0cAXtBYjygmjnxDTUMd8KvO9y7j644cm8GlyiUgAMBcWAolmsQKBgQCT\n      |AJQTWfPGxM6QSi3d32VfwhsFROGnVzGrm/HofYTCV6jhraAmkKcDOKJ3p0LT286l\n      |XQiC/FzqiGmbbaRPVlPQbiofESzMQIamgMTwyaKYNy1XyP9kUVYSYqfff4GXPqRY\n      |00bYGPOxlC3utkuNmEgKhxnaCncqY5+hFkceR6+nCQKBgQC1Gonjhw0lYe43aHpp\n      |nDJKv3FnyN3wxjsR2c9sWpDzHA6CMVhSeLoXCB9ishmrSE/CygNlTU1TEy63xN22\n      |+dMHl5I/urMesjKKWiKZHdbWVIjJDv25r3jrN9VLr4q6AD9r1Su5G0o2j0N5ujVg\n      |SzpFHp+ZzhL/SANa8EqlcF6ItQ==\n      |-----END PRIVATE KEY-----\n      |\n      \"\"\".trimMargin()\n    val heldCertificate = decode(pkcs8Pem + certificatePem)\n    assertThat(heldCertificate.certificatePem()).isEqualTo(certificatePem)\n    assertThat(heldCertificate.privateKeyPkcs8Pem()).isEqualTo(pkcs8Pem)\n    val certificate = heldCertificate.certificate\n    assertThat(certificate.notBefore.time).isEqualTo(5000L)\n    assertThat(certificate.notAfter.time).isEqualTo(10000L)\n    assertThat(certificate.subjectAlternativeNames.toList()).containsExactly(\n      listOf(GeneralName.iPAddress, \"1.1.1.1\"),\n      listOf(GeneralName.dNSName, \"cash.app\"),\n    )\n    assertThat(certificate.getSubjectX500Principal().name)\n      .isEqualTo(\"CN=cash.app,OU=engineering\")\n  }\n\n  @Test\n  fun decodeWrongNumber() {\n    val certificatePem =\n      \"\"\"\n      |-----BEGIN CERTIFICATE-----\n      |MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl\n      |cmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAwMTAx\n      |MDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2FzaC5h\n      |cHAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASda8ChkQXxGELnrV/oBnIAx3dD\n      |ocUOJfdz4pOJTP6dVQB9U3UBiW5uSX/MoOD0LL5zG3bVyL3Y6pDwKuYvfLNhoyAw\n      |HjAcBgNVHREBAf8EEjAQhwQBAQEBgghjYXNoLmFwcDAKBggqhkjOPQQDAgNIADBF\n      |AiAyHHg1N6YDDQiY920+cnI5XSZwEGhAtb9PYWO8bLmkcQIhAI2CfEZf3V/obmdT\n      |yyaoEufLKVXhrTQhRfodTeigi4RX\n      |-----END CERTIFICATE-----\n      |\n      \"\"\".trimMargin()\n    val pkcs8Pem =\n      \"\"\"\n      |-----BEGIN PRIVATE KEY-----\n      |MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCA7ODT0xhGSNn4ESj6J\n      |lu/GJQZoU9lDrCPeUcQ28tzOWw==\n      |-----END PRIVATE KEY-----\n      |\n      \"\"\".trimMargin()\n    try {\n      decode(certificatePem)\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\"string does not include a private key\")\n    }\n    try {\n      decode(pkcs8Pem)\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\"string does not include a certificate\")\n    }\n    try {\n      decode(certificatePem + pkcs8Pem + certificatePem)\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\"string includes multiple certificates\")\n    }\n    try {\n      decode(pkcs8Pem + certificatePem + pkcs8Pem)\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\"string includes multiple private keys\")\n    }\n  }\n\n  @Test\n  fun decodeWrongType() {\n    try {\n      decode(\n        \"\"\"\n        |-----BEGIN CERTIFICATE-----\n        |MIIBmjCCAQOgAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhjYXNo\n        |-----END CERTIFICATE-----\n        |-----BEGIN RSA PRIVATE KEY-----\n        |sXPVYAsGD1wizrXX+wFaL3chtF1oG1Fx/jcsSsG6BA==\n        |-----END RSA PRIVATE KEY-----\n        |\n        \"\"\".trimMargin(),\n      )\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      assertThat(expected.message).isEqualTo(\"unexpected type: RSA PRIVATE KEY\")\n    }\n  }\n\n  @Test\n  fun decodeMalformed() {\n    try {\n      decode(\n        \"\"\"\n        |-----BEGIN CERTIFICATE-----\n        |MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl\n        |-----END CERTIFICATE-----\n        |-----BEGIN PRIVATE KEY-----\n        |MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCA7ODT0xhGSNn4ESj6J\n        |lu/GJQZoU9lDrCPeUcQ28tzOWw==\n        |-----END PRIVATE KEY-----\n        |\n        \"\"\".trimMargin(),\n      )\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      if (!platform.isConscrypt()) {\n        assertThat(expected.message).isEqualTo(\"failed to decode certificate\")\n      }\n    }\n    try {\n      decode(\n        \"\"\"\n        |-----BEGIN CERTIFICATE-----\n        |MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl\n        |cmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAwMTAx\n        |MDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2FzaC5h\n        |cHAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASda8ChkQXxGELnrV/oBnIAx3dD\n        |ocUOJfdz4pOJTP6dVQB9U3UBiW5uSX/MoOD0LL5zG3bVyL3Y6pDwKuYvfLNhoyAw\n        |HjAcBgNVHREBAf8EEjAQhwQBAQEBgghjYXNoLmFwcDAKBggqhkjOPQQDAgNIADBF\n        |AiAyHHg1N6YDDQiY920+cnI5XSZwEGhAtb9PYWO8bLmkcQIhAI2CfEZf3V/obmdT\n        |yyaoEufLKVXhrTQhRfodTeigi4RX\n        |-----END CERTIFICATE-----\n        |-----BEGIN PRIVATE KEY-----\n        |MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCA7ODT0xhGSNn4ESj6J\n        |-----END PRIVATE KEY-----\n        |\n        \"\"\".trimMargin(),\n      )\n      fail<Any>()\n    } catch (expected: IllegalArgumentException) {\n      if (!platform.isConscrypt()) {\n        assertThat(expected.message).isEqualTo(\"failed to decode private key\")\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "okhttp-tls/src/test/java/okhttp3/tls/internal/der/DerCertificatesTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.math.BigInteger\nimport java.security.KeyFactory\nimport java.security.spec.PKCS8EncodedKeySpec\nimport java.security.spec.X509EncodedKeySpec\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.TimeZone\nimport okhttp3.tls.HeldCertificate\nimport okhttp3.tls.decodeCertificatePem\nimport okhttp3.tls.internal.der.ObjectIdentifiers.BASIC_CONSTRAINTS\nimport okhttp3.tls.internal.der.ObjectIdentifiers.COMMON_NAME\nimport okhttp3.tls.internal.der.ObjectIdentifiers.ORGANIZATIONAL_UNIT_NAME\nimport okhttp3.tls.internal.der.ObjectIdentifiers.RSA_ENCRYPTION\nimport okhttp3.tls.internal.der.ObjectIdentifiers.SHA256_WITH_RSA_ENCRYPTION\nimport okhttp3.tls.internal.der.ObjectIdentifiers.SUBJECT_ALTERNATIVE_NAME\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.decodeBase64\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.Test\n\ninternal class DerCertificatesTest {\n  private val stateOrProvince = \"1.3.6.1.4.1.311.60.2.1.2\"\n  private val country = \"1.3.6.1.4.1.311.60.2.1.3\"\n  private val certificateTransparencySignedCertificateTimestamps = \"1.3.6.1.4.1.11129.2.4.2\"\n  private val authorityInfoAccess = \"1.3.6.1.5.5.7.1.1\"\n  private val serialNumber = \"2.5.4.5\"\n  private val countryName = \"2.5.4.6\"\n  private val localityName = \"2.5.4.7\"\n  private val stateOrProvinceName = \"2.5.4.8\"\n  private val organizationName = \"2.5.4.10\"\n  private val businessCategory = \"2.5.4.15\"\n  private val subjectKeyIdentifier = \"2.5.29.14\"\n  private val keyUsage = \"2.5.29.15\"\n  private val crlDistributionPoints = \"2.5.29.31\"\n  private val certificatePolicies = \"2.5.29.32\"\n  private val authorityKeyIdentifier = \"2.5.29.35\"\n  private val extendedKeyUsage = \"2.5.29.37\"\n\n  @Test\n  fun `decode simple certificate`() {\n    val certificateBase64 =\n      \"\"\"\n      |MIIBmjCCAQOgAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhjYXNo\n      |LmFwcDAeFw03MDAxMDEwMDAwMDBaFw03MDAxMDEwMDAwMDFaMBMxETAPBgNVBAMT\n      |CGNhc2guYXBwMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCApFHhtrLan28q\n      |+oMolZuaTfWBA0V5aMIvq32BsloQu6LlvX1wJ4YEoUCjDlPOtpht7XLbUmBnbIzN\n      |89XK4UJVM6Sqp3K88Km8z7gMrdrfTom/274wL25fICR+yDEQ5fUVYBmJAKXZF1ao\n      |I0mIoEx0xFsQhIJ637v2MxJDupd61wIDAQABMA0GCSqGSIb3DQEBCwUAA4GBADam\n      |UVwKh5Ry7es3OxtY3IgQunPUoLc0Gw71gl9Z+7t2FJ5VkcI5gWfutmdxZ2bDXCI8\n      |8V0vxo1pHXnbBrnxhS/Z3TBerw8RyQqcaWOdp+pBXyIWmR+jHk9cHZCqQveTIBsY\n      |jaA9VEhgdaVhxBsT2qzUNDsXlOzGsliznDfoqETb\n      |\n      \"\"\".trimMargin()\n    val certificateByteString = certificateBase64.decodeBase64()!!\n    val certificatePem =\n      \"\"\"\n      |-----BEGIN CERTIFICATE-----\n      |$certificateBase64\n      |-----END CERTIFICATE-----\n      |\n      \"\"\".trimMargin()\n\n    val javaCertificate = certificatePem.decodeCertificatePem()\n    val okHttpCertificate =\n      CertificateAdapters.certificate\n        .fromDer(certificateByteString)\n\n    assertThat(okHttpCertificate.signatureValue.byteString)\n      .isEqualTo(javaCertificate.signature.toByteString())\n\n    val publicKeyBytes =\n      (\n        \"3081890281810080a451e1b6b2da9f6f2afa8328959b9a4df58103457968c22fab7d81\" +\n          \"b25a10bba2e5bd7d70278604a140a30e53ceb6986ded72db5260676c8ccdf3d5cae1425533a4aaa772bcf0a9\" +\n          \"bccfb80caddadf4e89bfdbbe302f6e5f20247ec83110e5f51560198900a5d91756a8234988a04c74c45b1084\" +\n          \"827adfbbf6331243ba977ad70203010001\"\n      ).decodeHex()\n    val signatureBytes =\n      (\n        \"36a6515c0a879472edeb373b1b58dc8810ba73d4a0b7341b0ef5825f59fbbb76149e55\" +\n          \"91c2398167eeb667716766c35c223cf15d2fc68d691d79db06b9f1852fd9dd305eaf0f11c90a9c69639da7ea\" +\n          \"415f2216991fa31e4f5c1d90aa42f793201b188da03d54486075a561c41b13daacd4343b1794ecc6b258b39c\" +\n          \"37e8a844db\"\n      ).decodeHex()\n\n    assertThat(okHttpCertificate).isEqualTo(\n      Certificate(\n        tbsCertificate =\n          TbsCertificate(\n            // v3.\n            version = 2L,\n            serialNumber = BigInteger.ONE,\n            signature =\n              AlgorithmIdentifier(\n                algorithm = SHA256_WITH_RSA_ENCRYPTION,\n                parameters = null,\n              ),\n            issuer =\n              listOf(\n                listOf(\n                  AttributeTypeAndValue(\n                    type = COMMON_NAME,\n                    value = \"cash.app\",\n                  ),\n                ),\n              ),\n            validity =\n              Validity(\n                notBefore = 0L,\n                notAfter = 1000L,\n              ),\n            subject =\n              listOf(\n                listOf(\n                  AttributeTypeAndValue(\n                    type = COMMON_NAME,\n                    value = \"cash.app\",\n                  ),\n                ),\n              ),\n            subjectPublicKeyInfo =\n              SubjectPublicKeyInfo(\n                algorithm =\n                  AlgorithmIdentifier(\n                    algorithm = RSA_ENCRYPTION,\n                    parameters = null,\n                  ),\n                subjectPublicKey =\n                  BitString(\n                    byteString = publicKeyBytes,\n                    unusedBitsCount = 0,\n                  ),\n              ),\n            issuerUniqueID = null,\n            subjectUniqueID = null,\n            extensions = listOf(),\n          ),\n        signatureAlgorithm =\n          AlgorithmIdentifier(\n            algorithm = SHA256_WITH_RSA_ENCRYPTION,\n            parameters = null,\n          ),\n        signatureValue =\n          BitString(\n            byteString = signatureBytes,\n            unusedBitsCount = 0,\n          ),\n      ),\n    )\n  }\n\n  @Test\n  fun `decode CA certificate`() {\n    val certificateBase64 =\n      \"\"\"\n      |MIIE/zCCA+egAwIBAgIEUdNARDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMC\n      |VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0\n      |Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW\n      |KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl\n      |cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDkyMjE3MTQ1N1oXDTI0MDkyMzAx\n      |MzE1M1owgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgw\n      |JgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQL\n      |EzAoYykgMjAwOSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9u\n      |bHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0\n      |eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoS2ctueDGvi\n      |mekwAad26jK4lUEaydphTlhyz/72gnm/c2EGCqUn2LNf00VOHHLWTjLycooP94MZ\n      |0GqAgABFHrDH55q/ElcnHKNoLwqHvWprDl5l8xx31dSFjXAhtLMy54ui1YY5ArG4\n      |0kfO5MlJxDun3vtUfVe+8OhuwnmyOgtV4lCYFjITXC94VsHClLPyWuQnmp8k18bs\n      |0JslguPMwsRFxYyXegZrKhGfqQpuSDtv29QRGUL3jwe/9VNfnD70FyzmaaxOMkxi\n      |d+q36OW7NLwZi66cUee3frVTsTMi5W3PcDwa+uKbZ7aD9I2lr2JMTeBYrGQ0EgP4\n      |to2UYySkcQIDAQABo4IBDzCCAQswDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI\n      |MAYBAf8CAQEwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz\n      |cC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmVudHJ1\n      |c3QubmV0L3Jvb3RjYTEuY3JsMDsGA1UdIAQ0MDIwMAYEVR0gADAoMCYGCCsGAQUF\n      |BwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NQUzAdBgNVHQ4EFgQUanImetAe\n      |733nO2lR1GyNn5ASZqswHwYDVR0jBBgwFoAUaJDkZ6SmU4DHhmak8fdLQ/uEvW0w\n      |DQYJKoZIhvcNAQELBQADggEBAGkzg/woem99751V68U+ep11s8zDODbZNKIoaBjq\n      |HmnTvefQd9q4AINOSs9v0fHBIj905PeYSZ6btp7h25h3LVY0sag82f3Azce/BQPU\n      |AsXx5cbaCKUTx2IjEdFhMB1ghEXveajGJpOkt800uGnFE/aRs8lFc3a2kvZ2Clvh\n      |A0e36SlMkTIjN0qcNdh4/R0f5IOJJICtt/nP5F2l1HHEhVtwH9s/HAHrGkUmMRTM\n      |Zb9n3srMM2XlQZHXN75BGpad5oqXnafOrE6aPb0BoGrZTyIAi0TVaWJ7LuvMuueS\n      |fWlnPfy4fN5Bh9Bp6roKGHoalUOzeXEodm2h+1dK7E3IDhA=\n      |\n      \"\"\".trimMargin()\n    val certificateByteString = certificateBase64.decodeBase64()!!\n    val certificatePem =\n      \"\"\"\n      |-----BEGIN CERTIFICATE-----\n      |$certificateBase64\n      |-----END CERTIFICATE-----\n      |\n      \"\"\".trimMargin()\n\n    val javaCertificate = certificatePem.decodeCertificatePem()\n    val okHttpCertificate =\n      CertificateAdapters.certificate\n        .fromDer(certificateByteString)\n\n    val publicKeyBytes =\n      (\n        \"3082010a0282010100ba84b672db9e0c6be299e93001a776ea32b895411ac9da614e58\" +\n          \"72cffef68279bf7361060aa527d8b35fd3454e1c72d64e32f2728a0ff78319d06a808000451eb0c7e79abf12\" +\n          \"57271ca3682f0a87bd6a6b0e5e65f31c77d5d4858d7021b4b332e78ba2d5863902b1b8d247cee4c949c43ba7\" +\n          \"defb547d57bef0e86ec279b23a0b55e250981632135c2f7856c1c294b3f25ae4279a9f24d7c6ecd09b2582e3\" +\n          \"ccc2c445c58c977a066b2a119fa90a6e483b6fdbd4111942f78f07bff5535f9c3ef4172ce669ac4e324c6277\" +\n          \"eab7e8e5bb34bc198bae9c51e7b77eb553b13322e56dcf703c1afae29b67b683f48da5af624c4de058ac6434\" +\n          \"1203f8b68d946324a4710203010001\"\n      ).decodeHex()\n    val signatureBytes =\n      (\n        \"693383fc287a6f7def9d55ebc53e7a9d75b3ccc33836d934a2286818ea1e69d3bde7d0\" +\n          \"77dab800834e4acf6fd1f1c1223f74e4f798499e9bb69ee1db98772d5634b1a83cd9fdc0cdc7bf0503d402c5\" +\n          \"f1e5c6da08a513c7622311d161301d608445ef79a8c62693a4b7cd34b869c513f691b3c9457376b692f6760a\" +\n          \"5be10347b7e9294c913223374a9c35d878fd1d1fe483892480adb7f9cfe45da5d471c4855b701fdb3f1c01eb\" +\n          \"1a45263114cc65bf67decacc3365e54191d737be411a969de68a979da7ceac4e9a3dbd01a06ad94f22008b44\" +\n          \"d569627b2eebccbae7927d69673dfcb87cde4187d069eaba0a187a1a9543b3797128766da1fb574aec4dc80e\" +\n          \"10\"\n      ).decodeHex()\n\n    assertThat(okHttpCertificate.signatureValue.byteString)\n      .isEqualTo(javaCertificate.signature.toByteString())\n\n    assertThat(okHttpCertificate).isEqualTo(\n      Certificate(\n        tbsCertificate =\n          TbsCertificate(\n            // v3.\n            version = 2L,\n            serialNumber = BigInteger(\"1372799044\"),\n            signature =\n              AlgorithmIdentifier(\n                algorithm = SHA256_WITH_RSA_ENCRYPTION,\n                parameters = null,\n              ),\n            issuer =\n              listOf(\n                listOf(\n                  AttributeTypeAndValue(\n                    type = countryName,\n                    value = \"US\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = organizationName,\n                    value = \"Entrust, Inc.\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = ORGANIZATIONAL_UNIT_NAME,\n                    value = \"www.entrust.net/CPS is incorporated by reference\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = ORGANIZATIONAL_UNIT_NAME,\n                    value = \"(c) 2006 Entrust, Inc.\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = COMMON_NAME,\n                    value = \"Entrust Root Certification Authority\",\n                  ),\n                ),\n              ),\n            validity =\n              Validity(\n                notBefore = 1411406097000L,\n                notAfter = 1727055113000L,\n              ),\n            subject =\n              listOf(\n                listOf(\n                  AttributeTypeAndValue(\n                    type = countryName,\n                    value = \"US\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = organizationName,\n                    value = \"Entrust, Inc.\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = ORGANIZATIONAL_UNIT_NAME,\n                    value = \"See www.entrust.net/legal-terms\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = ORGANIZATIONAL_UNIT_NAME,\n                    value = \"(c) 2009 Entrust, Inc. - for authorized use only\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = COMMON_NAME,\n                    value = \"Entrust Root Certification Authority - G2\",\n                  ),\n                ),\n              ),\n            subjectPublicKeyInfo =\n              SubjectPublicKeyInfo(\n                algorithm =\n                  AlgorithmIdentifier(\n                    algorithm = RSA_ENCRYPTION,\n                    parameters = null,\n                  ),\n                subjectPublicKey =\n                  BitString(\n                    byteString = publicKeyBytes,\n                    unusedBitsCount = 0,\n                  ),\n              ),\n            issuerUniqueID = null,\n            subjectUniqueID = null,\n            extensions =\n              listOf(\n                Extension(\n                  id = keyUsage,\n                  critical = true,\n                  value = \"03020106\".decodeHex(),\n                ),\n                Extension(\n                  id = BASIC_CONSTRAINTS,\n                  critical = true,\n                  value =\n                    BasicConstraints(\n                      ca = true,\n                      maxIntermediateCas = 1L,\n                    ),\n                ),\n                Extension(\n                  id = authorityInfoAccess,\n                  critical = false,\n                  value =\n                    (\n                      \"3025302306082b060105050730018617687474703a2f2f6f6373702e656\" +\n                        \"e74727573742e6e6574\"\n                    ).decodeHex(),\n                ),\n                Extension(\n                  id = crlDistributionPoints,\n                  critical = false,\n                  value =\n                    (\n                      \"302a3028a026a0248622687474703a2f2f63726c2e656e74727573742e6\" +\n                        \"e65742f726f6f746361312e63726c\"\n                    ).decodeHex(),\n                ),\n                Extension(\n                  id = certificatePolicies,\n                  critical = false,\n                  value =\n                    (\n                      \"303230300604551d20003028302606082b06010505070201161a6874747\" +\n                        \"03a2f2f7777772e656e74727573742e6e65742f435053\"\n                    ).decodeHex(),\n                ),\n                Extension(\n                  id = subjectKeyIdentifier,\n                  critical = false,\n                  value = \"04146a72267ad01eef7de73b6951d46c8d9f901266ab\".decodeHex(),\n                ),\n                Extension(\n                  id = authorityKeyIdentifier,\n                  critical = false,\n                  value = \"301680146890e467a4a65380c78666a4f1f74b43fb84bd6d\".decodeHex(),\n                ),\n              ),\n          ),\n        signatureAlgorithm =\n          AlgorithmIdentifier(\n            algorithm = SHA256_WITH_RSA_ENCRYPTION,\n            parameters = null,\n          ),\n        signatureValue =\n          BitString(\n            byteString = signatureBytes,\n            unusedBitsCount = 0,\n          ),\n      ),\n    )\n  }\n\n  @Test\n  fun `decode typical certificate`() {\n    val certificateBase64 =\n      \"\"\"\n      |MIIHHTCCBgWgAwIBAgIRAL5oALmpH7l6AAAAAFTRMh0wDQYJKoZIhvcNAQELBQAw\n      |gboxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL\n      |Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg\n      |MjAxNCBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxLjAs\n      |BgNVBAMTJUVudHJ1c3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBMMU0wHhcN\n      |MjAwNDEzMTMyNTQ5WhcNMjEwNDEyMTM1NTQ5WjCBxTELMAkGA1UEBhMCVVMxEzAR\n      |BgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgsr\n      |BgEEAYI3PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIBAhMIRGVsYXdhcmUxFTATBgNV\n      |BAoTDFNxdWFyZSwgSW5jLjEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24x\n      |EDAOBgNVBAUTBzQ2OTk4NTUxETAPBgNVBAMTCGNhc2guYXBwMIIBIjANBgkqhkiG\n      |9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqv2iSwWvb6ys/Ru4LtSz0R4wDaxklrFIGqdJ\n      |rxxYdAdLQjyjHyJsfkNQdt2u4JYPRKaRTVYR9VIIeWUx/IjhZhsGPstPMjYT3cN1\n      |VsphSDtrRVuxYlmkrvHar0HoadNr1MHd96Ach3g1QJlV8uyUJ7JXpPCNJ8EMiH52\n      |n8bVzpjDjXwoYg3oOYvceteA0GJ5VWYACDgfmkeoaN1Cx31O9qcSiUk5AY8HfAnP\n      |h20VcrnPo2dJmm7fkUKohIxrMjtpwi5esWhCBZJk50FveKrgdeSe4XxNL7uJPD89\n      |SJtKmX7jxoNQSY3mrPssLdadwltUOhzc4Lcmoj4Ob24JxuVw8QIDAQABo4IDDzCC\n      |AwswIQYDVR0RBBowGIIIY2FzaC5hcHCCDHd3dy5jYXNoLmFwcDCCAX8GCisGAQQB\n      |1nkCBAIEggFvBIIBawFpAHcAVhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ\n      |0N0AAAFxc9MmmwAABAMASDBGAiEAqeWK3uWt9LX1p3l0gPgNxYBB142oqtRMnMBB\n      |anTKy2ICIQDrRj7PRsVyXf1QRxgE5MZl6K6XkBKbaXBlAqPpb8z2hQB3AId1v+dZ\n      |fPiMQ5lfvfNu/1aNR1Y2/0q1YMG06v9eoIMPAAABcXPTJq0AAAQDAEgwRgIhANRS\n      |wAmVQLXhhxbbUTSKIA6P0Q6EmNABCNSJjSK5Q0ItAiEA88hnegYqVaykbbsQSSI0\n      |gP/+Odnm/Thso6HEJFXvYGcAdQB9PvL4j/+IVWgkwsDKnlKJeSvFDngJfy5ql2iZ\n      |fiLw1wAAAXFz0yazAAAEAwBGMEQCIH4RLAKbk+DbFdHeQO3bmqelXutLSM6MlN34\n      |7XEzHpMeAiB4KB48OcjmQ7kBwrxsRwqg7TrQG/F/DyB9wPilq1QacDAOBgNVHQ8B\n      |Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMGgGCCsGAQUF\n      |BwEBBFwwWjAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZW50cnVzdC5uZXQwMwYI\n      |KwYBBQUHMAKGJ2h0dHA6Ly9haWEuZW50cnVzdC5uZXQvbDFtLWNoYWluMjU2LmNl\n      |cjAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmVudHJ1c3QubmV0L2xldmVs\n      |MW0uY3JsMEoGA1UdIARDMEEwNgYKYIZIAYb6bAoBAjAoMCYGCCsGAQUFBwIBFhpo\n      |dHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAHBgVngQwBATAfBgNVHSMEGDAWgBTD\n      |99C1KjCtrw2RIXA5VN28iXDHOjAdBgNVHQ4EFgQUdf0kwt9ZJZnjLzNz4YwEUN0b\n      |h7YwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEAYLX6TSuQqSAEu37pJ+au\n      |9IlRiAEhtdybxr3mhuII0zImejhLuo2knO2SD59avCDBPivITsSvh2aewOUmeKj1\n      |GYI7v16xCOCTQz3k31sCAX2L7DozHtbrY4wG7hUSA9dSv/aYJEtebkwim3lgHwv3\n      |NHA3iiW3raH1DPJThQmxFJrnT1zL0LQbM1nRQMXaBVfQEEhIYnrU672x6D/cya6r\n      |5UwWye3TOZCH0Lh+YaZqtuKx9lEIEXaxjD3jpGlwRLuE/fI6fXg+0kMvaqNVLmpN\n      |aJT7WeHs5bkf0dU7rtDefr0iKeqIxrlURPgbeWZF8GAkpdNaCwWMDAFO8DG04K+t\n      |Aw==\n      |\n      \"\"\".trimMargin()\n    val certificateByteString = certificateBase64.decodeBase64()!!\n    val certificatePem =\n      \"\"\"\n      |-----BEGIN CERTIFICATE-----\n      |$certificateBase64\n      |-----END CERTIFICATE-----\n      |\n      \"\"\".trimMargin()\n\n    val javaCertificate = certificatePem.decodeCertificatePem()\n    val okHttpCertificate =\n      CertificateAdapters.certificate\n        .fromDer(certificateByteString)\n\n    val publicKeyBytes =\n      (\n        \"3082010a0282010100aafda24b05af6facacfd1bb82ed4b3d11e300dac6496b1481aa7\" +\n          \"49af1c5874074b423ca31f226c7e435076ddaee0960f44a6914d5611f55208796531fc88e1661b063ecb4f32\" +\n          \"3613ddc37556ca61483b6b455bb16259a4aef1daaf41e869d36bd4c1ddf7a01c877835409955f2ec9427b257\" +\n          \"a4f08d27c10c887e769fc6d5ce98c38d7c28620de8398bdc7ad780d0627955660008381f9a47a868dd42c77d\" +\n          \"4ef6a712894939018f077c09cf876d1572b9cfa367499a6edf9142a8848c6b323b69c22e5eb16842059264e7\" +\n          \"416f78aae075e49ee17c4d2fbb893c3f3d489b4a997ee3c68350498de6acfb2c2dd69dc25b543a1cdce0b726\" +\n          \"a23e0e6f6e09c6e570f10203010001\"\n      ).decodeHex()\n    val signatureBytes =\n      (\n        \"60b5fa4d2b90a92004bb7ee927e6aef48951880121b5dc9bc6bde686e208d332267a38\" +\n          \"4bba8da49ced920f9f5abc20c13e2bc84ec4af87669ec0e52678a8f519823bbf5eb108e093433de4df5b0201\" +\n          \"7d8bec3a331ed6eb638c06ee151203d752bff698244b5e6e4c229b79601f0bf73470378a25b7ada1f50cf253\" +\n          \"8509b1149ae74f5ccbd0b41b3359d140c5da0557d0104848627ad4ebbdb1e83fdcc9aeabe54c16c9edd33990\" +\n          \"87d0b87e61a66ab6e2b1f651081176b18c3de3a4697044bb84fdf23a7d783ed2432f6aa3552e6a4d6894fb59\" +\n          \"e1ece5b91fd1d53baed0de7ebd2229ea88c6b95444f81b796645f06024a5d35a0b058c0c014ef031b4e0afad\" +\n          \"03\"\n      ).decodeHex()\n\n    assertThat(okHttpCertificate.signatureValue.byteString)\n      .isEqualTo(javaCertificate.signature.toByteString())\n\n    assertThat(okHttpCertificate).isEqualTo(\n      Certificate(\n        tbsCertificate =\n          TbsCertificate(\n            // v3.\n            version = 2L,\n            serialNumber = BigInteger(\"253093332781973022312510445874391888413\"),\n            signature =\n              AlgorithmIdentifier(\n                algorithm = SHA256_WITH_RSA_ENCRYPTION,\n                parameters = null,\n              ),\n            issuer =\n              listOf(\n                listOf(\n                  AttributeTypeAndValue(\n                    type = countryName,\n                    value = \"US\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = organizationName,\n                    value = \"Entrust, Inc.\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = ORGANIZATIONAL_UNIT_NAME,\n                    value = \"See www.entrust.net/legal-terms\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = ORGANIZATIONAL_UNIT_NAME,\n                    value = \"(c) 2014 Entrust, Inc. - for authorized use only\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = COMMON_NAME,\n                    value = \"Entrust Certification Authority - L1M\",\n                  ),\n                ),\n              ),\n            validity =\n              Validity(\n                notBefore = 1586784349000L,\n                notAfter = 1618235749000L,\n              ),\n            subject =\n              listOf(\n                listOf(\n                  AttributeTypeAndValue(\n                    type = countryName,\n                    value = \"US\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = stateOrProvinceName,\n                    value = \"California\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = localityName,\n                    value = \"San Francisco\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = country,\n                    value = \"US\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = stateOrProvince,\n                    value = \"Delaware\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = organizationName,\n                    value = \"Square, Inc.\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = businessCategory,\n                    value = \"Private Organization\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = serialNumber,\n                    value = \"4699855\",\n                  ),\n                ),\n                listOf(\n                  AttributeTypeAndValue(\n                    type = COMMON_NAME,\n                    value = \"cash.app\",\n                  ),\n                ),\n              ),\n            subjectPublicKeyInfo =\n              SubjectPublicKeyInfo(\n                algorithm =\n                  AlgorithmIdentifier(\n                    algorithm = RSA_ENCRYPTION,\n                    parameters = null,\n                  ),\n                subjectPublicKey =\n                  BitString(\n                    byteString = publicKeyBytes,\n                    unusedBitsCount = 0,\n                  ),\n              ),\n            issuerUniqueID = null,\n            subjectUniqueID = null,\n            extensions =\n              listOf(\n                Extension(\n                  id = SUBJECT_ALTERNATIVE_NAME,\n                  critical = false,\n                  value =\n                    listOf(\n                      CertificateAdapters.generalNameDnsName to \"cash.app\",\n                      CertificateAdapters.generalNameDnsName to \"www.cash.app\",\n                    ),\n                ),\n                Extension(\n                  id = certificateTransparencySignedCertificateTimestamps,\n                  critical = false,\n                  value =\n                    (\n                      \"0482016b01690077005614069a2fd7c2ecd3f5e1bd44b23ec74676b9bc9\" +\n                        \"9115cc0ef949855d689d0dd0000017173d3269b0000040300483046022100a9e58ad\" +\n                        \"ee5adf4b5f5a7797480f80dc58041d78da8aad44c9cc0416a74cacb62022100eb463\" +\n                        \"ecf46c5725dfd50471804e4c665e8ae9790129b69706502a3e96fccf685007700877\" +\n                        \"5bfe7597cf88c43995fbdf36eff568d475636ff4ab560c1b4eaff5ea0830f0000017\" +\n                        \"173d326ad0000040300483046022100d452c0099540b5e18716db51348a200e8fd10\" +\n                        \"e8498d00108d4898d22b943422d022100f3c8677a062a55aca46dbb1049223480fff\" +\n                        \"e39d9e6fd386ca3a1c42455ef60670075007d3ef2f88fff88556824c2c0ca9e52897\" +\n                        \"92bc50e78097f2e6a9768997e22f0d70000017173d326b3000004030046304402207\" +\n                        \"e112c029b93e0db15d1de40eddb9aa7a55eeb4b48ce8c94ddf8ed71331e931e02207\" +\n                        \"8281e3c39c8e643b901c2bc6c470aa0ed3ad01bf17f0f207dc0f8a5ab541a70\"\n                    ).decodeHex(),\n                ),\n                Extension(\n                  id = keyUsage,\n                  critical = true,\n                  value = \"030205a0\".decodeHex(),\n                ),\n                Extension(\n                  id = extendedKeyUsage,\n                  critical = false,\n                  value = \"301406082b0601050507030106082b06010505070302\".decodeHex(),\n                ),\n                Extension(\n                  id = authorityInfoAccess,\n                  critical = false,\n                  value =\n                    (\n                      \"305a302306082b060105050730018617687474703a2f2f6f6373702e656\" +\n                        \"e74727573742e6e6574303306082b060105050730028627687474703a2f2f6169612\" +\n                        \"e656e74727573742e6e65742f6c316d2d636861696e3235362e636572\"\n                    ).decodeHex(),\n                ),\n                Extension(\n                  id = crlDistributionPoints,\n                  critical = false,\n                  value =\n                    (\n                      \"302a3028a026a0248622687474703a2f2f63726c2e656e74727573742e6\" +\n                        \"e65742f6c6576656c316d2e63726c\"\n                    ).decodeHex(),\n                ),\n                Extension(\n                  id = certificatePolicies,\n                  critical = false,\n                  value =\n                    (\n                      \"30413036060a6086480186fa6c0a01023028302606082b0601050507020\" +\n                        \"1161a687474703a2f2f7777772e656e74727573742e6e65742f72706130070605678\" +\n                        \"10c0101\"\n                    ).decodeHex(),\n                ),\n                Extension(\n                  id = authorityKeyIdentifier,\n                  critical = false,\n                  value = (\"30168014c3f7d0b52a30adaf0d9121703954ddbc8970c73a\").decodeHex(),\n                ),\n                Extension(\n                  id = subjectKeyIdentifier,\n                  critical = false,\n                  value = \"041475fd24c2df592599e32f3373e18c0450dd1b87b6\".decodeHex(),\n                ),\n                Extension(\n                  id = BASIC_CONSTRAINTS,\n                  critical = false,\n                  value =\n                    BasicConstraints(\n                      ca = false,\n                      maxIntermediateCas = null,\n                    ),\n                ),\n              ),\n          ),\n        signatureAlgorithm =\n          AlgorithmIdentifier(\n            algorithm = SHA256_WITH_RSA_ENCRYPTION,\n            parameters = null,\n          ),\n        signatureValue =\n          BitString(\n            byteString = signatureBytes,\n            unusedBitsCount = 0,\n          ),\n      ),\n    )\n  }\n\n  @Test\n  fun `certificate attributes`() {\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(3)\n        .commonName(\"Jurassic Park\")\n        .organizationalUnit(\"Gene Research\")\n        .addSubjectAlternativeName(\"*.example.com\")\n        .addSubjectAlternativeName(\"www.example.org\")\n        .validityInterval(-1000L, 2000L)\n        .serialNumber(17L)\n        .build()\n\n    val certificateByteString = certificate.certificate.encoded.toByteString()\n\n    val okHttpCertificate =\n      CertificateAdapters.certificate\n        .fromDer(certificateByteString)\n\n    assertThat(okHttpCertificate.basicConstraints).isEqualTo(\n      Extension(\n        id = BASIC_CONSTRAINTS,\n        critical = true,\n        value = BasicConstraints(true, 3),\n      ),\n    )\n    assertThat(okHttpCertificate.commonName).isEqualTo(\"Jurassic Park\")\n    assertThat(okHttpCertificate.organizationalUnitName).isEqualTo(\"Gene Research\")\n    assertThat(okHttpCertificate.subjectAlternativeNames).isEqualTo(\n      Extension(\n        id = SUBJECT_ALTERNATIVE_NAME,\n        critical = true,\n        value =\n          listOf(\n            CertificateAdapters.generalNameDnsName to \"*.example.com\",\n            CertificateAdapters.generalNameDnsName to \"www.example.org\",\n          ),\n      ),\n    )\n    assertThat(okHttpCertificate.tbsCertificate.validity).isEqualTo(Validity(-1000L, 2000L))\n    assertThat(okHttpCertificate.tbsCertificate.serialNumber).isEqualTo(BigInteger(\"17\"))\n  }\n\n  @Test\n  fun `missing subject alternative names`() {\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(3)\n        .commonName(\"Jurassic Park\")\n        .organizationalUnit(\"Gene Research\")\n        .validityInterval(-1000L, 2000L)\n        .serialNumber(17L)\n        .build()\n\n    val certificateByteString = certificate.certificate.encoded.toByteString()\n\n    val okHttpCertificate =\n      CertificateAdapters.certificate\n        .fromDer(certificateByteString)\n\n    assertThat(okHttpCertificate.commonName).isEqualTo(\"Jurassic Park\")\n    assertThat(okHttpCertificate.subjectAlternativeNames).isNull()\n  }\n\n  @Test\n  fun `public key`() {\n    val publicKeyBytes =\n      (\n        \"MIGJAoGBAICkUeG2stqfbyr6gyiVm5pN9YEDRXlowi+rfYGyWhC7ouW9fXAnhgShQKMOU8\" +\n          \"62mG3tcttSYGdsjM3z1crhQlUzpKqncrzwqbzPuAyt2t9Oib/bvjAvbl8gJH7IMRDl9RVgGYkApdkXVqgjSYigTH\" +\n          \"TEWxCEgnrfu/YzEkO6l3rXAgMBAAE=\"\n      ).decodeBase64()!!\n    val privateKeyBytes =\n      (\n        \"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAICkUeG2stqfbyr6gyiVm\" +\n          \"5pN9YEDRXlowi+rfYGyWhC7ouW9fXAnhgShQKMOU862mG3tcttSYGdsjM3z1crhQlUzpKqncrzwqbzPuAyt2t9Oi\" +\n          \"b/bvjAvbl8gJH7IMRDl9RVgGYkApdkXVqgjSYigTHTEWxCEgnrfu/YzEkO6l3rXAgMBAAECgYB99mhnB6piADOud\" +\n          \"dXv626NzUBTr4xbsYRTgSxHzwf50oFTTBSDuW+1IOBVyTWu94SSPyt0LllPbC8Di3sQSTnVGpSqAvEXknBMzIc0U\" +\n          \"O74Rn9p3gZjEenPt1l77fIBa2nK06/rdsJCoE/1P1JSfM9w7LU1RsTmseYMLeJl5F79gQJBAO/BbAKqg1yzK7Vij\" +\n          \"ygvBoUrr+rt2lbmKgcUQ/rxu8IIQk0M/xgJqSkXDXuOnboGM7sQSKfJAZUtT7xozvLzV7ECQQCJW59w7NIM0qZ/g\" +\n          \"IX2gcNZr1B/V3zcGlolTDciRm+fnKGNt2EEDKnVL3swzbEfTCa48IT0QKgZJqpXZERa26UHAkBLXmiP5f5pk8F3w\" +\n          \"cXzAeVw06z3k1IB41Tu6MX+CyPU+TeudRlz+wV8b0zDvK+EnRKCCbptVFj1Bkt8lQ4JfcnhAkAk2Y3Gz+HySrkcT\" +\n          \"7Cg12M/NkdUQnZe3jr88pt/+IGNwomc6Wt/mJ4fcWONTkGMcfOZff1NQeNXDAZ6941XCsIVAkASOg02PlVHLidU7\" +\n          \"mIE65swMM5/RNhS4aFjez/MwxFNOHaxc9VgCwYPXCLOtdf7AVovdyG0XWgbUXH+NyxKwboE\"\n      ).decodeBase64()!!\n\n    val x509PublicKey =\n      encodeKey(\n        algorithm = RSA_ENCRYPTION,\n        publicKeyBytes = publicKeyBytes,\n      )\n    val keyFactory = KeyFactory.getInstance(\"RSA\")\n    val publicKey = keyFactory.generatePublic(X509EncodedKeySpec(x509PublicKey.toByteArray()))\n    val privateKey = keyFactory.generatePrivate(PKCS8EncodedKeySpec(privateKeyBytes.toByteArray()))\n\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .keyPair(publicKey, privateKey)\n        .build()\n\n    val certificateByteString = certificate.certificate.encoded.toByteString()\n\n    val okHttpCertificate =\n      CertificateAdapters.certificate\n        .fromDer(certificateByteString)\n\n    assertThat(okHttpCertificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey)\n      .isEqualTo(BitString(publicKeyBytes, 0))\n  }\n\n  @Test fun `time before 2050 uses UTC_TIME`() {\n    val utcTimeDer = \"170d3439313233313233353935395a\".decodeHex()\n\n    val decoded = CertificateAdapters.time.fromDer(utcTimeDer)\n    val encoded = CertificateAdapters.time.toDer(decoded)\n\n    assertThat(decoded).isEqualTo(date(\"2049-12-31T23:59:59.000+0000\").time)\n    assertThat(encoded).isEqualTo(utcTimeDer)\n  }\n\n  @Test fun `time not before 2050 uses GENERALIZED_TIME`() {\n    val generalizedTimeDer = \"180f32303530303130313030303030305a\".decodeHex()\n\n    val decoded = CertificateAdapters.time.fromDer(generalizedTimeDer)\n    val encoded = CertificateAdapters.time.toDer(decoded)\n\n    assertThat(decoded).isEqualTo(date(\"2050-01-01T00:00:00.000+0000\").time)\n    assertThat(encoded).isEqualTo(generalizedTimeDer)\n  }\n\n  /**\n   * Conforming applications MUST be able to process validity dates that are encoded in either\n   * UTCTime or GeneralizedTime.\n   */\n  @Test fun `can read GENERALIZED_TIME before 2050`() {\n    val generalizedTimeDer = \"180f32303439313233313233353935395a\".decodeHex()\n\n    val decoded = CertificateAdapters.time.fromDer(generalizedTimeDer)\n    assertThat(decoded).isEqualTo(date(\"2049-12-31T23:59:59.000+0000\").time)\n  }\n\n  @Test fun `time before 1950 uses GENERALIZED_TIME`() {\n    val generalizedTimeDer = \"180f31393439313233313233353935395a\".decodeHex()\n\n    val decoded = CertificateAdapters.time.fromDer(generalizedTimeDer)\n    val encoded = CertificateAdapters.time.toDer(decoded)\n\n    assertThat(decoded).isEqualTo(date(\"1949-12-31T23:59:59.000+0000\").time)\n    assertThat(encoded).isEqualTo(generalizedTimeDer)\n  }\n\n  @Test\n  fun `reencode golden EC certificate`() {\n    val certificateByteString =\n      (\n        \"MIIBkjCCATmgAwIBAgIBETAKBggqhkjOPQQDAjAwMRYwFAYDVQQLDA1HZW5lIFJ\" +\n          \"lc2VhcmNoMRYwFAYDVQQDDA1KdXJhc3NpYyBQYXJrMB4XDTY5MTIzMTIzNTk1OVoXDTcwMDEwMTAwMDAwMlowMDE\" +\n          \"WMBQGA1UECwwNR2VuZSBSZXNlYXJjaDEWMBQGA1UEAwwNSnVyYXNzaWMgUGFyazBZMBMGByqGSM49AgEGCCqGSM4\" +\n          \"9AwEHA0IABKzhiMzpN+BkUSPLIKItu6O2iao2Pd7dxrvPdIs4xv9/2tPCVgUxevZ27qRcqZOnSd31ZP6B04vkXag\" +\n          \"/awy2/iujRDBCMBIGA1UdEwEB/wQIMAYBAf8CAQMwLAYDVR0RAQH/BCIwIIINKi5leGFtcGxlLmNvbYIPd3d3LmV\" +\n          \"4YW1wbGUub3JnMAoGCCqGSM49BAMCA0cAMEQCIHzutN/uzViLBXZ0slMqO5oz7ghgBgDbgo2ZyroVeQ/KAiB6Vqo\" +\n          \"QXETXce4IZyv3mwGWYePlXU2yMXtezbNluXqUxQ==\"\n      ).decodeBase64()!!\n\n    val decoded = CertificateAdapters.certificate.fromDer(certificateByteString)\n    val encoded = CertificateAdapters.certificate.toDer(decoded)\n\n    assertThat(encoded).isEqualTo(certificateByteString)\n  }\n\n  @Test\n  fun `reencode golden RSA certificate`() {\n    val certificateByteString =\n      (\n        \"MIIDHzCCAgegAwIBAgIBETANBgkqhkiG9w0BAQsFADAwMRYwFAYDVQQLDA1HZW5\" +\n          \"lIFJlc2VhcmNoMRYwFAYDVQQDDA1KdXJhc3NpYyBQYXJrMB4XDTY5MTIzMTIzNTk1OVoXDTcwMDEwMTAwMDAwMlo\" +\n          \"wMDEWMBQGA1UECwwNR2VuZSBSZXNlYXJjaDEWMBQGA1UEAwwNSnVyYXNzaWMgUGFyazCCASIwDQYJKoZIhvcNAQE\" +\n          \"BBQADggEPADCCAQoCggEBAMfROxfCzmxIX5bDSZt6hstXALVeiywFFzTLW5UI0eKSDCliojmiKBcGR5ln7gVe6/t\" +\n          \"me35J9n+Xe5LLmRogMo1CxCoyJxuDX4RrTpPGSepJCrvsBaMA7bQXc/9SbckPF4DYGbE5j3L6IyFU++8RKep/xjc\" +\n          \"FAK4yhEgriDh7Gb+sbG6Mv2qTO4p6TR9WhMKXhMgHdk1JYyaSsJ+tSruKiPVmMAcQLBWgNez6MUIC1WVDyvCvfXI\" +\n          \"pgsxosVCMtEDSllYe2lVta5tq1RkyzrvkazMEROK+0CVTfg8CadyBn83WTdWRsAX3qiwng8fQU3R4D9HuF/monfH\" +\n          \"XuHsr53J+6v8CAwEAAaNEMEIwEgYDVR0TAQH/BAgwBgEB/wIBAzAsBgNVHREBAf8EIjAggg0qLmV4YW1wbGUuY29\" +\n          \"tgg93d3cuZXhhbXBsZS5vcmcwDQYJKoZIhvcNAQELBQADggEBAC/+HbZBfVzazPARyI90ot3wzyEmCnXEotNhyl3\" +\n          \"0QHZ6UGtJwvVBqY187xg9whytqdMFmadCp8FQT/dLRUn27gtQLOju4FfA3yetJ5oWjbgkaAr7YGP7Auz3o+w51aa\" +\n          \"YpseFTZ/zABwnADSiHCIl35TGZJa1XOl32+RWn9VhT92zm3R12FMBovpMFaDckSJAi0jhMHm/QsFK66V0DZxdvl9\" +\n          \"LX/UI7q870lojkolCmDJfftAnd2eazoY/O3TqP/duRH522U+C42nXRg9y0CFgzVWmee4EzsCHhkeHUDbsijgSHd4\" +\n          \"vjraGi943vN59SjQrflkISUnOqChOaWP0oSztRUA=\"\n      ).decodeBase64()!!\n\n    val decoded = CertificateAdapters.certificate.fromDer(certificateByteString)\n    val encoded = CertificateAdapters.certificate.toDer(decoded)\n\n    assertThat(encoded).isEqualTo(certificateByteString)\n  }\n\n  @Test\n  fun `private key info`() {\n    val privateKeyInfoByteString =\n      (\n        \"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAICkUeG2stqf\" +\n          \"byr6gyiVm5pN9YEDRXlowi+rfYGyWhC7ouW9fXAnhgShQKMOU862mG3tcttSYGdsjM3z1crhQlUzpKqncrzwqbzP\" +\n          \"uAyt2t9Oib/bvjAvbl8gJH7IMRDl9RVgGYkApdkXVqgjSYigTHTEWxCEgnrfu/YzEkO6l3rXAgMBAAECgYB99mhn\" +\n          \"B6piADOuddXv626NzUBTr4xbsYRTgSxHzwf50oFTTBSDuW+1IOBVyTWu94SSPyt0LllPbC8Di3sQSTnVGpSqAvEX\" +\n          \"knBMzIc0UO74Rn9p3gZjEenPt1l77fIBa2nK06/rdsJCoE/1P1JSfM9w7LU1RsTmseYMLeJl5F79gQJBAO/BbAKq\" +\n          \"g1yzK7VijygvBoUrr+rt2lbmKgcUQ/rxu8IIQk0M/xgJqSkXDXuOnboGM7sQSKfJAZUtT7xozvLzV7ECQQCJW59w\" +\n          \"7NIM0qZ/gIX2gcNZr1B/V3zcGlolTDciRm+fnKGNt2EEDKnVL3swzbEfTCa48IT0QKgZJqpXZERa26UHAkBLXmiP\" +\n          \"5f5pk8F3wcXzAeVw06z3k1IB41Tu6MX+CyPU+TeudRlz+wV8b0zDvK+EnRKCCbptVFj1Bkt8lQ4JfcnhAkAk2Y3G\" +\n          \"z+HySrkcT7Cg12M/NkdUQnZe3jr88pt/+IGNwomc6Wt/mJ4fcWONTkGMcfOZff1NQeNXDAZ6941XCsIVAkASOg02\" +\n          \"PlVHLidU7mIE65swMM5/RNhS4aFjez/MwxFNOHaxc9VgCwYPXCLOtdf7AVovdyG0XWgbUXH+NyxKwboE\"\n      ).decodeBase64()!!\n\n    val decoded = CertificateAdapters.privateKeyInfo.fromDer(privateKeyInfoByteString)\n\n    assertThat(decoded.version).isEqualTo(0L)\n    assertThat(decoded.algorithmIdentifier).isEqualTo(AlgorithmIdentifier(RSA_ENCRYPTION, null))\n    assertThat(decoded.privateKey.size).isEqualTo(607)\n\n    val encoded = CertificateAdapters.privateKeyInfo.toDer(decoded)\n    assertThat(encoded).isEqualTo(privateKeyInfoByteString)\n  }\n\n  @Test\n  fun `RSA issuer and signature`() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .rsa2048()\n        .build()\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(root)\n        .rsa2048()\n        .build()\n\n    val certificateByteString = certificate.certificate.encoded.toByteString()\n\n    // Valid signature.\n    val okHttpCertificate =\n      CertificateAdapters.certificate\n        .fromDer(certificateByteString)\n    println(okHttpCertificate)\n    assertThat(okHttpCertificate.checkSignature(root.keyPair.public)).isTrue()\n\n    // Invalid signature.\n    val okHttpCertificateWithBadSignature =\n      okHttpCertificate.copy(\n        signatureValue =\n          okHttpCertificate.signatureValue.copy(\n            byteString = okHttpCertificate.signatureValue.byteString.offByOneBit(),\n          ),\n      )\n    assertThat(okHttpCertificateWithBadSignature.checkSignature(root.keyPair.public)).isFalse()\n\n    // Wrong public key.\n    assertThat(okHttpCertificate.checkSignature(certificate.keyPair.public)).isFalse()\n  }\n\n  @Test\n  fun `EC issuer and signature`() {\n    val root =\n      HeldCertificate\n        .Builder()\n        .certificateAuthority(0)\n        .ecdsa256()\n        .build()\n    val certificate =\n      HeldCertificate\n        .Builder()\n        .signedBy(root)\n        .ecdsa256()\n        .build()\n\n    val certificateByteString = certificate.certificate.encoded.toByteString()\n\n    // Valid signature.\n    val okHttpCertificate =\n      CertificateAdapters.certificate\n        .fromDer(certificateByteString)\n    assertThat(okHttpCertificate.checkSignature(root.keyPair.public)).isTrue()\n\n    // Invalid signature.\n    val okHttpCertificateWithBadSignature =\n      okHttpCertificate.copy(\n        signatureValue =\n          okHttpCertificate.signatureValue.copy(\n            byteString = okHttpCertificate.signatureValue.byteString.offByOneBit(),\n          ),\n      )\n    assertThat(okHttpCertificateWithBadSignature.checkSignature(root.keyPair.public)).isFalse()\n\n    // Wrong public key.\n    assertThat(okHttpCertificate.checkSignature(certificate.keyPair.public)).isFalse()\n  }\n\n  /**\n   * We don't have API support for rfc822Name values (email addresses) in the subject alternative\n   * name, but we don't crash either.\n   */\n  @Test\n  fun `unsupported general name tag`() {\n    val certificateByteString =\n      (\n        \"MIIFEDCCA/igAwIBAgIRAJK4dE9xztDibHKj2NXZJbIwDQYJKoZIhvcNAQELBQA\" +\n          \"wSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmV\" +\n          \"UcnVzdCBDQTAeFw0xNjA5MDExNDM1MzVaFw0yNDA5MjkxNDM1MzVaMIG1MQswCQYDVQQGEwJVUzERMA8GA1UECBM\" +\n          \"ISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE9MDs\" +\n          \"GA1UEAxM0VHJ1c3R3YXZlIE9yZ2FuaXphdGlvbiBWYWxpZGF0aW9uIFNIQTI1NiBDQSwgTGV2ZWwgMTEfMB0GCSq\" +\n          \"GSIb3DQEJARYQY2FAdHJ1c3R3YXZlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPTqIZSRwS\" +\n          \"f47okE5omzktvKR7wgqdWzznOnpUOtgwmBPwNeCV1LSPMmlHPZxY4enTc0eyoxTxKv6g6ZUJe39U74eYnwTTT9sE\" +\n          \"OnvNtE1pTzuB4Uf+YOPt4hZidTe5Ba8Q6dfz/Ht/vZXCbF3JFwrXxZEPbJaICap2grIqYHax+IEIYnBQC+WKh8Ng\" +\n          \"Cn3LWS0j6cYSN8SEjFf5SEMGT1iNtttb/QC3JKJIeaVunUyvMfMjVFMntc7eZrFs6rp3wY1WFVI+fy17uOoUvfTH\" +\n          \"8bvNAESUch7FyLh2zM8FVxqilT2XygHRwZeXtxJQozcDcvh4ItPb0uz6AFIYwn/8Gzp0CAwEAAaOCAYUwggGBMBI\" +\n          \"GA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFMrOHRgDdx4c83xYsppwqAiAFvSuMA4GA1UdDwEB/wQEAwIBhjA\" +\n          \"yBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLnRydXN0d2F2ZS5jb20vU1RDQS5jcmwwPQYDVR0gBDYwNDAyBgR\" +\n          \"VHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vc3NsLnRydXN0d2F2ZS5jb20vQ0EwbAYIKwYBBQUHAQEEYDBeMCU\" +\n          \"GCCsGAQUFBzABhhlodHRwOi8vb2NzcC50cnVzdHdhdmUuY29tMDUGCCsGAQUFBzAChilodHRwOi8vc3NsLnRydXN\" +\n          \"0d2F2ZS5jb20vaXNzdWVycy9TVENBLmNydDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwHwYDVR0jBBg\" +\n          \"wFoAUQjK2FvoE/f5dS3rD/fdMQB1aQ68wGwYDVR0RBBQwEoEQY2FAdHJ1c3R3YXZlLmNvbTANBgkqhkiG9w0BAQs\" +\n          \"FAAOCAQEAC0OvN7/UJBcRDXchA4b2qJo7mBD05+XR96N7vucMaanz26CnUxs1o8DcBckpqyEXCxdOanIr+/UJNbB\" +\n          \"LXLJCzNLJEJcgV9TjbVu33eQR23yMuXD+cZsqLMF+L5IIM47W8dlwKJvMy0xs7Jb1S3NOIhcoVu+XPzRsgKv8Yi2\" +\n          \"B6l278RfzegiCx4vYJv0pBjFzizEiFH9bWTYIOlIJJSM57hoICgjCTS8BoEgndwWIyc/nEmlYaUwmCo9QynY+UmW\" +\n          \"1WPWmVITEJPMdMK6AZqvvaWmuHJ6/vURaz+Hoc5D3z0yJDDCkv52bXV04ZoF6cbcWry7JvNA+djvay/4BRR4SZQ==\"\n      ).decodeBase64()!!\n\n    val decoded = CertificateAdapters.certificate.fromDer(certificateByteString)\n    assertThat(decoded.subjectAlternativeNames).isEqualTo(\n      Extension(\n        id = SUBJECT_ALTERNATIVE_NAME,\n        critical = false,\n        value =\n          listOf(\n            Adapters.ANY_VALUE to\n              AnyValue(\n                tagClass = DerHeader.TAG_CLASS_CONTEXT_SPECIFIC,\n                tag = 1L,\n                constructed = false,\n                length = 16,\n                bytes = \"ca@trustwave.com\".encodeUtf8(),\n              ),\n          ),\n      ),\n    )\n  }\n\n  /** Converts public key bytes to SubjectPublicKeyInfo bytes. */\n  private fun encodeKey(\n    algorithm: String,\n    publicKeyBytes: ByteString,\n  ): ByteString {\n    val subjectPublicKeyInfo =\n      SubjectPublicKeyInfo(\n        algorithm = AlgorithmIdentifier(algorithm = algorithm, parameters = null),\n        subjectPublicKey = BitString(publicKeyBytes, 0),\n      )\n    return CertificateAdapters.subjectPublicKeyInfo.toDer(subjectPublicKeyInfo)\n  }\n\n  /** Returns a byte string that differs from this one by one bit. */\n  private fun ByteString.offByOneBit(): ByteString =\n    Buffer()\n      .write(this, 0, size - 1)\n      .writeByte(this[size - 1].toInt() xor 1)\n      .readByteString()\n\n  private fun date(s: String): Date =\n    SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSSZ\").run {\n      timeZone = TimeZone.getTimeZone(\"GMT\")\n      parse(s)\n    }\n}\n"
  },
  {
    "path": "okhttp-tls/src/test/java/okhttp3/tls/internal/der/DerTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.tls.internal.der\n\nimport assertk.assertThat\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isFalse\nimport assertk.assertions.isNull\nimport assertk.assertions.isTrue\nimport java.math.BigInteger\nimport java.net.InetAddress\nimport java.net.ProtocolException\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.TimeZone\nimport kotlin.test.assertFailsWith\nimport okhttp3.tls.internal.der.CertificateAdapters.generalNameDnsName\nimport okhttp3.tls.internal.der.CertificateAdapters.generalNameIpAddress\nimport okhttp3.tls.internal.der.ObjectIdentifiers.BASIC_CONSTRAINTS\nimport okhttp3.tls.internal.der.ObjectIdentifiers.COMMON_NAME\nimport okhttp3.tls.internal.der.ObjectIdentifiers.SHA256_WITH_RSA_ENCRYPTION\nimport okhttp3.tls.internal.der.ObjectIdentifiers.SUBJECT_ALTERNATIVE_NAME\nimport okio.Buffer\nimport okio.ByteString.Companion.decodeHex\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.ByteString.Companion.toByteString\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\n\ninternal class DerTest {\n  @Test fun `decode tag and length`() {\n    val buffer =\n      Buffer()\n        .writeByte(0b00011110)\n        .writeByte(0b10000001)\n        .writeByte(0b11001001)\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_UNIVERSAL)\n      assertThat(header.tag).isEqualTo(30)\n      assertThat(header.constructed).isFalse()\n      assertThat(header.length).isEqualTo(201)\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `decode length encoded with leading zero byte`() {\n    val buffer =\n      Buffer()\n        .writeByte(0b00000010)\n        .writeByte(0b10000010)\n        .writeByte(0b00000000)\n        .writeByte(0b01111111)\n\n    val derReader = DerReader(buffer)\n\n    assertFailsWith<ProtocolException> {\n      derReader.read(\"test\") {}\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"invalid encoding for length\")\n    }\n  }\n\n  @Test fun `decode length not encoded in shortest form possible`() {\n    val buffer =\n      Buffer()\n        .writeByte(0b00000010)\n        .writeByte(0b10000001)\n        .writeByte(0b01111111)\n\n    val derReader = DerReader(buffer)\n\n    assertFailsWith<ProtocolException> {\n      derReader.read(\"test\") {}\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"invalid encoding for length\")\n    }\n  }\n\n  @Test fun `decode length equal to Long MAX_VALUE`() {\n    val buffer =\n      Buffer()\n        .writeByte(0b00000010)\n        .writeByte(0b10001000)\n        .writeByte(0b01111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n\n    val derReader = DerReader(buffer)\n\n    val header = derReader.readHeader()\n    assertThat(header.length).isEqualTo(Long.MAX_VALUE)\n  }\n\n  @Test fun `decode length overflowing Long`() {\n    val buffer =\n      Buffer()\n        .writeByte(0b00000010)\n        .writeByte(0b10001000)\n        .writeByte(0b10000000)\n        .writeByte(0b00000000)\n        .writeByte(0b00000000)\n        .writeByte(0b00000000)\n        .writeByte(0b00000000)\n        .writeByte(0b00000000)\n        .writeByte(0b00000000)\n        .writeByte(0b00000000)\n\n    val derReader = DerReader(buffer)\n\n    assertFailsWith<ProtocolException> {\n      derReader.read(\"test\") {}\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"length > Long.MAX_VALUE\")\n    }\n  }\n\n  @Test fun `decode length encoded with more than 8 bytes`() {\n    val buffer =\n      Buffer()\n        .writeByte(0b00000010)\n        .writeByte(0b10001001)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n        .writeByte(0b11111111)\n\n    val derReader = DerReader(buffer)\n\n    assertFailsWith<ProtocolException> {\n      derReader.read(\"test\") {}\n    }.also { expected ->\n      assertThat(expected.message)\n        .isEqualTo(\"length encoded with more than 8 bytes is not supported\")\n    }\n  }\n\n  @Test fun `encode tag and length`() {\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\"test\", tagClass = DerHeader.TAG_CLASS_UNIVERSAL, tag = 30L) {\n      derWriter.writeUtf8(\"a\".repeat(201))\n    }\n\n    assertThat(buffer.readByteString(3)).isEqualTo(\"1e81c9\".decodeHex())\n    assertThat(buffer.readUtf8()).isEqualTo(\"a\".repeat(201))\n  }\n\n  @Test fun `decode primitive bit string`() {\n    val buffer =\n      Buffer()\n        .write(\"0307040A3B5F291CD0\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(3L)\n      assertThat(derReader.readBitString()).isEqualTo(BitString(\"0A3B5F291CD0\".decodeHex(), 4))\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode primitive bit string`() {\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\"test\", tagClass = DerHeader.TAG_CLASS_UNIVERSAL, tag = 3L) {\n      derWriter.writeBitString(BitString(\"0A3B5F291CD0\".decodeHex(), 4))\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"0307040A3B5F291CD0\".decodeHex())\n  }\n\n  @Test fun `decode primitive string`() {\n    val buffer =\n      Buffer()\n        .write(\"1A054A6F6E6573\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(26L)\n      assertThat(header.constructed).isFalse()\n      assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_UNIVERSAL)\n      assertThat(derReader.readOctetString()).isEqualTo(\"Jones\".encodeUtf8())\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode primitive string`() {\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\"test\", tagClass = DerHeader.TAG_CLASS_UNIVERSAL, tag = 26L) {\n      derWriter.writeOctetString(\"Jones\".encodeUtf8())\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"1A054A6F6E6573\".decodeHex())\n  }\n\n  @Test fun `decode implicit prefixed type`() {\n    // Type1 ::= VisibleString\n    // Type2 ::= [APPLICATION 3] IMPLICIT Type1\n    val buffer =\n      Buffer()\n        .write(\"43054A6F6E6573\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(3L)\n      assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_APPLICATION)\n      assertThat(derReader.readOctetString()).isEqualTo(\"Jones\".encodeUtf8())\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode implicit prefixed type`() {\n    // Type1 ::= VisibleString\n    // Type2 ::= [APPLICATION 3] IMPLICIT Type1\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\"test\", tagClass = DerHeader.TAG_CLASS_APPLICATION, tag = 3L) {\n      derWriter.writeOctetString(\"Jones\".encodeUtf8())\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"43054A6F6E6573\".decodeHex())\n  }\n\n  @Test fun `decode tagged implicit prefixed type`() {\n    // Type1 ::= VisibleString\n    // Type2 ::= [APPLICATION 3] IMPLICIT Type1\n    // Type3 ::= [2] Type2\n    val buffer =\n      Buffer()\n        .write(\"A20743054A6F6E6573\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(2L)\n      assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_CONTEXT_SPECIFIC)\n      assertThat(header.length).isEqualTo(7L)\n\n      derReader.read(\"test\") { header ->\n        assertThat(header.tag).isEqualTo(3L)\n        assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_APPLICATION)\n        assertThat(header.length).isEqualTo(5L)\n        assertThat(derReader.readOctetString()).isEqualTo(\"Jones\".encodeUtf8())\n      }\n\n      assertThat(derReader.hasNext()).isFalse()\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode tagged implicit prefixed type`() {\n    // Type1 ::= VisibleString\n    // Type2 ::= [APPLICATION 3] IMPLICIT Type1\n    // Type3 ::= [2] Type2\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\"test\", tagClass = DerHeader.TAG_CLASS_CONTEXT_SPECIFIC, tag = 2L) {\n      derWriter.write(\"test\", tagClass = DerHeader.TAG_CLASS_APPLICATION, tag = 3L) {\n        derWriter.writeOctetString(\"Jones\".encodeUtf8())\n      }\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"A20743054A6F6E6573\".decodeHex())\n  }\n\n  @Test fun `decode implicit tagged implicit prefixed type`() {\n    // Type1 ::= VisibleString\n    // Type2 ::= [APPLICATION 3] IMPLICIT Type1\n    // Type3 ::= [2] Type2\n    // Type4 ::= [APPLICATION 7] IMPLICIT Type3\n    val buffer =\n      Buffer()\n        .write(\"670743054A6F6E6573\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(7L)\n      assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_APPLICATION)\n      assertThat(header.length).isEqualTo(7L)\n\n      derReader.read(\"test\") { header2 ->\n        assertThat(header2.tag).isEqualTo(3L)\n        assertThat(header2.tagClass).isEqualTo(DerHeader.TAG_CLASS_APPLICATION)\n        assertThat(header2.length).isEqualTo(5L)\n        assertThat(derReader.readOctetString()).isEqualTo(\"Jones\".encodeUtf8())\n      }\n\n      assertThat(derReader.hasNext()).isFalse()\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode implicit tagged implicit prefixed type`() {\n    // Type1 ::= VisibleString\n    // Type2 ::= [APPLICATION 3] IMPLICIT Type1\n    // Type3 ::= [2] Type2\n    // Type4 ::= [APPLICATION 7] IMPLICIT Type3\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\"test\", tagClass = DerHeader.TAG_CLASS_APPLICATION, tag = 7L) {\n      derWriter.write(\"test\", tagClass = DerHeader.TAG_CLASS_APPLICATION, tag = 3L) {\n        derWriter.writeOctetString(\"Jones\".encodeUtf8())\n      }\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"670743054A6F6E6573\".decodeHex())\n  }\n\n  @Test fun `decode implicit implicit prefixed type`() {\n    // Type1 ::= VisibleString\n    // Type2 ::= [APPLICATION 3] IMPLICIT Type1\n    // Type5 ::= [2] IMPLICIT Type2\n    val buffer =\n      Buffer()\n        .write(\"82054A6F6E6573\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(2L)\n      assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_CONTEXT_SPECIFIC)\n      assertThat(header.length).isEqualTo(5L)\n      assertThat(derReader.readOctetString()).isEqualTo(\"Jones\".encodeUtf8())\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode implicit implicit prefixed type`() {\n    // Type1 ::= VisibleString\n    // Type2 ::= [APPLICATION 3] IMPLICIT Type1\n    // Type5 ::= [2] IMPLICIT Type2\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\n      name = \"test\",\n      tagClass = DerHeader.TAG_CLASS_CONTEXT_SPECIFIC,\n      tag = 2L,\n    ) {\n      derWriter.writeOctetString(\"Jones\".encodeUtf8())\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"82054A6F6E6573\".decodeHex())\n  }\n\n  @Test fun `decode object identifier without adapter`() {\n    val buffer =\n      Buffer()\n        .write(\"0603883703\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(6L)\n      assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_UNIVERSAL)\n      assertThat(header.length).isEqualTo(3L)\n      assertThat(derReader.readObjectIdentifier()).isEqualTo(\"2.999.3\")\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode object identifier without adapter`() {\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\n      name = \"test\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 6L,\n    ) {\n      derWriter.writeObjectIdentifier(\"2.999.3\")\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"0603883703\".decodeHex())\n  }\n\n  @Test fun `decode relative object identifier`() {\n    val buffer =\n      Buffer()\n        .write(\"0D04c27B0302\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(13L)\n      assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_UNIVERSAL)\n      assertThat(header.length).isEqualTo(4L)\n      assertThat(derReader.readRelativeObjectIdentifier()).isEqualTo(\"8571.3.2\")\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode relative object identifier`() {\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\n      name = \"test\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 13L,\n    ) {\n      derWriter.writeRelativeObjectIdentifier(\"8571.3.2\")\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"0D04c27B0302\".decodeHex())\n  }\n\n  @Test fun `decode raw sequence`() {\n    val buffer =\n      Buffer()\n        .write(\"300A\".decodeHex())\n        .write(\"1505\".decodeHex())\n        .write(\"Smith\".encodeUtf8())\n        .write(\"01\".decodeHex())\n        .write(\"01\".decodeHex())\n        .write(\"FF\".decodeHex())\n\n    val derReader = DerReader(buffer)\n\n    derReader.read(\"test\") { header ->\n      assertThat(header.tag).isEqualTo(16L)\n\n      derReader.read(\"test\") { header2 ->\n        assertThat(header2.tag).isEqualTo(21L)\n        assertThat(derReader.readOctetString()).isEqualTo(\"Smith\".encodeUtf8())\n      }\n\n      derReader.read(\"test\") { header3 ->\n        assertThat(header3.tag).isEqualTo(1L)\n        assertThat(derReader.readBoolean()).isTrue()\n      }\n\n      assertThat(derReader.hasNext()).isFalse()\n    }\n\n    assertThat(derReader.hasNext()).isFalse()\n  }\n\n  @Test fun `encode raw sequence`() {\n    val buffer = Buffer()\n    val derWriter = DerWriter(buffer)\n\n    derWriter.write(\n      name = \"test\",\n      tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n      tag = 16L,\n    ) {\n      derWriter.write(\n        name = \"test\",\n        tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n        tag = 21L,\n      ) {\n        derWriter.writeOctetString(\"Smith\".encodeUtf8())\n      }\n\n      derWriter.write(\n        name = \"test\",\n        tagClass = DerHeader.TAG_CLASS_UNIVERSAL,\n        tag = 1L,\n      ) {\n        derWriter.writeBoolean(true)\n      }\n    }\n\n    assertThat(buffer.readByteString()).isEqualTo(\"300a1505536d6974680101ff\".decodeHex())\n  }\n\n  @Test fun `sequence of`() {\n    val bytes = \"3009020107020108020109\".decodeHex()\n    val sequenceOf = listOf(7L, 8L, 9L)\n    val adapter = Adapters.INTEGER_AS_LONG.asSequenceOf()\n    assertThat(adapter.fromDer(bytes)).isEqualTo(sequenceOf)\n    assertThat(adapter.toDer(sequenceOf)).isEqualTo(bytes)\n  }\n\n  @Test fun `point with only x set`() {\n    val bytes = \"3003800109\".decodeHex()\n    val point = Point(9L, null)\n    assertThat(Point.ADAPTER.fromDer(bytes)).isEqualTo(point)\n    assertThat(Point.ADAPTER.toDer(point)).isEqualTo(bytes)\n  }\n\n  @Test fun `point with only y set`() {\n    val bytes = \"3003810109\".decodeHex()\n    val point = Point(null, 9L)\n    assertThat(Point.ADAPTER.fromDer(bytes)).isEqualTo(point)\n    assertThat(Point.ADAPTER.toDer(point)).isEqualTo(bytes)\n  }\n\n  @Test fun `point with both fields set`() {\n    val bytes = \"3006800109810109\".decodeHex()\n    val point = Point(9L, 9L)\n    assertThat(Point.ADAPTER.fromDer(bytes)).isEqualTo(point)\n    assertThat(Point.ADAPTER.toDer(point)).isEqualTo(bytes)\n  }\n\n  @Test fun `implicit tag`() {\n    // [5] IMPLICIT UTF8String\n    val bytes = \"85026869\".decodeHex()\n    val implicitAdapter = Adapters.UTF8_STRING.withTag(tag = 5L)\n    assertThat(implicitAdapter.fromDer(bytes)).isEqualTo(\"hi\")\n    assertThat(implicitAdapter.toDer(\"hi\")).isEqualTo(bytes)\n  }\n\n  @Test fun `encode implicit`() {\n    // [5] IMPLICIT UTF8String\n    val implicitAdapter = Adapters.UTF8_STRING.withTag(tag = 5L)\n    val string = implicitAdapter.fromDer(\"85026869\".decodeHex())\n    assertThat(string).isEqualTo(\"hi\")\n  }\n\n  @Test fun `explicit tag`() {\n    // [5] EXPLICIT UTF8String\n    val bytes = \"A5040C026869\".decodeHex()\n    val explicitAdapter = Adapters.UTF8_STRING.withExplicitBox(tag = 5L)\n    assertThat(explicitAdapter.fromDer(bytes)).isEqualTo(\"hi\")\n    assertThat(explicitAdapter.toDer(\"hi\")).isEqualTo(bytes)\n  }\n\n  @Test fun `boolean`() {\n    val bytes = \"0101FF\".decodeHex()\n    assertThat(Adapters.BOOLEAN.fromDer(bytes)).isEqualTo(true)\n    assertThat(Adapters.BOOLEAN.toDer(true)).isEqualTo(bytes)\n  }\n\n  @Test fun `positive integer`() {\n    val bytes = \"020132\".decodeHex()\n    assertThat(Adapters.INTEGER_AS_LONG.toDer(50L)).isEqualTo(bytes)\n    assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(50L)\n  }\n\n  @Test fun `decode negative integer`() {\n    val bytes = \"02019c\".decodeHex()\n    assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(-100L)\n    assertThat(Adapters.INTEGER_AS_LONG.toDer(-100L)).isEqualTo(bytes)\n  }\n\n  @Test fun `five byte integer`() {\n    val bytes = \"02058000000001\".decodeHex()\n    assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(-549755813887L)\n    assertThat(Adapters.INTEGER_AS_LONG.toDer(-549755813887L)).isEqualTo(bytes)\n  }\n\n  @Test fun `eight zeros`() {\n    val bytes = \"020200ff\".decodeHex()\n    assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(255)\n    assertThat(Adapters.INTEGER_AS_LONG.toDer(255)).isEqualTo(bytes)\n  }\n\n  @Test fun `eight ones`() {\n    val bytes = \"0201ff\".decodeHex()\n    assertThat(Adapters.INTEGER_AS_LONG.toDer(-1L)).isEqualTo(bytes)\n    assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(-1L)\n  }\n\n  @Test fun `last byte all zeros`() {\n    val bytes = \"0202ff00\".decodeHex()\n    assertThat(Adapters.INTEGER_AS_LONG.toDer(-256L)).isEqualTo(bytes)\n    assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(-256L)\n  }\n\n  @Test fun `max long`() {\n    val bytes = \"02087fffffffffffffff\".decodeHex()\n    assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(Long.MAX_VALUE)\n    assertThat(Adapters.INTEGER_AS_LONG.toDer(Long.MAX_VALUE)).isEqualTo(bytes)\n  }\n\n  @Test fun `min long`() {\n    val bytes = \"02088000000000000000\".decodeHex()\n    assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(Long.MIN_VALUE)\n    assertThat(Adapters.INTEGER_AS_LONG.toDer(Long.MIN_VALUE)).isEqualTo(bytes)\n  }\n\n  @Test fun `bigger than max long`() {\n    val bytes = \"0209008000000000000001\".decodeHex()\n    val bigInteger = BigInteger(\"9223372036854775809\")\n    assertThat(Adapters.INTEGER_AS_BIG_INTEGER.fromDer(bytes)).isEqualTo(bigInteger)\n    assertThat(Adapters.INTEGER_AS_BIG_INTEGER.toDer(bigInteger)).isEqualTo(bytes)\n  }\n\n  @Test fun `utf8 string`() {\n    val bytes = \"0c04f09f988e\".decodeHex()\n    assertThat(Adapters.UTF8_STRING.fromDer(bytes)).isEqualTo(\"\\uD83D\\uDE0E\")\n    assertThat(Adapters.UTF8_STRING.toDer(\"\\uD83D\\uDE0E\")).isEqualTo(bytes)\n  }\n\n  @Test fun `ia5 string`() {\n    val bytes = \"16026869\".decodeHex()\n    assertThat(Adapters.IA5_STRING.fromDer(bytes)).isEqualTo(\"hi\")\n    assertThat(Adapters.IA5_STRING.toDer(\"hi\")).isEqualTo(bytes)\n  }\n\n  @Test fun `printable string`() {\n    val bytes = \"13026869\".decodeHex()\n    assertThat(Adapters.PRINTABLE_STRING.fromDer(bytes)).isEqualTo(\"hi\")\n    assertThat(Adapters.PRINTABLE_STRING.toDer(\"hi\")).isEqualTo(bytes)\n  }\n\n  @Test fun `cannot decode utc time with offset`() {\n    assertFailsWith<ProtocolException> {\n      Adapters.UTC_TIME.fromDer(\"17113139313231353139303231302d30383030\".decodeHex())\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"Failed to parse UTCTime 191215190210-0800\")\n    }\n  }\n\n  @Test fun `utc time`() {\n    val bytes = \"170d3139313231363033303231305a\".decodeHex()\n    val utcTime = date(\"2019-12-16T03:02:10.000+0000\").time\n    assertThat(Adapters.UTC_TIME.toDer(utcTime)).isEqualTo(bytes)\n    assertThat(Adapters.UTC_TIME.fromDer(bytes)).isEqualTo(utcTime)\n  }\n\n  @Test fun `cannot decode malformed utc time`() {\n    val bytes = \"170d3139313231362333303231305a\".decodeHex()\n    assertFailsWith<ProtocolException> {\n      Adapters.UTC_TIME.fromDer(bytes)\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"Failed to parse UTCTime 191216#30210Z\")\n    }\n  }\n\n  @Test fun `cannot decode generalized time with offset`() {\n    assertFailsWith<ProtocolException> {\n      Adapters.GENERALIZED_TIME.fromDer(\"181332303139313231353139303231302d30383030\".decodeHex())\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"Failed to parse GeneralizedTime 20191215190210-0800\")\n    }\n  }\n\n  @Test fun `generalized time`() {\n    val bytes = \"180f32303139313231363033303231305a\".decodeHex()\n    val generalizedTime = date(\"2019-12-16T03:02:10.000+0000\").time\n    assertThat(Adapters.GENERALIZED_TIME.fromDer(bytes)).isEqualTo(generalizedTime)\n    assertThat(Adapters.GENERALIZED_TIME.toDer(generalizedTime)).isEqualTo(bytes)\n  }\n\n  @Test fun `cannot decode malformed generalized time`() {\n    val bytes = \"180f32303139313231362333303231305a\".decodeHex()\n    assertFailsWith<ProtocolException> {\n      Adapters.GENERALIZED_TIME.fromDer(bytes)\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"Failed to parse GeneralizedTime 20191216#30210Z\")\n    }\n  }\n\n  @Test fun `parse utc time`() {\n    assertThat(Adapters.parseUtcTime(\"920521000000Z\"))\n      .isEqualTo(date(\"1992-05-21T00:00:00.000+0000\").time)\n    assertThat(Adapters.parseUtcTime(\"920622123421Z\"))\n      .isEqualTo(date(\"1992-06-22T12:34:21.000+0000\").time)\n    assertThat(Adapters.parseUtcTime(\"920722132100Z\"))\n      .isEqualTo(date(\"1992-07-22T13:21:00.000+0000\").time)\n  }\n\n  @Test fun `decode utc time two digit year cutoff is 1950`() {\n    assertThat(Adapters.parseUtcTime(\"500101000000Z\"))\n      .isEqualTo(date(\"1950-01-01T00:00:00.000+0000\").time)\n    assertThat(Adapters.parseUtcTime(\"500101010000Z\"))\n      .isEqualTo(date(\"1950-01-01T01:00:00.000+0000\").time)\n\n    assertThat(Adapters.parseUtcTime(\"491231225959Z\"))\n      .isEqualTo(date(\"2049-12-31T22:59:59.000+0000\").time)\n    assertThat(Adapters.parseUtcTime(\"491231235959Z\"))\n      .isEqualTo(date(\"2049-12-31T23:59:59.000+0000\").time)\n  }\n\n  @Test fun `encode utc time two digit year cutoff is 1950`() {\n    assertThat(Adapters.formatUtcTime(date(\"1950-01-01T00:00:00.000+0000\").time))\n      .isEqualTo(\"500101000000Z\")\n    assertThat(Adapters.formatUtcTime(date(\"2049-12-31T23:59:59.000+0000\").time))\n      .isEqualTo(\"491231235959Z\")\n  }\n\n  @Test fun `parse generalized time`() {\n    assertThat(Adapters.parseGeneralizedTime(\"18990101000000Z\"))\n      .isEqualTo(date(\"1899-01-01T00:00:00.000+0000\").time)\n    assertThat(Adapters.parseGeneralizedTime(\"19500101000000Z\"))\n      .isEqualTo(date(\"1950-01-01T00:00:00.000+0000\").time)\n    assertThat(Adapters.parseGeneralizedTime(\"20500101000000Z\"))\n      .isEqualTo(date(\"2050-01-01T00:00:00.000+0000\").time)\n    assertThat(Adapters.parseGeneralizedTime(\"20990101000000Z\"))\n      .isEqualTo(date(\"2099-01-01T00:00:00.000+0000\").time)\n    assertThat(Adapters.parseGeneralizedTime(\"19920521000000Z\"))\n      .isEqualTo(date(\"1992-05-21T00:00:00.000+0000\").time)\n    assertThat(Adapters.parseGeneralizedTime(\"19920622123421Z\"))\n      .isEqualTo(date(\"1992-06-22T12:34:21.000+0000\").time)\n  }\n\n  @Disabled(\"fractional seconds are not implemented\")\n  @Test\n  fun `parse generalized time with fractional seconds`() {\n    assertThat(Adapters.parseGeneralizedTime(\"19920722132100.3Z\"))\n      .isEqualTo(date(\"1992-07-22T13:21:00.300+0000\").time)\n  }\n\n  @Test fun `format generalized time`() {\n    assertThat(Adapters.formatGeneralizedTime(date(\"1899-01-01T00:00:00.000+0000\").time))\n      .isEqualTo(\"18990101000000Z\")\n    assertThat(Adapters.formatGeneralizedTime(date(\"1950-01-01T00:00:00.000+0000\").time))\n      .isEqualTo(\"19500101000000Z\")\n    assertThat(Adapters.formatGeneralizedTime(date(\"2050-01-01T00:00:00.000+0000\").time))\n      .isEqualTo(\"20500101000000Z\")\n    assertThat(Adapters.formatGeneralizedTime(date(\"2099-01-01T00:00:00.000+0000\").time))\n      .isEqualTo(\"20990101000000Z\")\n  }\n\n  @Test fun `decode object identifier`() {\n    val bytes = \"06092a864886f70d01010b\".decodeHex()\n    assertThat(Adapters.OBJECT_IDENTIFIER.fromDer(bytes)).isEqualTo(SHA256_WITH_RSA_ENCRYPTION)\n    assertThat(Adapters.OBJECT_IDENTIFIER.toDer(SHA256_WITH_RSA_ENCRYPTION)).isEqualTo(bytes)\n  }\n\n  @Test fun `null value`() {\n    val bytes = \"0500\".decodeHex()\n    assertThat(Adapters.NULL.fromDer(bytes)).isNull()\n    assertThat(Adapters.NULL.toDer(null)).isEqualTo(bytes)\n  }\n\n  @Test fun `sequence algorithm`() {\n    val bytes = \"300d06092a864886f70d01010b0500\".decodeHex()\n    val algorithmIdentifier =\n      AlgorithmIdentifier(\n        algorithm = SHA256_WITH_RSA_ENCRYPTION,\n        parameters = null,\n      )\n    assertThat(CertificateAdapters.algorithmIdentifier.fromDer(bytes))\n      .isEqualTo(algorithmIdentifier)\n    assertThat(CertificateAdapters.algorithmIdentifier.toDer(algorithmIdentifier))\n      .isEqualTo(bytes)\n  }\n\n  @Test fun `bit string`() {\n    val bytes = \"0304066e5dc0\".decodeHex()\n    val bitString = BitString(\"6e5dc0\".decodeHex(), 6)\n\n    assertThat(Adapters.BIT_STRING.fromDer(bytes)).isEqualTo(bitString)\n    assertThat(Adapters.BIT_STRING.toDer(bitString)).isEqualTo(bytes)\n  }\n\n  @Test fun `cannot decode empty bit string`() {\n    val bytes = \"0300\".decodeHex()\n    assertFailsWith<ProtocolException> {\n      Adapters.BIT_STRING.fromDer(bytes)\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"malformed bit string\")\n    }\n  }\n\n  @Test fun `octet string`() {\n    val bytes = \"0404030206A0\".decodeHex()\n    val octetString = \"030206A0\".decodeHex()\n    assertThat(Adapters.OCTET_STRING.fromDer(bytes)).isEqualTo(octetString)\n    assertThat(Adapters.OCTET_STRING.toDer(octetString)).isEqualTo(bytes)\n  }\n\n  @Test fun `cannot decode constructed octet string`() {\n    assertFailsWith<ProtocolException> {\n      Adapters.OCTET_STRING.fromDer(\n        \"2410040668656c6c6f200406776f726c6421\".decodeHex(),\n      )\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"constructed octet strings not supported for DER\")\n    }\n  }\n\n  @Test fun `cannot decode constructed bit string`() {\n    assertFailsWith<ProtocolException> {\n      Adapters.BIT_STRING.fromDer(\n        \"231203070068656c6c6f20030700776f726c6421\".decodeHex(),\n      )\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"constructed bit strings not supported for DER\")\n    }\n  }\n\n  @Test fun `cannot decode constructed string`() {\n    assertFailsWith<ProtocolException> {\n      Adapters.UTF8_STRING.fromDer(\n        \"2c100c0668656c6c6f200c06776f726c6421\".decodeHex(),\n      )\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"constructed strings not supported for DER\")\n    }\n  }\n\n  @Test fun `cannot decode indefinite length bit string`() {\n    assertFailsWith<ProtocolException> {\n      Adapters.BIT_STRING.fromDer(\n        \"23800303000A3B0305045F291CD00000\".decodeHex(),\n      )\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"indefinite length not permitted for DER\")\n    }\n  }\n\n  @Test fun `cannot decode constructed octet string in enclosing sequence`() {\n    val buffer =\n      Buffer()\n        .write(\"3A0904034A6F6E04026573\".decodeHex())\n    val derReader = DerReader(buffer)\n    assertFailsWith<Exception> {\n      derReader.read(\"test\") {\n        derReader.readOctetString()\n      }\n    }.also { expected ->\n      assertThat(expected).hasMessage(\"constructed octet strings not supported for DER\")\n    }\n  }\n\n  @Test fun `choice IP address`() {\n    val bytes = \"8704c0a80201\".decodeHex()\n    val localhost = InetAddress.getByName(\"192.168.2.1\").address.toByteString()\n    assertThat(CertificateAdapters.generalName.fromDer(bytes))\n      .isEqualTo(generalNameIpAddress to localhost)\n    assertThat(CertificateAdapters.generalName.toDer(generalNameIpAddress to localhost))\n      .isEqualTo(bytes)\n  }\n\n  @Test fun `choice dns`() {\n    val bytes = \"820b6578616d706c652e636f6d\".decodeHex()\n    assertThat(CertificateAdapters.generalName.fromDer(bytes))\n      .isEqualTo(generalNameDnsName to \"example.com\")\n    assertThat(CertificateAdapters.generalName.toDer(generalNameDnsName to \"example.com\"))\n      .isEqualTo(bytes)\n  }\n\n  @Test fun `extension with type hint for basic constraints`() {\n    val extension =\n      Extension(\n        BASIC_CONSTRAINTS,\n        false,\n        BasicConstraints(true, 4),\n      )\n    val bytes = \"300f0603551d13040830060101ff020104\".decodeHex()\n\n    assertThat(CertificateAdapters.extension.toDer(extension))\n      .isEqualTo(bytes)\n    assertThat(CertificateAdapters.extension.fromDer(bytes))\n      .isEqualTo(extension)\n  }\n\n  @Test fun `extension with type hint for subject alternative names`() {\n    val extension =\n      Extension(\n        SUBJECT_ALTERNATIVE_NAME,\n        false,\n        listOf(\n          generalNameDnsName to \"cash.app\",\n          generalNameDnsName to \"www.cash.app\",\n        ),\n      )\n    val bytes = \"30210603551d11041a30188208636173682e617070820c7777772e636173682e617070\".decodeHex()\n\n    assertThat(CertificateAdapters.extension.toDer(extension))\n      .isEqualTo(bytes)\n    assertThat(CertificateAdapters.extension.fromDer(bytes))\n      .isEqualTo(extension)\n  }\n\n  @Test fun `extension with unknown type hint`() {\n    val extension =\n      Extension(\n        // common name is not an extension.\n        COMMON_NAME,\n        false,\n        \"3006800109810109\".decodeHex(),\n      )\n    val bytes = \"300f060355040304083006800109810109\".decodeHex()\n\n    assertThat(CertificateAdapters.extension.toDer(extension))\n      .isEqualTo(bytes)\n    assertThat(CertificateAdapters.extension.fromDer(bytes))\n      .isEqualTo(extension)\n  }\n\n  /** Tags larger than 30 are a special case. */\n  @Test fun `large tag`() {\n    val bytes = \"df83fb6800\".decodeHex()\n\n    val adapter = Adapters.NULL.withTag(tagClass = DerHeader.TAG_CLASS_PRIVATE, tag = 65_000L)\n    assertThat(adapter.toDer(null)).isEqualTo(bytes)\n    assertThat(adapter.fromDer(bytes)).isNull()\n  }\n\n  /** Make the claimed length of a nested object larger than the enclosing object. */\n  @Test fun `large object inside small object`() {\n    val bytes = \"301b300d06092a864886f70d010101050003847fffffff000504030201\".decodeHex()\n    assertFailsWith<ProtocolException> {\n      CertificateAdapters.subjectPublicKeyInfo.fromDer(bytes)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"enclosed object too large\")\n    }\n  }\n\n  /** Object identifiers are nominally self-delimiting. Outrun the limit with one. */\n  @Test fun `variable length long outruns limit`() {\n    val bytes = \"060229ffffff7f\".decodeHex()\n    assertFailsWith<ProtocolException> {\n      Adapters.OBJECT_IDENTIFIER.fromDer(bytes)\n    }.also { expected ->\n      assertThat(expected.message).isEqualTo(\"unexpected byte count at OBJECT IDENTIFIER\")\n    }\n  }\n\n  /**\n   * ```\n   * Point ::= SEQUENCE {\n   *   x [0] INTEGER OPTIONAL,\n   *   y [1] INTEGER OPTIONAL\n   * }\n   * ```\n   */\n  data class Point(\n    val x: Long?,\n    val y: Long?,\n  ) {\n    companion object {\n      val ADAPTER =\n        Adapters.sequence(\n          \"Point\",\n          Adapters.INTEGER_AS_LONG.withTag(tag = 0L).optional(),\n          Adapters.INTEGER_AS_LONG.withTag(tag = 1L).optional(),\n          decompose = { listOf(it.x, it.y) },\n          construct = { Point(it[0] as Long?, it[1] as Long?) },\n        )\n    }\n  }\n\n  private fun date(s: String): Date =\n    SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSSZ\").run {\n      timeZone = TimeZone.getTimeZone(\"GMT\")\n      parse(s)\n    }\n}\n"
  },
  {
    "path": "okhttp-urlconnection/README.md",
    "content": "OkHttp URLConnection\n====================\n\nThis module integrates OkHttp with `Authenticator` and `CookieHandler` from `java.net`.\n\nThis module is obsolete; prefer `okhttp-java-net-cookiejar`.\n\n### Download\n\n```kotlin\ntestImplementation(\"com.squareup.okhttp3:okhttp-urlconnection:5.3.0\")\n```\n"
  },
  {
    "path": "okhttp-urlconnection/api/okhttp-urlconnection.api",
    "content": "public final class okhttp3/JavaNetAuthenticator : okhttp3/Authenticator {\n\tpublic fun <init> ()V\n\tpublic fun authenticate (Lokhttp3/Route;Lokhttp3/Response;)Lokhttp3/Request;\n}\n\npublic final class okhttp3/JavaNetCookieJar : okhttp3/CookieJar {\n\tpublic fun <init> (Ljava/net/CookieHandler;)V\n\tpublic fun loadForRequest (Lokhttp3/HttpUrl;)Ljava/util/List;\n\tpublic fun saveFromResponse (Lokhttp3/HttpUrl;Ljava/util/List;)V\n}\n\n"
  },
  {
    "path": "okhttp-urlconnection/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Fragment-Host: com.squareup.okhttp3; bundle-version=\\\"\\${range;[==,+);\\${version_cleanup;${projects.okhttp.version}}}\\\"\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.urlconnection\",\n  \"-removeheaders: Private-Package\",\n)\n\nproject.applyJavaModules(\"okhttp3.urlconnection\")\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n  api(projects.okhttpJavaNetCookiejar)\n  compileOnly(libs.animalsniffer.annotations)\n}\n"
  },
  {
    "path": "okhttp-urlconnection/src/main/java9/module-info.java",
    "content": "@SuppressWarnings(\"module\")\nmodule okhttp3.urlconnection {\n  requires okhttp3;\n}\n"
  },
  {
    "path": "okhttp-urlconnection/src/main/kotlin/okhttp3/JavaNetAuthenticator.kt",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.IOException\nimport okhttp3.Authenticator.Companion.JAVA_NET_AUTHENTICATOR\n\n/**\n * Do not use this.\n *\n * Instead, configure your OkHttpClient.Builder to use `Authenticator.JAVA_NET_AUTHENTICATOR`:\n *\n * ```\n *   val okHttpClient = OkHttpClient.Builder()\n *     .authenticator(okhttp3.Authenticator.Companion.JAVA_NET_AUTHENTICATOR)\n *     .build()\n * ```\n */\n@Deprecated(message = \"Use okhttp3.Authenticator.Companion.JAVA_NET_AUTHENTICATOR instead\")\nclass JavaNetAuthenticator : Authenticator {\n  @Throws(IOException::class)\n  override fun authenticate(\n    route: Route?,\n    response: Response,\n  ): Request? = JAVA_NET_AUTHENTICATOR.authenticate(route, response)\n}\n"
  },
  {
    "path": "okhttp-urlconnection/src/main/kotlin/okhttp3/JavaNetCookieJar.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage okhttp3\n\nimport java.net.CookieHandler\n\n/**\n * A cookie jar that delegates to a [java.net.CookieHandler].\n *\n * This implementation delegates everything to [okhttp3.java.net.cookiejar.JavaNetCookieJar], which\n * conforms to the package-naming limitations of JPMS.\n *\n * Callers should prefer to use [okhttp3.java.net.cookiejar.JavaNetCookieJar] directly.\n */\nclass JavaNetCookieJar private constructor(\n  delegate: okhttp3.java.net.cookiejar.JavaNetCookieJar,\n) : CookieJar by delegate {\n  constructor(cookieHandler: CookieHandler) : this(\n    okhttp3.java.net.cookiejar.JavaNetCookieJar(\n      cookieHandler,\n    ),\n  )\n}\n"
  },
  {
    "path": "okhttp-zstd/README.md",
    "content": "OkHttp Zstandard (zstd) Integration\n===================================\n\nThis module enables [Zstandard (zstd)][1] response compression in addition to Gzip, as long as\nthe `Accept-Encoding` header is not otherwise set. Web servers must be configured to return zstd\nresponses.\n\nNote that zstd is not used for sending requests.\n\n```java\nOkHttpClient client = new OkHttpClient.Builder()\n  .addInterceptor(CompressionInterceptor(Zstd, Gzip))\n  .build();\n```\n\n```kotlin\nimplementation(\"com.squareup.okhttp3:okhttp-zstd:5.3.0\")\n```\n\n [1]: https://github.com/facebook/zstd\n"
  },
  {
    "path": "okhttp-zstd/api/okhttp-zstd.api",
    "content": "public final class okhttp3/zstd/Zstd : okhttp3/CompressionInterceptor$DecompressionAlgorithm {\n\tpublic static final field INSTANCE Lokhttp3/zstd/Zstd;\n\tpublic fun decompress (Lokio/BufferedSource;)Lokio/Source;\n\tpublic fun getEncoding ()Ljava/lang/String;\n}\n\n"
  },
  {
    "path": "okhttp-zstd/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.publish-conventions\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\nproject.applyOsgi(\n  \"Export-Package: okhttp3.zstd\",\n  \"Automatic-Module-Name: okhttp3.zstd\",\n  \"Bundle-SymbolicName: com.squareup.okhttp3.zstd\",\n)\n\ndependencies {\n  \"friendsApi\"(projects.okhttp)\n  implementation(libs.square.zstd.kmp.okio)\n\n  testImplementation(projects.okhttpBrotli)\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(libs.kotlin.test.common)\n  testImplementation(libs.kotlin.test.junit)\n  testImplementation(libs.assertk)\n}\n"
  },
  {
    "path": "okhttp-zstd/src/main/kotlin/okhttp3/zstd/Zstd.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.zstd\n\nimport com.squareup.zstd.okio.zstdDecompress\nimport okhttp3.CompressionInterceptor\nimport okio.BufferedSource\nimport okio.Source\n\n/**\n * Support for Zstandard encoding. Use via the [CompressionInterceptor].\n */\nobject Zstd : CompressionInterceptor.DecompressionAlgorithm {\n  override val encoding: String get() = \"zstd\"\n\n  override fun decompress(compressedSource: BufferedSource): Source = compressedSource.zstdDecompress()\n}\n"
  },
  {
    "path": "okhttp-zstd/src/test/java/okhttp3/zstd/ZstdInterceptorJavaTest.java",
    "content": "/*\n * Copyright (c) 2025 Block, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.zstd;\n\nimport okhttp3.CompressionInterceptor;\nimport okhttp3.Gzip;\nimport okhttp3.brotli.Brotli;\nimport org.junit.jupiter.api.Test;\n\nclass ZstdInterceptorJavaTest {\n  @Test\n  public void testConstructor() {\n    CompressionInterceptor interceptor = new CompressionInterceptor(\n      Zstd.INSTANCE,\n      Gzip.INSTANCE,\n      Brotli.INSTANCE\n    );\n  }\n}\n"
  },
  {
    "path": "okhttp-zstd/src/test/java/okhttp3/zstd/ZstdInterceptorTest.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.zstd\n\nimport assertk.assertThat\nimport assertk.assertions.hasMessage\nimport assertk.assertions.isEmpty\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport com.squareup.zstd.okio.zstdCompress\nimport java.io.IOException\nimport kotlin.test.assertFailsWith\nimport okhttp3.CompressionInterceptor\nimport okhttp3.Gzip\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.Protocol\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.ResponseBody.Companion.toResponseBody\nimport okio.Buffer\nimport okio.ByteString\nimport okio.ByteString.Companion.EMPTY\nimport okio.ByteString.Companion.encodeUtf8\nimport okio.Sink\nimport okio.buffer\nimport okio.gzip\nimport org.junit.jupiter.api.Test\n\nclass ZstdInterceptorTest {\n  val zstdInterceptor = CompressionInterceptor(Zstd, Gzip)\n\n  @Test\n  fun testDecompressZstd() {\n    val s = \"hello zstd world\".encodeUtf8().zstdCompress()\n\n    val response =\n      response(\"https://example.com/\", s) {\n        header(\"Content-Encoding\", \"zstd\")\n      }\n\n    val decompressed = zstdInterceptor.decompress(response)\n    assertThat(decompressed.header(\"Content-Encoding\")).isNull()\n\n    val responseString = decompressed.body.string()\n    assertThat(responseString).isEqualTo(\"hello zstd world\")\n  }\n\n  @Test\n  fun testDecompressGzip() {\n    val s = \"hello gzip world\".encodeUtf8().gzipCompress()\n\n    val response =\n      response(\"https://example.com/\", s) {\n        header(\"Content-Encoding\", \"gzip\")\n      }\n\n    val decompressed = zstdInterceptor.decompress(response)\n    assertThat(decompressed.header(\"Content-Encoding\")).isNull()\n\n    val responseString = decompressed.body.string()\n    assertThat(responseString).isEqualTo(\"hello gzip world\")\n  }\n\n  @Test\n  fun testNoDecompress() {\n    val s = \"hello not compressed world\".encodeUtf8()\n\n    val response = response(\"https://example.com/\", s)\n\n    val decompressed = zstdInterceptor.decompress(response)\n    assertThat(decompressed.header(\"Content-Encoding\")).isNull()\n\n    val responseString = decompressed.body.string()\n    assertThat(responseString).isEqualTo(\"hello not compressed world\")\n  }\n\n  @Test\n  fun testUnknownAlgorithm() {\n    val s = \"hello unknown algorithm world\".encodeUtf8()\n\n    val response =\n      response(\"https://example.com/\", s) {\n        header(\"Content-Encoding\", \"deflate\")\n      }\n\n    val decompressed = zstdInterceptor.decompress(response)\n    assertThat(decompressed.header(\"Content-Encoding\")).isEqualTo(\"deflate\")\n\n    val responseString = decompressed.body.string()\n    assertThat(responseString).isEqualTo(\"hello unknown algorithm world\")\n  }\n\n  @Test\n  fun testFailsDecompress() {\n    val s = \"this is not valid zstd\".encodeUtf8()\n\n    val response =\n      response(\"https://example.com/\", s) {\n        header(\"Content-Encoding\", \"zstd\")\n      }\n\n    val decompressed = zstdInterceptor.decompress(response)\n    assertThat(decompressed.header(\"Content-Encoding\")).isNull()\n\n    assertFailsWith<IOException> {\n      decompressed.body.string()\n    }.also { ioe ->\n      assertThat(ioe).hasMessage(\"zstd decompress failed: Unknown frame descriptor\")\n    }\n  }\n\n  @Test\n  fun testSkipDecompressNoContentResponse() {\n    val response =\n      response(\"https://example.com/\", EMPTY) {\n        header(\"Content-Encoding\", \"zstd\")\n        code(204)\n        message(\"NO CONTENT\")\n      }\n\n    val same = zstdInterceptor.decompress(response)\n\n    val responseString = same.body.string()\n    assertThat(responseString).isEmpty()\n  }\n\n  private fun ByteString.zstdCompress(): ByteString {\n    val result = Buffer()\n    result.zstdCompress().buffer().use {\n      it.write(this@zstdCompress)\n    }\n    return result.readByteString()\n  }\n\n  private fun ByteString.gzipCompress(): ByteString {\n    val result = Buffer()\n    (result as Sink).gzip().buffer().use {\n      it.write(this@gzipCompress)\n    }\n    return result.readByteString()\n  }\n\n  private fun response(\n    url: String,\n    body: ByteString,\n    fn: Response.Builder.() -> Unit = {},\n  ): Response =\n    Response\n      .Builder()\n      .body(body.toResponseBody(\"text/plain\".toMediaType()))\n      .code(200)\n      .message(\"OK\")\n      .request(Request.Builder().url(url).build())\n      .protocol(Protocol.HTTP_2)\n      .apply(fn)\n      .build()\n}\n"
  },
  {
    "path": "okhttp-zstd/src/test/java/okhttp3/zstd/ZstdTestMain.kt",
    "content": "/*\n * Copyright (C) 2025 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.zstd\n\nimport okhttp3.CompressionInterceptor\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nfun main() {\n  val client =\n    OkHttpClient\n      .Builder()\n      .addInterceptor(CompressionInterceptor(Zstd))\n      .build()\n\n  sendRequest(\"https://developers.facebook.com/docs/\", client)\n  sendRequest(\"https://www.facebook.com/robots.txt\", client)\n  sendRequest(\"https://www.instagram.com/robots.txt\", client)\n}\n\nprivate fun sendRequest(\n  url: String,\n  client: OkHttpClient,\n) {\n  val req = Request.Builder().url(url).build()\n\n  client.newCall(req).execute().use {\n    println(url)\n    println(\"\"\"Content-Encoding: ${it.networkResponse?.header(\"Content-Encoding\")}\"\"\")\n    println(\"| ${it.body.string().replace(\"\\n\", \"\\n| \")}\")\n  }\n}\n"
  },
  {
    "path": "regression-test/README.md",
    "content": "Regression Test\n===============\n\nA gradle module for running Regression tests on a device, emulator or JVM.\n\n1. Add an Emulator named `pixel5`, if you don't already have one\n\n```\n$ sdkmanager --install \"system-images;android-29;google_apis;x86\"\n$ echo \"no\" | avdmanager --verbose create avd --force --name \"pixel5\" --device \"pixel\" --package \"system-images;android-29;google_apis;x86\" --tag \"google_apis\" --abi \"x86\"\n```\n\n2. Run an Emulator using Android Studio or from command line.\n\n```\n$ emulator -no-window -no-snapshot-load @pixel5\n```\n\n2. Turn on logs with logcat\n\n```\n$ adb logcat '*:E' OkHttp:D Http2:D TestRunner:D TaskRunner:D OkHttpTest:D GnssHAL_GnssInterface:F DeviceStateChecker:F memtrack:F\n...\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] responseHeadersEnd: Response{protocol=h2, code=200, message=, url=https://1.1.1.1/dns-query?dns=AAABAAABAAAAAAAAA3d3dwhmYWNlYm9vawNjb20AABwAAQ}\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] responseBodyStart\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] responseBodyEnd: byteCount=128\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] connectionReleased\n01-01 12:53:32.811 10999 11089 D OkHttp  : [49 ms] callEnd\n01-01 12:53:32.816 10999 11090 D OkHttp  : [54 ms] responseHeadersStart\n01-01 12:53:32.816 10999 11090 D OkHttp  : [54 ms] responseHeadersEnd: Response{protocol=h2, code=200, message=, url=https://1.1.1.1/dns-query?dns=AAABAAABAAAAAAAAA3d3dwhmYWNlYm9vawNjb20AAAEAAQ}\n01-01 12:53:32.817 10999 11090 D OkHttp  : [55 ms] responseBodyStart\n01-01 12:53:32.818 10999 11090 D OkHttp  : [56 ms] responseBodyEnd: byteCount=128\n01-01 12:53:32.818 10999 11090 D OkHttp  : [56 ms] connectionReleased\n01-01 12:53:32.818 10999 11090 D OkHttp  : [56 ms] callEnd\n```\n\n3. Run tests using gradle\n\n```\n$ ANDROID_SDK_ROOT=/Users/myusername/Library/Android/sdk ./gradlew :regression-test:connectedCheck\n...\n> Task :regression-test:connectedDebugAndroidTest\n...\n11:55:40 V/InstrumentationResultParser: Time: 13.271\n11:55:40 V/InstrumentationResultParser:\n11:55:40 V/InstrumentationResultParser: OK (12 tests)\n...\n11:55:40 I/XmlResultReporter: XML test result file generated at /Users/myusername/workspace/okhttp/regression-test/build/outputs/regression-results/connected/TEST-pixel3a-Q(AVD) - 10-android-test-.xml. Total tests 13, passed 11, assumption_failure 1, ignored 1,\n...\nBUILD SUCCESSFUL in 1m 30s\n63 actionable tasks: 61 executed, 2 up-to-date\n\n```\n\nn.b. use ANDROID_SERIAL=emulator-5554 or similar if you need to select between devices.\n"
  },
  {
    "path": "regression-test/build.gradle.kts",
    "content": "plugins {\n  id(\"okhttp.base-conventions\")\n  id(\"com.android.library\")\n}\n\nandroid {\n  compileSdk = 36\n\n  namespace = \"okhttp.android.regression\"\n\n  defaultConfig {\n    minSdk = 21\n\n    // Make sure to use the AndroidJUnitRunner (or a sub-class) in order to hook in the JUnit 5 Test Builder\n    testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n    testInstrumentationRunnerArguments += mapOf(\n      \"notClass\" to \"org.conscrypt.KitKatPlatformOpenSSLSocketImplAdapter,org.bouncycastle.pqc.crypto.qtesla.QTeslaKeyEncodingTests\"\n    )\n  }\n\n  compileOptions {\n    targetCompatibility(JavaVersion.VERSION_11)\n    sourceCompatibility(JavaVersion.VERSION_11)\n  }\n\n\n  // issue merging due to conflict with httpclient and something else\n  packagingOptions.resources.excludes += setOf(\n    \"META-INF/DEPENDENCIES\"\n  )\n}\n\ndependencies {\n  val okhttpLegacyVersion = \"3.12.12\"\n\n  implementation(libs.kotlin.reflect)\n  implementation(libs.playservices.safetynet)\n  implementation(\"com.squareup.okhttp3:okhttp:${okhttpLegacyVersion}\")\n  implementation(\"com.squareup.okhttp3:okhttp-tls:${okhttpLegacyVersion}\") {\n    exclude(\"org.bouncycastle\")\n  }\n  androidTestImplementation(\"com.squareup.okhttp3:mockwebserver:${okhttpLegacyVersion}\")\n  androidTestImplementation(libs.bouncycastle.bcprov)\n  androidTestImplementation(libs.bouncycastle.bctls)\n  androidTestImplementation(libs.androidx.junit)\n  androidTestImplementation(libs.androidx.espresso.core)\n  androidTestImplementation(libs.http.client5)\n  androidTestImplementation(libs.square.moshi)\n  androidTestImplementation(libs.square.moshi.kotlin)\n}\n"
  },
  {
    "path": "regression-test/src/androidTest/java/okhttp/regression/IssueReproductionTest.java",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.regression;\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Protocol;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport java.io.IOException;\nimport java.security.cert.Certificate;\nimport java.security.cert.X509Certificate;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Simple test adaptable to show a failure in older versions of OkHttp\n * or Android SDKs.\n */\n@RunWith(AndroidJUnit4.class)\npublic class IssueReproductionTest {\n  @Test public void getFailsWithoutAdditionalCert() throws IOException {\n    OkHttpClient client = new OkHttpClient();\n\n    sendRequest(client, \"https://google.com/robots.txt\");\n  }\n\n  private void sendRequest(OkHttpClient client, String url) throws IOException {\n    Request request = new Request.Builder()\n            .url(url)\n            .build();\n    try (Response response = client.newCall(request).execute()) {\n      assertTrue(response.code() == 200 || response.code() == 404);\n      assertEquals(Protocol.HTTP_2, response.protocol());\n\n      for (Certificate c: response.handshake().peerCertificates()) {\n        X509Certificate x = (X509Certificate) c;\n        System.out.println(x.getSubjectDN());\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "regression-test/src/androidTest/java/okhttp/regression/LetsEncryptTest.java",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.regression;\n\nimport android.os.Build;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport okhttp3.OkHttpClient;\nimport okhttp3.Protocol;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.tls.HandshakeCertificates;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport javax.net.ssl.SSLHandshakeException;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\n/**\n * Let's Encrypt expiring root test.\n *\n * Read https://community.letsencrypt.org/t/mobile-client-workarounds-for-isrg-issue/137807\n * for background.\n */\n@RunWith(AndroidJUnit4.class)\npublic class LetsEncryptTest {\n  @Test public void getFailsWithoutAdditionalCert() throws IOException {\n    OkHttpClient client = new OkHttpClient();\n\n    boolean androidMorEarlier = Build.VERSION.SDK_INT <= 23;\n    try {\n      sendRequest(client, \"https://valid-isrgrootx1.letsencrypt.org/robots.txt\");\n      if (androidMorEarlier) {\n        fail();\n      }\n    } catch (SSLHandshakeException sslhe) {\n      assertTrue(androidMorEarlier);\n    }\n  }\n\n  @Test public void getPassesAdditionalCert() throws IOException, CertificateException {\n    boolean androidMorEarlier = Build.VERSION.SDK_INT <= 23;\n\n    OkHttpClient.Builder builder = new OkHttpClient.Builder();\n\n    if (androidMorEarlier) {\n      String isgCert =\n              \"-----BEGIN CERTIFICATE-----\\n\" +\n              \"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\\n\" +\n              \"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\\n\" +\n              \"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\\n\" +\n              \"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\\n\" +\n              \"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\\n\" +\n              \"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\\n\" +\n              \"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\\n\" +\n              \"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\\n\" +\n              \"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\\n\" +\n              \"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\\n\" +\n              \"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\\n\" +\n              \"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\\n\" +\n              \"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\\n\" +\n              \"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\\n\" +\n              \"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\\n\" +\n              \"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\\n\" +\n              \"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\\n\" +\n              \"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\\n\" +\n              \"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\\n\" +\n              \"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\\n\" +\n              \"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\\n\" +\n              \"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\\n\" +\n              \"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\\n\" +\n              \"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\\n\" +\n              \"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\\n\" +\n              \"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\\n\" +\n              \"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\\n\" +\n              \"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\\n\" +\n              \"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\\n\" +\n              \"-----END CERTIFICATE-----\";\n\n      CertificateFactory cf = CertificateFactory.getInstance(\"X.509\");\n      Certificate isgCertificate = cf.generateCertificate(new ByteArrayInputStream(isgCert.getBytes(\"UTF-8\")));\n\n      HandshakeCertificates certificates = new HandshakeCertificates.Builder()\n              .addTrustedCertificate((X509Certificate) isgCertificate)\n              // Uncomment to allow connection to any site generally, but will cause\n              // noticeable memory pressure in Android apps.\n//              .addPlatformTrustedCertificates()\n              .build();\n\n      builder.sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager());\n    }\n\n    OkHttpClient client = builder.build();\n\n    sendRequest(client, \"https://valid-isrgrootx1.letsencrypt.org/robots.txt\");\n\n    try {\n      sendRequest(client, \"https://google.com/robots.txt\");\n      if (androidMorEarlier) {\n        // will pass with default CAs on N or later\n        fail();\n      }\n    } catch (SSLHandshakeException sslhe) {\n      assertTrue(androidMorEarlier);\n    }\n  }\n\n  private void sendRequest(OkHttpClient client, String url) throws IOException {\n    Request request = new Request.Builder()\n            .url(url)\n            .build();\n    try (Response response = client.newCall(request).execute()) {\n      assertTrue(response.code() == 200 || response.code() == 404);\n      assertEquals(Protocol.HTTP_2, response.protocol());\n    }\n  }\n}\n"
  },
  {
    "path": "regression-test/src/androidTest/java/okhttp/regression/compare/AndroidHttpEngineTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.regression.compare\n\nimport android.net.http.ConnectionMigrationOptions\nimport android.net.http.DnsOptions\nimport android.net.http.HttpEngine\nimport android.net.http.HttpException\nimport android.net.http.QuicOptions\nimport android.net.http.UrlRequest\nimport android.net.http.UrlRequest.Callback\nimport android.net.http.UrlRequest.REQUEST_PRIORITY_MEDIUM\nimport android.net.http.UrlResponseInfo\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.filters.SdkSuppress\nimport androidx.test.platform.app.InstrumentationRegistry\nimport java.net.HttpURLConnection\nimport java.net.URL\nimport java.nio.ByteBuffer\nimport java.util.concurrent.CompletableFuture\nimport java.util.concurrent.ExecutionException\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport java.util.concurrent.TimeUnit\nimport kotlin.coroutines.cancellation.CancellationException\nimport okio.Buffer\nimport org.junit.After\nimport org.junit.Assert.assertEquals\nimport org.junit.Assert.assertTrue\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Android HttpEngine.\n */\n@RunWith(AndroidJUnit4::class)\n@SdkSuppress(minSdkVersion = 34)\nclass AndroidHttpEngineTest {\n  val context = InstrumentationRegistry.getInstrumentation().context\n\n  val cacheDir =\n    context.cacheDir.resolve(\"httpEngine\").also {\n      it.mkdirs()\n    }\n  val engine =\n    HttpEngine\n      .Builder(context)\n      .setEnableBrotli(true)\n      .setStoragePath(cacheDir.path)\n      .setEnableHttpCache(HttpEngine.Builder.HTTP_CACHE_DISK, 10_000_000)\n      .setConnectionMigrationOptions(\n        ConnectionMigrationOptions\n          .Builder()\n          .setDefaultNetworkMigration(ConnectionMigrationOptions.MIGRATION_OPTION_ENABLED)\n          .setPathDegradationMigration(ConnectionMigrationOptions.MIGRATION_OPTION_ENABLED)\n          .setAllowNonDefaultNetworkUsage(ConnectionMigrationOptions.MIGRATION_OPTION_ENABLED)\n          .build(),\n      ).setDnsOptions(\n        DnsOptions\n          .Builder()\n          .setUseHttpStackDnsResolver(DnsOptions.DNS_OPTION_ENABLED)\n          .setStaleDns(DnsOptions.DNS_OPTION_ENABLED)\n          .setPersistHostCache(DnsOptions.DNS_OPTION_ENABLED)\n          .build(),\n      ).setQuicOptions(\n        QuicOptions\n          .Builder()\n          .addAllowedQuicHost(\"google.com\")\n          .addAllowedQuicHost(\"www.google.com\")\n          .build(),\n      ).addQuicHint(\"google.com\", 443, 443)\n      .addQuicHint(\"www.google.com\", 443, 443)\n      .build()\n\n  @After\n  fun tearDown() {\n    engine.shutdown()\n    cacheDir.deleteRecursively()\n  }\n\n  @Test\n  fun get() {\n    val executor = Executors.newCachedThreadPool()\n\n    val completableFuture = execute(engine, executor, \"https://google.com/robots.txt\")\n\n    try {\n      val response = completableFuture.get(10, TimeUnit.SECONDS)\n\n      assertEquals(200, response.code)\n      assertEquals(\"h3\", response.negotiatedProtocol)\n      assertTrue(response.content.contains(\"Disallow\"))\n    } catch (ee: ExecutionException) {\n      throw ee.cause?.cause ?: ee.cause!!\n    }\n  }\n\n  data class Response(\n    val code: Int,\n    val negotiatedProtocol: String,\n    val content: String,\n  )\n\n  private fun execute(\n    engine: HttpEngine,\n    executor: ExecutorService,\n    url: String,\n  ): CompletableFuture<Response> {\n    val completableFuture = CompletableFuture<Response>()\n    val buffer = Buffer()\n\n    val req =\n      engine\n        .newUrlRequestBuilder(\n          url,\n          executor,\n          object : Callback {\n            override fun onRedirectReceived(\n              request: UrlRequest,\n              info: UrlResponseInfo,\n              newLocationUrl: String,\n            ) {\n              println(\"request \" + info.httpStatusCode + \" \" + newLocationUrl)\n              request.followRedirect()\n            }\n\n            override fun onResponseStarted(\n              request: UrlRequest,\n              info: UrlResponseInfo,\n            ) {\n              println(\"onResponseStarted ${info.headers.asMap} ${info.negotiatedProtocol}\")\n              request.read(ByteBuffer.allocateDirect(4096 * 8))\n            }\n\n            override fun onReadCompleted(\n              request: UrlRequest,\n              info: UrlResponseInfo,\n              byteBuffer: ByteBuffer,\n            ) {\n              println(\"onReadCompleted ${info.headers.asMap}\")\n              byteBuffer.flip()\n              buffer.write(byteBuffer)\n              byteBuffer.clear()\n              request.read(byteBuffer)\n            }\n\n            override fun onSucceeded(\n              request: UrlRequest,\n              info: UrlResponseInfo,\n            ) {\n              println(\"onSucceeded ${info.headers.asMap}\")\n              completableFuture.complete(Response(info.httpStatusCode, info.negotiatedProtocol, buffer.readUtf8()))\n            }\n\n            override fun onFailed(\n              request: UrlRequest,\n              info: UrlResponseInfo?,\n              error: HttpException,\n            ) {\n              println(\"onSucceeded ${info?.headers?.asMap}\")\n              completableFuture.completeExceptionally(error)\n            }\n\n            override fun onCanceled(\n              request: UrlRequest,\n              info: UrlResponseInfo?,\n            ) {\n              completableFuture.completeExceptionally(CancellationException())\n            }\n          },\n        ).setPriority(REQUEST_PRIORITY_MEDIUM)\n        .setDirectExecutorAllowed(true)\n        .setTrafficStatsTag(101)\n        .build()\n\n    req.start()\n    return completableFuture\n  }\n\n  @Test\n  fun urlConnection() {\n    val conn = engine.openConnection(URL(\"https://google.com/robots.txt\")) as HttpURLConnection\n\n    val text =\n      conn.inputStream.use {\n        it.bufferedReader().readText()\n      }\n\n    assertEquals(200, conn.responseCode)\n\n    assertTrue(text.contains(\"Disallow\"))\n  }\n}\n"
  },
  {
    "path": "regression-test/src/androidTest/java/okhttp/regression/compare/ApacheHttpClientHttp2Test.kt",
    "content": "/*\n * ====================================================================\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n * ====================================================================\n *\n * This software consists of voluntary contributions made by many\n * individuals on behalf of the Apache Software Foundation.  For more\n * information on the Apache Software Foundation, please see\n * <http://www.apache.org/>.\n *\n */\npackage okhttp.regression.compare\n\nimport org.apache.hc.client5.http.async.methods.SimpleHttpRequests\nimport org.apache.hc.client5.http.async.methods.SimpleHttpResponse\nimport org.apache.hc.client5.http.impl.async.HttpAsyncClients\nimport org.apache.hc.core5.concurrent.FutureCallback\nimport org.apache.hc.core5.http.ProtocolVersion\nimport org.junit.Assert\nimport org.junit.Ignore\nimport org.junit.Test\n\n/**\n * Simplified from\n * https://hc.apache.org/httpcomponents-client-5.0.x/httpclient5/examples/AsyncClientTlsAlpn.java\n *\n * Mainly intended to verify behaviour of popular clients across Android versions, similar\n * to observing Firefox or Chrome browser behaviour.\n */\n@Ignore(\"Failing with Netty errors\")\nclass ApacheHttpClientHttp2Test {\n  @Test\n  fun testHttp2() {\n    val client = HttpAsyncClients.createHttp2Default()\n\n    client.use { client ->\n      client.start()\n      val request = SimpleHttpRequests.get(\"https://google.com/robots.txt\")\n      val response = client.execute(request, LoggingCallback).get()\n\n      println(\"Protocol ${response.version}\")\n      println(\"Response ${response.code}\")\n      println(\"${response.body.bodyText.substring(0, 20)}...\")\n\n      Assert.assertEquals(ProtocolVersion(\"HTTP\", 2, 0), response.version)\n    }\n  }\n}\n\nobject LoggingCallback : FutureCallback<SimpleHttpResponse> {\n  override fun completed(response: SimpleHttpResponse) {\n  }\n\n  override fun failed(ex: Exception) {\n    println(\"Failed: $ex\")\n  }\n\n  override fun cancelled() {\n    println(\"Cancelled\")\n  }\n}\n"
  },
  {
    "path": "regression-test/src/androidTest/java/okhttp/regression/compare/ApacheHttpClientTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.regression.compare\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.apache.hc.client5.http.classic.methods.HttpGet\nimport org.apache.hc.client5.http.impl.classic.HttpClients\nimport org.apache.hc.core5.http.HttpVersion\nimport org.junit.After\nimport org.junit.Assert.assertEquals\nimport org.junit.Ignore\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Apache HttpClient 5.x.\n *\n * https://hc.apache.org/httpcomponents-client-5.0.x/index.html\n */\n@Ignore(\"Failing with Netty errors\")\n@RunWith(AndroidJUnit4::class)\nclass ApacheHttpClientTest {\n  private var httpClient = HttpClients.createDefault()\n\n  @After fun tearDown() {\n    httpClient.close()\n  }\n\n  @Test fun get() {\n    val request = HttpGet(\"https://google.com/robots.txt\")\n\n    httpClient.execute(request).use { response ->\n      assertEquals(200, response.code)\n      // TODO enable ALPN later\n      assertEquals(HttpVersion.HTTP_1_1, response.version)\n    }\n  }\n}\n"
  },
  {
    "path": "regression-test/src/androidTest/java/okhttp/regression/compare/OkHttpClientTest.java",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp.regression.compare;\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport java.io.IOException;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Protocol;\nimport okhttp3.Request;\nimport static org.junit.Assert.assertEquals;\n\nimport okhttp3.Response;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/**\n * OkHttp.\n *\n * https://square.github.io/okhttp/\n */\n@RunWith(AndroidJUnit4.class)\npublic class OkHttpClientTest {\n  @Test public void get() throws IOException {\n    OkHttpClient client = new OkHttpClient();\n    Request request = new Request.Builder()\n        .url(\"https://google.com/robots.txt\")\n        .build();\n    try (Response response = client.newCall(request).execute()) {\n      assertEquals(200, response.code());\n      assertEquals(Protocol.HTTP_2, response.protocol());\n    }\n  }\n}\n"
  },
  {
    "path": "regression-test/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"okhttp.android.regression\">\n  <uses-permission android:name=\"android.permission.INTERNET\" />\n\n  <!-- For HttpEngine -->\n  <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n</manifest>\n"
  },
  {
    "path": "regression-test/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"app_name\">regression-test</string>\n</resources>\n"
  },
  {
    "path": "regression-test/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n  <base-config cleartextTrafficPermitted=\"false\">\n  </base-config>\n</network-security-config>"
  },
  {
    "path": "samples/compare/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\ndependencies {\n  testImplementation(projects.okhttp)\n  testImplementation(projects.mockwebserver3)\n  testImplementation(projects.mockwebserver3Junit5)\n  testImplementation(projects.okhttpTls)\n  testImplementation(projects.okhttpTestingSupport)\n  testImplementation(libs.http.client5)\n  testImplementation(libs.jetty.client)\n  testImplementation(libs.junit)\n  testImplementation(libs.assertk)\n}\n\ntasks.compileJava {\n  options.isWarnings = false\n}\n"
  },
  {
    "path": "samples/compare/src/test/kotlin/okhttp3/compare/ApacheHttpClientTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.compare\n\nimport assertk.assertThat\nimport assertk.assertions.containsExactlyInAnyOrder\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.startsWith\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport org.apache.hc.client5.http.classic.methods.HttpGet\nimport org.apache.hc.client5.http.impl.classic.HttpClients\nimport org.apache.hc.core5.http.io.entity.EntityUtils\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\n\n/**\n * Apache HttpClient 5.x.\n *\n * https://hc.apache.org/httpcomponents-client-5.0.x/index.html\n *\n * Baseline test if we ned to validate OkHttp behaviour against other popular clients.\n */\nclass ApacheHttpClientTest {\n  private val httpClient = HttpClients.createDefault()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @AfterEach\n  fun tearDown() {\n    httpClient.close()\n  }\n\n  @Test\n  fun get() {\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"hello, Apache HttpClient 5.x\")\n        .build(),\n    )\n\n    val request = HttpGet(server.url(\"/\").toUri())\n    request.addHeader(\"Accept\", \"text/plain\")\n\n    @Suppress(\"DEPRECATION\")\n    httpClient.execute(request).use { response ->\n      assertThat(response.code).isEqualTo(200)\n      assertThat(EntityUtils.toString(response.entity)).isEqualTo(\"hello, Apache HttpClient 5.x\")\n    }\n\n    val recorded = server.takeRequest()\n    assertThat(recorded.headers[\"Accept\"]).isEqualTo(\"text/plain\")\n    assertThat(\n      recorded.headers[\"Accept-Encoding\"]?.split(\", \").orEmpty(),\n    ).containsExactlyInAnyOrder(\n      \"gzip\",\n      \"x-gzip\",\n      \"deflate\",\n    )\n    assertThat(recorded.headers[\"User-Agent\"]!!).startsWith(\"Apache-HttpClient/\")\n  }\n}\n"
  },
  {
    "path": "samples/compare/src/test/kotlin/okhttp3/compare/JavaHttpClientTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.compare\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNotNull\nimport assertk.assertions.isNull\nimport assertk.assertions.matches\nimport java.net.http.HttpClient\nimport java.net.http.HttpClient.Redirect.NORMAL\nimport java.net.http.HttpRequest\nimport java.net.http.HttpResponse.BodyHandlers\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n/**\n * Java HTTP Client.\n *\n * https://openjdk.java.net/groups/net/httpclient/intro.html\n *\n * Baseline test if we ned to validate OkHttp behaviour against other popular clients.\n */\nclass JavaHttpClientTest {\n  @JvmField @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @Test fun get() {\n    // Not available\n    platform.expectFailureOnJdkVersion(8)\n\n    val httpClient =\n      HttpClient\n        .newBuilder()\n        .followRedirects(NORMAL)\n        .build()\n\n    server.enqueue(\n      MockResponse\n        .Builder()\n        .body(\"hello, Java HTTP Client\")\n        .build(),\n    )\n\n    val request =\n      HttpRequest\n        .newBuilder(server.url(\"/\").toUri())\n        .header(\"Accept\", \"text/plain\")\n        .build()\n\n    val response = httpClient.send(request, BodyHandlers.ofString())\n    assertThat(response.statusCode()).isEqualTo(200)\n    assertThat(response.body()).isEqualTo(\"hello, Java HTTP Client\")\n\n    val recorded = server.takeRequest()\n    assertThat(recorded.headers[\"Accept\"]).isEqualTo(\"text/plain\")\n    assertThat(recorded.headers[\"Accept-Encoding\"]).isNull() // No built-in gzip.\n    assertThat(recorded.headers[\"Connection\"]).isEqualTo(\"Upgrade, HTTP2-Settings\")\n    assertThat(recorded.headers[\"HTTP2-Settings\"]).isNotNull()\n    assertThat(recorded.headers[\"Upgrade\"]).isEqualTo(\"h2c\") // HTTP/2 over plaintext!\n    assertThat(recorded.headers[\"User-Agent\"]!!).matches(Regex(\"Java-http-client/.*\"))\n  }\n}\n"
  },
  {
    "path": "samples/compare/src/test/kotlin/okhttp3/compare/JettyHttpClientTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.compare\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.isNull\nimport assertk.assertions.matches\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport org.eclipse.jetty.client.HttpClient\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\n/**\n * Jetty HTTP client.\n *\n * https://www.eclipse.org/jetty/documentation/current/http-client.html\n *\n * Baseline test if we ned to validate OkHttp behaviour against other popular clients.\n */\nclass JettyHttpClientTest {\n  private val client = HttpClient()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @BeforeEach fun setUp() {\n    client.start()\n  }\n\n  @AfterEach fun tearDown() {\n    client.stop()\n  }\n\n  @Test fun get() {\n    server.enqueue(MockResponse(body = \"hello, Jetty HTTP Client\"))\n\n    val request =\n      client\n        .newRequest(server.url(\"/\").toUri())\n        .header(\"Accept\", \"text/plain\")\n    val response = request.send()\n    assertThat(response.status).isEqualTo(200)\n    assertThat(response.contentAsString).isEqualTo(\"hello, Jetty HTTP Client\")\n\n    val recorded = server.takeRequest()\n    assertThat(recorded.headers[\"Accept\"]).isEqualTo(\"text/plain\")\n    assertThat(recorded.headers[\"Accept-Encoding\"]).isEqualTo(\"gzip\")\n    assertThat(recorded.headers[\"Connection\"]).isNull()\n    assertThat(recorded.headers[\"User-Agent\"]!!).matches(Regex(\"Jetty/.*\"))\n  }\n}\n"
  },
  {
    "path": "samples/compare/src/test/kotlin/okhttp3/compare/OkHttpClientTest.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.compare\n\nimport assertk.assertThat\nimport assertk.assertions.isEqualTo\nimport assertk.assertions.matches\nimport mockwebserver3.MockResponse\nimport mockwebserver3.MockWebServer\nimport mockwebserver3.junit5.StartStop\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.testing.PlatformRule\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n/**\n * OkHttp.\n *\n * https://square.github.io/okhttp/\n */\nclass OkHttpClientTest {\n  @JvmField @RegisterExtension\n  val platform = PlatformRule()\n\n  @StartStop\n  private val server = MockWebServer()\n\n  @Test fun get() {\n    server.enqueue(MockResponse(body = \"hello, OkHttp\"))\n\n    val client = OkHttpClient()\n\n    val request =\n      Request\n        .Builder()\n        .url(server.url(\"/\"))\n        .header(\"Accept\", \"text/plain\")\n        .build()\n    val response = client.newCall(request).execute()\n    assertThat(response.code).isEqualTo(200)\n    assertThat(response.body.string()).isEqualTo(\"hello, OkHttp\")\n\n    val recorded = server.takeRequest()\n    assertThat(recorded.headers[\"Accept\"]).isEqualTo(\"text/plain\")\n    assertThat(recorded.headers[\"Accept-Encoding\"]).isEqualTo(\"gzip\")\n    assertThat(recorded.headers[\"Connection\"]).isEqualTo(\"Keep-Alive\")\n    assertThat(recorded.headers[\"User-Agent\"]!!).matches(Regex(\"okhttp/.*\"))\n  }\n}\n"
  },
  {
    "path": "samples/crawler/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n  application\n}\n\napplication {\n  mainClass.set(\"okhttp3.sample.Crawler\")\n}\n\ndependencies {\n  implementation(projects.okhttp)\n  implementation(libs.jsoup)\n}\n\ntasks.compileJava {\n  options.isWarnings = false\n}\n"
  },
  {
    "path": "samples/crawler/src/main/java/okhttp3/sample/Crawler.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sample;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport okhttp3.Cache;\nimport okhttp3.HttpUrl;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Document;\nimport org.jsoup.nodes.Element;\n\n/**\n * Fetches HTML from a requested URL, follows the links, and repeats.\n */\npublic final class Crawler {\n  private final OkHttpClient client;\n  private final Set<HttpUrl> fetchedUrls = Collections.synchronizedSet(new LinkedHashSet<>());\n  private final BlockingQueue<HttpUrl> queue;\n  private final ConcurrentHashMap<String, AtomicInteger> hostnames = new ConcurrentHashMap<>();\n  private final int hostLimit;\n\n  public Crawler(OkHttpClient client, int queueLimit, int hostLimit) {\n    this.client = client;\n    this.queue = new LinkedBlockingQueue<>(queueLimit);\n    this.hostLimit = hostLimit;\n  }\n\n  private void parallelDrainQueue(int threadCount) {\n    ExecutorService executor = Executors.newFixedThreadPool(threadCount);\n    for (int i = 0; i < threadCount; i++) {\n      executor.execute(() -> {\n        try {\n          drainQueue();\n        } catch (Throwable e) {\n          e.printStackTrace();\n        }\n      });\n    }\n    executor.shutdown();\n  }\n\n  private void drainQueue() throws Exception {\n    for (HttpUrl url; (url = queue.take()) != null; ) {\n      if (!fetchedUrls.add(url)) {\n        continue;\n      }\n\n      Thread currentThread = Thread.currentThread();\n      String originalName = currentThread.getName();\n      currentThread.setName(\"Crawler \" + url);\n      try {\n        fetch(url);\n      } catch (IOException e) {\n        System.out.printf(\"XXX: %s %s%n\", url, e);\n      } finally {\n        currentThread.setName(originalName);\n      }\n    }\n  }\n\n  public void fetch(HttpUrl url) throws IOException {\n    // Skip hosts that we've visited many times.\n    AtomicInteger hostnameCount = new AtomicInteger();\n    AtomicInteger previous = hostnames.putIfAbsent(url.host(), hostnameCount);\n    if (previous != null) hostnameCount = previous;\n    if (hostnameCount.getAndIncrement() >= hostLimit) return;\n\n    Request request = new Request.Builder()\n        .url(url)\n        .build();\n    try (Response response = client.newCall(request).execute()) {\n      String responseSource = response.networkResponse() != null ? (\"(network: \"\n          + response.networkResponse().code()\n          + \" over \"\n          + response.protocol()\n          + \")\") : \"(cache)\";\n      int responseCode = response.code();\n\n      System.out.printf(\"%03d: %s %s%n\", responseCode, url, responseSource);\n\n      String contentType = response.header(\"Content-Type\");\n      if (responseCode != 200 || contentType == null) {\n        return;\n      }\n\n      MediaType mediaType = MediaType.parse(contentType);\n      if (mediaType == null || !mediaType.subtype().equalsIgnoreCase(\"html\")) {\n        return;\n      }\n\n      Document document = Jsoup.parse(response.body().string(), url.toString());\n      for (Element element : document.select(\"a[href]\")) {\n        String href = element.attr(\"href\");\n        HttpUrl link = response.request().url().resolve(href);\n        if (link == null) continue; // URL is either invalid or its scheme isn't http/https.\n        HttpUrl linkWithoutFragment = link.newBuilder().fragment(null).build();\n        if (!queue.offer(linkWithoutFragment)) break; // Queue is full.\n      }\n    }\n  }\n\n  public static void main(String[] args) throws IOException {\n    if (args.length != 2) {\n      System.out.println(\"Usage: Crawler <cache dir> <root>\");\n      return;\n    }\n\n    int threadCount = 20;\n    int queueLimit = 1000;\n    int hostLimit = 25;\n    long cacheByteCount = 1024L * 1024L * 100L;\n\n    Cache cache = new Cache(new File(args[0]), cacheByteCount);\n    OkHttpClient client = new OkHttpClient.Builder()\n        .cache(cache)\n        .callTimeout(5, TimeUnit.SECONDS)\n        .build();\n\n    Crawler crawler = new Crawler(client, queueLimit, hostLimit);\n    crawler.queue.add(HttpUrl.get(args[1]));\n    crawler.parallelDrainQueue(threadCount);\n  }\n}\n"
  },
  {
    "path": "samples/guide/README.md",
    "content": "Samples\n=======\n"
  },
  {
    "path": "samples/guide/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n  id(\"com.google.devtools.ksp\")\n}\n\ndependencies {\n  \"friendsImplementation\"(projects.okhttp)\n  implementation(projects.mockwebserver)\n  implementation(projects.okhttpTestingSupport)\n  implementation(projects.okhttpTls)\n  implementation(libs.animalsniffer.annotations)\n  implementation(libs.square.moshi)\n  implementation(libs.square.okio.fakefilesystem)\n  ksp(libs.square.moshi.compiler)\n}\n\ntasks.compileJava {\n  options.isWarnings = false\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/guide/GetExample.java",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.guide;\n\nimport java.io.IOException;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic class GetExample {\n  final OkHttpClient client = new OkHttpClient();\n\n  String run(String url) throws IOException {\n    Request request = new Request.Builder()\n        .url(url)\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      return response.body().string();\n    }\n  }\n\n  public static void main(String[] args) throws IOException {\n    GetExample example = new GetExample();\n    String response = example.run(\"https://raw.github.com/square/okhttp/master/README.md\");\n    System.out.println(response);\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/guide/PostExample.java",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.guide;\n\nimport java.io.IOException;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic class PostExample {\n  public static final MediaType JSON = MediaType.get(\"application/json; charset=utf-8\");\n\n  final OkHttpClient client = new OkHttpClient();\n\n  String post(String url, String json) throws IOException {\n    RequestBody body = RequestBody.create(json, JSON);\n    Request request = new Request.Builder()\n        .url(url)\n        .post(body)\n        .build();\n    try (Response response = client.newCall(request).execute()) {\n      return response.body().string();\n    }\n  }\n\n  String bowlingJson(String player1, String player2) {\n    return \"{'winCondition':'HIGH_SCORE',\"\n        + \"'name':'Bowling',\"\n        + \"'round':4,\"\n        + \"'lastSaved':1367702411696,\"\n        + \"'dateStarted':1367702378785,\"\n        + \"'players':[\"\n        + \"{'name':'\" + player1 + \"','history':[10,8,6,7,8],'color':-13388315,'total':39},\"\n        + \"{'name':'\" + player2 + \"','history':[6,10,5,10,10],'color':-48060,'total':41}\"\n        + \"]}\";\n  }\n\n  public static void main(String[] args) throws IOException {\n    PostExample example = new PostExample();\n    String json = example.bowlingJson(\"Jesse\", \"Jake\");\n    String response = example.post(\"http://www.roundsapp.com/post\", json);\n    System.out.println(response);\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/AccessHeaders.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class AccessHeaders {\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://api.github.com/repos/square/okhttp/issues\")\n        .header(\"User-Agent\", \"OkHttp Headers.java\")\n        .addHeader(\"Accept\", \"application/json; q=0.5\")\n        .addHeader(\"Accept\", \"application/vnd.github.v3+json\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(\"Server: \" + response.header(\"Server\"));\n      System.out.println(\"Date: \" + response.header(\"Date\"));\n      System.out.println(\"Vary: \" + response.headers(\"Vary\"));\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new AccessHeaders().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/AsynchronousGet.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.Call;\nimport okhttp3.Callback;\nimport okhttp3.Headers;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\n\npublic final class AsynchronousGet {\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"http://publicobject.com/helloworld.txt\")\n        .build();\n\n    client.newCall(request).enqueue(new Callback() {\n      @Override public void onFailure(Call call, IOException e) {\n        e.printStackTrace();\n      }\n\n      @Override public void onResponse(Call call, Response response) throws IOException {\n        try (ResponseBody responseBody = response.body()) {\n          if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n          Headers responseHeaders = response.headers();\n          for (int i = 0, size = responseHeaders.size(); i < size; i++) {\n            System.out.println(responseHeaders.name(i) + \": \" + responseHeaders.value(i));\n          }\n\n          System.out.println(responseBody.string());\n        }\n      }\n    });\n  }\n\n  public static void main(String... args) throws Exception {\n    new AsynchronousGet().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/Authenticate.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.Credentials;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class Authenticate {\n  private final OkHttpClient client;\n\n  public Authenticate() {\n    client = new OkHttpClient.Builder()\n        .authenticator((route, response) -> {\n          if (response.request().header(\"Authorization\") != null) {\n            return null; // Give up, we've already attempted to authenticate.\n          }\n\n          System.out.println(\"Authenticating for response: \" + response);\n          System.out.println(\"Challenges: \" + response.challenges());\n          String credential = Credentials.basic(\"jesse\", \"password1\");\n          return response.request().newBuilder()\n              .header(\"Authorization\", credential)\n              .build();\n        })\n        .build();\n  }\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"http://publicobject.com/secrets/hellosecret.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new Authenticate().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/CacheResponse.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.File;\nimport java.io.IOException;\nimport okhttp3.Cache;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class CacheResponse {\n  private final OkHttpClient client;\n\n  public CacheResponse(File cacheDirectory) throws Exception {\n    int cacheSize = 10 * 1024 * 1024; // 10 MiB\n    Cache cache = new Cache(cacheDirectory, cacheSize);\n\n    client = new OkHttpClient.Builder()\n        .cache(cache)\n        .build();\n  }\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"http://publicobject.com/helloworld.txt\")\n        .build();\n\n    String response1Body;\n    try (Response response1 = client.newCall(request).execute()) {\n      if (!response1.isSuccessful()) throw new IOException(\"Unexpected code \" + response1);\n\n      response1Body = response1.body().string();\n      System.out.println(\"Response 1 response:          \" + response1);\n      System.out.println(\"Response 1 cache response:    \" + response1.cacheResponse());\n      System.out.println(\"Response 1 network response:  \" + response1.networkResponse());\n    }\n\n    String response2Body;\n    try (Response response2 = client.newCall(request).execute()) {\n      if (!response2.isSuccessful()) throw new IOException(\"Unexpected code \" + response2);\n\n      response2Body = response2.body().string();\n      System.out.println(\"Response 2 response:          \" + response2);\n      System.out.println(\"Response 2 cache response:    \" + response2.cacheResponse());\n      System.out.println(\"Response 2 network response:  \" + response2.networkResponse());\n    }\n\n    System.out.println(\"Response 2 equals Response 1? \" + response1Body.equals(response2Body));\n  }\n\n  public static void main(String... args) throws Exception {\n    new CacheResponse(new File(\"CacheResponse.tmp\")).run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/CancelCall.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport okhttp3.Call;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic class CancelCall {\n  private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"http://httpbin.org/delay/2\") // This URL is served with a 2 second delay.\n        .build();\n\n    final long startNanos = System.nanoTime();\n    final Call call = client.newCall(request);\n\n    // Schedule a job to cancel the call in 1 second.\n    executor.schedule(() -> {\n      System.out.printf(\"%.2f Canceling call.%n\", (System.nanoTime() - startNanos) / 1e9f);\n      call.cancel();\n      System.out.printf(\"%.2f Canceled call.%n\", (System.nanoTime() - startNanos) / 1e9f);\n    }, 1, TimeUnit.SECONDS);\n\n    System.out.printf(\"%.2f Executing call.%n\", (System.nanoTime() - startNanos) / 1e9f);\n    try (Response response = call.execute()) {\n      System.out.printf(\"%.2f Call was expected to fail, but completed: %s%n\",\n          (System.nanoTime() - startNanos) / 1e9f, response);\n    } catch (IOException e) {\n      System.out.printf(\"%.2f Call failed as expected: %s%n\",\n          (System.nanoTime() - startNanos) / 1e9f, e);\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CancelCall().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/CertificatePinning.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.security.cert.Certificate;\nimport okhttp3.CertificatePinner;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class CertificatePinning {\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .certificatePinner(\n          new CertificatePinner.Builder()\n              .add(\"publicobject.com\", \"sha256/Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=\")\n              .build())\n      .build();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/robots.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      for (Certificate certificate : response.handshake().peerCertificates()) {\n        System.out.println(CertificatePinner.pin(certificate));\n      }\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CertificatePinning().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/CheckHandshake.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.security.cert.Certificate;\nimport java.util.Collections;\nimport java.util.Set;\nimport okhttp3.CertificatePinner;\nimport okhttp3.Interceptor;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class CheckHandshake {\n  /** Rejects otherwise-trusted certificates. */\n  private static final Interceptor CHECK_HANDSHAKE_INTERCEPTOR = new Interceptor() {\n    final Set<String> denylist = Collections.singleton(\n        \"sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=\");\n\n    @Override public Response intercept(Chain chain) throws IOException {\n      for (Certificate certificate : chain.connection().handshake().peerCertificates()) {\n        String pin = CertificatePinner.pin(certificate);\n        if (denylist.contains(pin)) {\n          throw new IOException(\"Denylisted peer certificate: \" + pin);\n        }\n      }\n      return chain.proceed(chain.request());\n    }\n  };\n\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .addNetworkInterceptor(CHECK_HANDSHAKE_INTERCEPTOR)\n      .build();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CheckHandshake().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/ConfigureTimeouts.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.util.concurrent.TimeUnit;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class ConfigureTimeouts {\n  private final OkHttpClient client;\n\n  public ConfigureTimeouts() throws Exception {\n    client = new OkHttpClient.Builder()\n        .connectTimeout(5, TimeUnit.SECONDS)\n        .writeTimeout(5, TimeUnit.SECONDS)\n        .readTimeout(5, TimeUnit.SECONDS)\n        .callTimeout(10, TimeUnit.SECONDS)\n        .build();\n  }\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"http://httpbin.org/delay/2\") // This URL is served with a 2 second delay.\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      System.out.println(\"Response completed: \" + response);\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new ConfigureTimeouts().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/CurrentDateHeader.java",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.util.Date;\nimport okhttp3.Headers;\nimport okhttp3.Interceptor;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class CurrentDateHeader {\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .addInterceptor(new CurrentDateInterceptor())\n      .build();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      System.out.println(response.request().header(\"Date\"));\n    }\n  }\n\n  static class CurrentDateInterceptor implements Interceptor {\n    @Override public Response intercept(Chain chain) throws IOException {\n      Request request = chain.request();\n      Headers newHeaders = request.headers()\n          .newBuilder()\n          .add(\"Date\", new Date())\n          .build();\n      Request newRequest = request.newBuilder()\n          .headers(newHeaders)\n          .build();\n      return chain.proceed(newRequest);\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CurrentDateHeader().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/CustomCipherSuites.java",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.Socket;\nimport java.security.GeneralSecurityException;\nimport java.security.KeyManagementException;\nimport java.security.KeyStore;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLSocket;\nimport javax.net.ssl.SSLSocketFactory;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.TrustManagerFactory;\nimport javax.net.ssl.X509TrustManager;\nimport okhttp3.CipherSuite;\nimport okhttp3.ConnectionSpec;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\nimport static java.util.Arrays.asList;\n\npublic final class CustomCipherSuites {\n  private final OkHttpClient client;\n\n  public CustomCipherSuites() throws GeneralSecurityException {\n    // Configure cipher suites to demonstrate how to customize which cipher suites will be used for\n    // an OkHttp request. In order to be selected a cipher suite must be included in both OkHttp's\n    // connection spec and in the SSLSocket's enabled cipher suites array. Most applications should\n    // not customize the cipher suites list.\n    List<CipherSuite> customCipherSuites = asList(\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\n        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\n        CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384);\n    final ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)\n        .cipherSuites(customCipherSuites.toArray(new CipherSuite[0]))\n        .build();\n\n    X509TrustManager trustManager = defaultTrustManager();\n    SSLSocketFactory sslSocketFactory = defaultSslSocketFactory(trustManager);\n    SSLSocketFactory customSslSocketFactory = new DelegatingSSLSocketFactory(sslSocketFactory) {\n      @Override protected SSLSocket configureSocket(SSLSocket socket) throws IOException {\n        socket.setEnabledCipherSuites(javaNames(spec.cipherSuites()));\n        return socket;\n      }\n    };\n\n    client = new OkHttpClient.Builder()\n        .connectionSpecs(Collections.singletonList(spec))\n        .sslSocketFactory(customSslSocketFactory, trustManager)\n        .build();\n  }\n\n  /**\n   * Returns the VM's default SSL socket factory, using {@code trustManager} for trusted root\n   * certificates.\n   */\n  private SSLSocketFactory defaultSslSocketFactory(X509TrustManager trustManager)\n      throws NoSuchAlgorithmException, KeyManagementException {\n    SSLContext sslContext = SSLContext.getInstance(\"TLS\");\n    sslContext.init(null, new TrustManager[] { trustManager }, null);\n\n    return sslContext.getSocketFactory();\n  }\n\n  /** Returns a trust manager that trusts the VM's default certificate authorities. */\n  private X509TrustManager defaultTrustManager() throws GeneralSecurityException {\n    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(\n        TrustManagerFactory.getDefaultAlgorithm());\n    trustManagerFactory.init((KeyStore) null);\n    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();\n    if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {\n      throw new IllegalStateException(\"Unexpected default trust managers:\"\n          + Arrays.toString(trustManagers));\n    }\n    return (X509TrustManager) trustManagers[0];\n  }\n\n  private String[] javaNames(List<CipherSuite> cipherSuites) {\n    String[] result = new String[cipherSuites.size()];\n    for (int i = 0; i < result.length; i++) {\n      result[i] = cipherSuites.get(i).javaName();\n    }\n    return result;\n  }\n\n  /**\n   * An SSL socket factory that forwards all calls to a delegate. Override {@link #configureSocket}\n   * to customize a created socket before it is returned.\n   */\n  static class DelegatingSSLSocketFactory extends SSLSocketFactory {\n    protected final SSLSocketFactory delegate;\n\n    DelegatingSSLSocketFactory(SSLSocketFactory delegate) {\n      this.delegate = delegate;\n    }\n\n    @Override public String[] getDefaultCipherSuites() {\n      return delegate.getDefaultCipherSuites();\n    }\n\n    @Override public String[] getSupportedCipherSuites() {\n      return delegate.getSupportedCipherSuites();\n    }\n\n    @Override public Socket createSocket(\n        Socket socket, String host, int port, boolean autoClose) throws IOException {\n      return configureSocket((SSLSocket) delegate.createSocket(socket, host, port, autoClose));\n    }\n\n    @Override public Socket createSocket(String host, int port) throws IOException {\n      return configureSocket((SSLSocket) delegate.createSocket(host, port));\n    }\n\n    @Override public Socket createSocket(\n        String host, int port, InetAddress localHost, int localPort) throws IOException {\n      return configureSocket((SSLSocket) delegate.createSocket(host, port, localHost, localPort));\n    }\n\n    @Override public Socket createSocket(InetAddress host, int port) throws IOException {\n      return configureSocket((SSLSocket) delegate.createSocket(host, port));\n    }\n\n    @Override public Socket createSocket(\n        InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {\n      return configureSocket((SSLSocket) delegate.createSocket(\n          address, port, localAddress, localPort));\n    }\n\n    protected SSLSocket configureSocket(SSLSocket socket) throws IOException {\n      return socket;\n    }\n  }\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.handshake().cipherSuite());\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CustomCipherSuites().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.security.cert.X509Certificate;\nimport okhttp3.Headers;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.tls.Certificates;\nimport okhttp3.tls.HandshakeCertificates;\n\npublic final class CustomTrust {\n  // PEM files for root certificates of Comodo and Entrust. These two CAs are sufficient to view\n  // https://publicobject.com (Comodo) and https://squareup.com (Entrust). But they aren't\n  // sufficient to connect to most HTTPS sites including https://godaddy.com and https://visa.com.\n  // Typically developers will need to get a PEM file from their organization's TLS administrator.\n  final X509Certificate comodoRsaCertificationAuthority = Certificates.decodeCertificatePem(\"\"\n      + \"-----BEGIN CERTIFICATE-----\\n\"\n      + \"MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\\n\"\n      + \"hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\\n\"\n      + \"A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\\n\"\n      + \"BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5\\n\"\n      + \"MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\\n\"\n      + \"EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\\n\"\n      + \"Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\\n\"\n      + \"dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR\\n\"\n      + \"6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X\\n\"\n      + \"pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC\\n\"\n      + \"9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV\\n\"\n      + \"/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf\\n\"\n      + \"Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z\\n\"\n      + \"+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w\\n\"\n      + \"qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah\\n\"\n      + \"SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC\\n\"\n      + \"u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf\\n\"\n      + \"Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq\\n\"\n      + \"crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\\n\"\n      + \"FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\\n\"\n      + \"/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl\\n\"\n      + \"wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM\\n\"\n      + \"4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV\\n\"\n      + \"2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna\\n\"\n      + \"FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ\\n\"\n      + \"CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK\\n\"\n      + \"boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke\\n\"\n      + \"jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL\\n\"\n      + \"S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb\\n\"\n      + \"QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl\\n\"\n      + \"0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\\n\"\n      + \"NVOFBkpdn627G190\\n\"\n      + \"-----END CERTIFICATE-----\\n\");\n\n  final X509Certificate entrustRootCertificateAuthority = Certificates.decodeCertificatePem(\"\"\n      + \"-----BEGIN CERTIFICATE-----\\n\"\n      + \"MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC\\n\"\n      + \"VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0\\n\"\n      + \"Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW\\n\"\n      + \"KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl\\n\"\n      + \"cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw\\n\"\n      + \"NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw\\n\"\n      + \"NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy\\n\"\n      + \"ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV\\n\"\n      + \"BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ\\n\"\n      + \"KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo\\n\"\n      + \"Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4\\n\"\n      + \"4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9\\n\"\n      + \"KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI\\n\"\n      + \"rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi\\n\"\n      + \"94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB\\n\"\n      + \"sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi\\n\"\n      + \"gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo\\n\"\n      + \"kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE\\n\"\n      + \"vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA\\n\"\n      + \"A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t\\n\"\n      + \"O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua\\n\"\n      + \"AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP\\n\"\n      + \"9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/\\n\"\n      + \"eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m\\n\"\n      + \"0vdXcDazv/wor3ElhVsT/h5/WrQ8\\n\"\n      + \"-----END CERTIFICATE-----\\n\");\n\n  final X509Certificate letsEncryptCertificateAuthority = Certificates.decodeCertificatePem(\"\"\n      + \"-----BEGIN CERTIFICATE-----\\n\"\n      +  \"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\\n\"\n      +  \"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\\n\"\n      +  \"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\\n\"\n      +  \"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\\n\"\n      +  \"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\\n\"\n      +  \"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\\n\"\n      +  \"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\\n\"\n      +  \"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\\n\"\n      +  \"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\\n\"\n      +  \"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\\n\"\n      +  \"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\\n\"\n      +  \"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\\n\"\n      +  \"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\\n\"\n      +  \"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\\n\"\n      +  \"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\\n\"\n      +  \"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\\n\"\n      +  \"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\\n\"\n      +  \"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\\n\"\n      +  \"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\\n\"\n      +  \"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\\n\"\n      +  \"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\\n\"\n      +  \"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\\n\"\n      +  \"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\\n\"\n      +  \"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\\n\"\n      +  \"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\\n\"\n      +  \"-----END CERTIFICATE-----\");\n\n  private final OkHttpClient client;\n\n  public CustomTrust() {\n    // This implementation just embeds the PEM files in Java strings; most applications will\n    // instead read this from a resource file that gets bundled with the application.\n\n    HandshakeCertificates certificates = new HandshakeCertificates.Builder()\n        .addTrustedCertificate(letsEncryptCertificateAuthority)\n        .addTrustedCertificate(entrustRootCertificateAuthority)\n        .addTrustedCertificate(comodoRsaCertificationAuthority)\n        // Uncomment if standard certificates are also required.\n        //.addPlatformTrustedCertificates()\n        .build();\n\n    client = new OkHttpClient.Builder()\n            .sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager())\n            .build();\n  }\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) {\n        Headers responseHeaders = response.headers();\n        for (int i = 0; i < responseHeaders.size(); i++) {\n          System.out.println(responseHeaders.name(i) + \": \" + responseHeaders.value(i));\n        }\n\n        throw new IOException(\"Unexpected code \" + response);\n      }\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CustomTrust().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/HttpsServer.java",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.net.InetAddress;\nimport okhttp3.Call;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.tls.HandshakeCertificates;\nimport okhttp3.tls.HeldCertificate;\n\n/**\n * Create an HTTPS server with a self-signed certificate that OkHttp trusts.\n */\npublic class HttpsServer {\n  public void run() throws Exception {\n    HeldCertificate localhostCertificate = new HeldCertificate.Builder()\n        .addSubjectAlternativeName(\"localhost\")\n        .build();\n\n    HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()\n        .heldCertificate(localhostCertificate)\n        .build();\n    MockWebServer server = new MockWebServer();\n    server.useHttps(serverCertificates.sslSocketFactory(), false);\n    server.enqueue(new MockResponse());\n\n    HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()\n        .addTrustedCertificate(localhostCertificate.certificate())\n        .build();\n    OkHttpClient client = new OkHttpClient.Builder()\n        .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())\n        .build();\n\n    Call call = client.newCall(new Request.Builder()\n        .url(server.url(\"/\"))\n        .build());\n    Response response = call.execute();\n    System.out.println(response.handshake().tlsVersion());\n  }\n\n  public static void main(String... args) throws Exception {\n    new HttpsServer().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/LoggingInterceptors.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.util.logging.Logger;\nimport okhttp3.Interceptor;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class LoggingInterceptors {\n  private static final Logger logger = Logger.getLogger(LoggingInterceptors.class.getName());\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .addInterceptor(new LoggingInterceptor())\n      .build();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    Response response = client.newCall(request).execute();\n    response.body().close();\n  }\n\n  private static class LoggingInterceptor implements Interceptor {\n    @Override public Response intercept(Chain chain) throws IOException {\n      long t1 = System.nanoTime();\n      Request request = chain.request();\n      logger.info(String.format(\"Sending request %s on %s%n%s\",\n          request.url(), chain.connection(), request.headers()));\n      Response response = chain.proceed(request);\n\n      long t2 = System.nanoTime();\n      logger.info(String.format(\"Received response for %s in %.1fms%n%s\",\n          request.url(), (t2 - t1) / 1e6d, response.headers()));\n      return response;\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new LoggingInterceptors().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/ParseResponseWithMoshi.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.Moshi;\nimport java.io.IOException;\nimport java.util.Map;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class ParseResponseWithMoshi {\n  private final OkHttpClient client = new OkHttpClient();\n  private final Moshi moshi = new Moshi.Builder().build();\n  private final JsonAdapter<Gist> gistJsonAdapter = moshi.adapter(Gist.class);\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://api.github.com/gists/c2a7c39532239ff261be\")\n        .build();\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      Gist gist = gistJsonAdapter.fromJson(response.body().source());\n\n      for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {\n        System.out.println(entry.getKey());\n        System.out.println(entry.getValue().content);\n      }\n    }\n  }\n\n  static class Gist {\n    Map<String, GistFile> files;\n  }\n\n  static class GistFile {\n    String content;\n  }\n\n  public static void main(String... args) throws Exception {\n    new ParseResponseWithMoshi().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PerCallSettings.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.util.concurrent.TimeUnit;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class PerCallSettings {\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"http://httpbin.org/delay/1\") // This URL is served with a 1 second delay.\n        .build();\n\n    // Copy to customize OkHttp for this request.\n    OkHttpClient client1 = client.newBuilder()\n        .readTimeout(500, TimeUnit.MILLISECONDS)\n        .build();\n    try (Response response = client1.newCall(request).execute()) {\n      System.out.println(\"Response 1 succeeded: \" + response);\n    } catch (IOException e) {\n      System.out.println(\"Response 1 failed: \" + e);\n    }\n\n    // Copy to customize OkHttp for this request.\n    OkHttpClient client2 = client.newBuilder()\n        .readTimeout(3000, TimeUnit.MILLISECONDS)\n        .build();\n    try (Response response = client2.newCall(request).execute()) {\n      System.out.println(\"Response 2 succeeded: \" + response);\n    } catch (IOException e) {\n      System.out.println(\"Response 2 failed: \" + e);\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PerCallSettings().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PostFile.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.File;\nimport java.io.IOException;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic final class PostFile {\n  public static final MediaType MEDIA_TYPE_MARKDOWN\n      = MediaType.get(\"text/x-markdown; charset=utf-8\");\n\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    File file = new File(\"README.md\");\n\n    Request request = new Request.Builder()\n        .url(\"https://api.github.com/markdown/raw\")\n        .post(RequestBody.create(file, MEDIA_TYPE_MARKDOWN))\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PostFile().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PostForm.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.FormBody;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic final class PostForm {\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    RequestBody formBody = new FormBody.Builder()\n        .add(\"search\", \"Jurassic Park\")\n        .build();\n    Request request = new Request.Builder()\n        .url(\"https://en.wikipedia.org/w/index.php\")\n        .post(formBody)\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PostForm().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PostMultipart.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.File;\nimport java.io.IOException;\nimport okhttp3.MediaType;\nimport okhttp3.MultipartBody;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic final class PostMultipart {\n  /**\n   * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running\n   * these examples, please request your own client ID! https://api.imgur.com/oauth2\n   */\n  private static final String IMGUR_CLIENT_ID = \"9199fdef135c122\";\n  private static final MediaType MEDIA_TYPE_PNG = MediaType.get(\"image/png\");\n\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image\n    RequestBody requestBody = new MultipartBody.Builder()\n        .setType(MultipartBody.FORM)\n        .addFormDataPart(\"title\", \"Square Logo\")\n        .addFormDataPart(\"image\", \"logo-square.png\",\n            RequestBody.create(\n                new File(\"docs/images/logo-square.png\"),\n                MEDIA_TYPE_PNG))\n        .build();\n\n    Request request = new Request.Builder()\n        .header(\"Authorization\", \"Client-ID \" + IMGUR_CLIENT_ID)\n        .url(\"https://api.imgur.com/3/image\")\n        .post(requestBody)\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PostMultipart().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PostStreaming.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\nimport okio.BufferedSink;\n\npublic final class PostStreaming {\n  public static final MediaType MEDIA_TYPE_MARKDOWN\n      = MediaType.get(\"text/x-markdown; charset=utf-8\");\n\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    RequestBody requestBody = new RequestBody() {\n      @Override public MediaType contentType() {\n        return MEDIA_TYPE_MARKDOWN;\n      }\n\n      @Override public void writeTo(BufferedSink sink) throws IOException {\n        sink.writeUtf8(\"Numbers\\n\");\n        sink.writeUtf8(\"-------\\n\");\n        for (int i = 2; i <= 997; i++) {\n          sink.writeUtf8(String.format(\" * %s = %s\\n\", i, factor(i)));\n        }\n      }\n\n      private String factor(int n) {\n        for (int i = 2; i < n; i++) {\n          int x = n / i;\n          if (x * i == n) return factor(x) + \" × \" + i;\n        }\n        return Integer.toString(n);\n      }\n    };\n\n    Request request = new Request.Builder()\n        .url(\"https://api.github.com/markdown/raw\")\n        .post(requestBody)\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PostStreaming().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PostStreamingWithPipe.java",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\nimport okio.BufferedSink;\nimport okio.Okio;\nimport okio.Pipe;\n\npublic final class PostStreamingWithPipe {\n  public static final MediaType MEDIA_TYPE_MARKDOWN\n      = MediaType.get(\"text/x-markdown; charset=utf-8\");\n\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    final PipeBody pipeBody = new PipeBody();\n\n    Request request = new Request.Builder()\n        .url(\"https://api.github.com/markdown/raw\")\n        .post(pipeBody)\n        .build();\n\n    streamPrimesToSinkAsynchronously(pipeBody.sink());\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  private void streamPrimesToSinkAsynchronously(final BufferedSink sink) {\n    Thread thread = new Thread(\"writer\") {\n      @Override public void run() {\n        try {\n          sink.writeUtf8(\"Numbers\\n\");\n          sink.writeUtf8(\"-------\\n\");\n          for (int i = 2; i <= 997; i++) {\n            System.out.println(i);\n            Thread.sleep(10);\n            sink.writeUtf8(String.format(\" * %s = %s\\n\", i, factor(i)));\n          }\n          sink.close();\n        } catch (IOException | InterruptedException e) {\n          e.printStackTrace();\n        }\n      }\n\n      private String factor(int n) {\n        for (int i = 2; i < n; i++) {\n          int x = n / i;\n          if (x * i == n) return factor(x) + \" × \" + i;\n        }\n        return Integer.toString(n);\n      }\n    };\n\n    thread.start();\n  }\n\n  /**\n   * This request body makes it possible for another thread to stream data to the uploading request.\n   * This is potentially useful for posting live event streams like video capture. Callers should\n   * write to {@code sink()} and close it to complete the post.\n   */\n  static final class PipeBody extends RequestBody {\n    private final Pipe pipe = new Pipe(8192);\n    private final BufferedSink sink = Okio.buffer(pipe.sink());\n\n    public BufferedSink sink() {\n      return sink;\n    }\n\n    @Override public MediaType contentType() {\n      return MEDIA_TYPE_MARKDOWN;\n    }\n\n    @Override public void writeTo(BufferedSink sink) throws IOException {\n      sink.writeAll(pipe.source());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PostStreamingWithPipe().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PostString.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic final class PostString {\n  public static final MediaType MEDIA_TYPE_MARKDOWN\n      = MediaType.get(\"text/x-markdown; charset=utf-8\");\n\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    String postBody = \"\"\n        + \"Releases\\n\"\n        + \"--------\\n\"\n        + \"\\n\"\n        + \" * _1.0_ May 6, 2013\\n\"\n        + \" * _1.1_ June 15, 2013\\n\"\n        + \" * _1.2_ August 11, 2013\\n\";\n\n    Request request = new Request.Builder()\n        .url(\"https://api.github.com/markdown/raw\")\n        .post(RequestBody.create(postBody, MEDIA_TYPE_MARKDOWN))\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PostString().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PreemptiveAuth.java",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.Credentials;\nimport okhttp3.Interceptor;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class PreemptiveAuth {\n  private final OkHttpClient client;\n\n  public PreemptiveAuth() {\n    client = new OkHttpClient.Builder()\n        .addInterceptor(\n            new BasicAuthInterceptor(\"publicobject.com\", \"jesse\", \"password1\"))\n        .build();\n  }\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/secrets/hellosecret.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PreemptiveAuth().run();\n  }\n\n  static final class BasicAuthInterceptor implements Interceptor {\n    private final String credentials;\n    private final String host;\n\n    BasicAuthInterceptor(String host, String username, String password) {\n      this.credentials = Credentials.basic(username, password);\n      this.host = host;\n    }\n\n    @Override public Response intercept(Chain chain) throws IOException {\n      Request request = chain.request();\n      if (request.url().host().equals(host)) {\n        request = request.newBuilder()\n            .header(\"Authorization\", credentials)\n            .build();\n      }\n      return chain.proceed(request);\n    }\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PrintEvents.java",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.Proxy;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicLong;\nimport okhttp3.Call;\nimport okhttp3.Callback;\nimport okhttp3.Connection;\nimport okhttp3.EventListener;\nimport okhttp3.Handshake;\nimport okhttp3.HttpUrl;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Protocol;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\n\npublic final class PrintEvents {\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .eventListenerFactory(PrintingEventListener.FACTORY)\n      .build();\n\n  public void run() throws Exception {\n    Request washingtonPostRequest = new Request.Builder()\n        .url(\"https://www.washingtonpost.com/\")\n        .build();\n    client.newCall(washingtonPostRequest).enqueue(new Callback() {\n      @Override public void onFailure(Call call, IOException e) {\n      }\n\n      @Override public void onResponse(Call call, Response response) throws IOException {\n        try (ResponseBody body = response.body()) {\n          // Consume and discard the response body.\n          body.source().readByteString();\n        }\n      }\n    });\n\n    Request newYorkTimesRequest = new Request.Builder()\n        .url(\"https://www.nytimes.com/\")\n        .build();\n    client.newCall(newYorkTimesRequest).enqueue(new Callback() {\n      @Override public void onFailure(Call call, IOException e) {\n      }\n\n      @Override public void onResponse(Call call, Response response) throws IOException {\n        try (ResponseBody body = response.body()) {\n          // Consume and discard the response body.\n          body.source().readByteString();\n        }\n      }\n    });\n  }\n\n  public static void main(String... args) throws Exception {\n    new PrintEvents().run();\n  }\n\n  private static final class PrintingEventListener extends EventListener {\n    private static final Factory FACTORY = new Factory() {\n      final AtomicLong nextCallId = new AtomicLong(1L);\n\n      @Override public EventListener create(Call call) {\n        long callId = nextCallId.getAndIncrement();\n        System.out.printf(\"%04d %s%n\", callId, call.request().url());\n        return new PrintingEventListener(callId, System.nanoTime());\n      }\n    };\n\n    final long callId;\n    final long callStartNanos;\n\n    PrintingEventListener(long callId, long callStartNanos) {\n      this.callId = callId;\n      this.callStartNanos = callStartNanos;\n    }\n\n    private void printEvent(String name) {\n      long elapsedNanos = System.nanoTime() - callStartNanos;\n      System.out.printf(\"%04d %.3f %s%n\", callId, elapsedNanos / 1000000000d, name);\n    }\n\n    @Override public void proxySelectStart(Call call, HttpUrl url) {\n      printEvent(\"proxySelectStart\");\n    }\n\n    @Override public void proxySelectEnd(Call call, HttpUrl url, List<Proxy> proxies) {\n      printEvent(\"proxySelectEnd\");\n    }\n\n    @Override public void callStart(Call call) {\n      printEvent(\"callStart\");\n    }\n\n    @Override public void dnsStart(Call call, String domainName) {\n      printEvent(\"dnsStart\");\n    }\n\n    @Override public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {\n      printEvent(\"dnsEnd\");\n    }\n\n    @Override public void connectStart(\n        Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {\n      printEvent(\"connectStart\");\n    }\n\n    @Override public void secureConnectStart(Call call) {\n      printEvent(\"secureConnectStart\");\n    }\n\n    @Override public void secureConnectEnd(Call call, Handshake handshake) {\n      printEvent(\"secureConnectEnd\");\n    }\n\n    @Override public void connectEnd(\n        Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {\n      printEvent(\"connectEnd\");\n    }\n\n    @Override public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy,\n        Protocol protocol, IOException ioe) {\n      printEvent(\"connectFailed\");\n    }\n\n    @Override public void connectionAcquired(Call call, Connection connection) {\n      printEvent(\"connectionAcquired\");\n    }\n\n    @Override public void connectionReleased(Call call, Connection connection) {\n      printEvent(\"connectionReleased\");\n    }\n\n    @Override public void requestHeadersStart(Call call) {\n      printEvent(\"requestHeadersStart\");\n    }\n\n    @Override public void requestHeadersEnd(Call call, Request request) {\n      printEvent(\"requestHeadersEnd\");\n    }\n\n    @Override public void requestBodyStart(Call call) {\n      printEvent(\"requestBodyStart\");\n    }\n\n    @Override public void requestBodyEnd(Call call, long byteCount) {\n      printEvent(\"requestBodyEnd\");\n    }\n\n    @Override public void requestFailed(Call call, IOException ioe) {\n      printEvent(\"requestFailed\");\n    }\n\n    @Override public void responseHeadersStart(Call call) {\n      printEvent(\"responseHeadersStart\");\n    }\n\n    @Override public void responseHeadersEnd(Call call, Response response) {\n      printEvent(\"responseHeadersEnd\");\n    }\n\n    @Override public void responseBodyStart(Call call) {\n      printEvent(\"responseBodyStart\");\n    }\n\n    @Override public void responseBodyEnd(Call call, long byteCount) {\n      printEvent(\"responseBodyEnd\");\n    }\n\n    @Override public void responseFailed(Call call, IOException ioe) {\n      printEvent(\"responseFailed\");\n    }\n\n    @Override public void callEnd(Call call) {\n      printEvent(\"callEnd\");\n    }\n\n    @Override public void callFailed(Call call, IOException ioe) {\n      printEvent(\"callFailed\");\n    }\n\n    @Override public void canceled(Call call) {\n      printEvent(\"canceled\");\n    }\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/PrintEventsNonConcurrent.java",
    "content": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.Proxy;\nimport java.util.List;\nimport okhttp3.Call;\nimport okhttp3.Connection;\nimport okhttp3.EventListener;\nimport okhttp3.Handshake;\nimport okhttp3.HttpUrl;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Protocol;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\n/**\n * This prints events for a single in-flight call. It won't work for multiple concurrent calls\n * because we don't know what callStartNanos refers to.\n */\npublic final class PrintEventsNonConcurrent {\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .eventListener(new PrintingEventListener())\n      .build();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    System.out.println(\"REQUEST 1 (new connection)\");\n    try (Response response = client.newCall(request).execute()) {\n      // Consume and discard the response body.\n      response.body().source().readByteString();\n    }\n\n    System.out.println(\"REQUEST 2 (pooled connection)\");\n    try (Response response = client.newCall(request).execute()) {\n      // Consume and discard the response body.\n      response.body().source().readByteString();\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new PrintEventsNonConcurrent().run();\n  }\n\n  private static final class PrintingEventListener extends EventListener {\n    long callStartNanos;\n\n    private void printEvent(String name) {\n      long nowNanos = System.nanoTime();\n      if (name.equals(\"callStart\")) {\n        callStartNanos = nowNanos;\n      }\n      long elapsedNanos = nowNanos - callStartNanos;\n      System.out.printf(\"%.3f %s%n\", elapsedNanos / 1000000000d, name);\n    }\n\n    @Override public void callStart(Call call) {\n      printEvent(\"callStart\");\n    }\n\n    @Override public void proxySelectStart(Call call, HttpUrl url) {\n      printEvent(\"proxySelectStart\");\n    }\n\n    @Override public void proxySelectEnd(Call call, HttpUrl url, List<Proxy> proxies) {\n      printEvent(\"proxySelectEnd\");\n    }\n\n    @Override public void dnsStart(Call call, String domainName) {\n      printEvent(\"dnsStart\");\n    }\n\n    @Override public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {\n      printEvent(\"dnsEnd\");\n    }\n\n    @Override public void connectStart(\n        Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {\n      printEvent(\"connectStart\");\n    }\n\n    @Override public void secureConnectStart(Call call) {\n      printEvent(\"secureConnectStart\");\n    }\n\n    @Override public void secureConnectEnd(Call call, Handshake handshake) {\n      printEvent(\"secureConnectEnd\");\n    }\n\n    @Override public void connectEnd(\n        Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {\n      printEvent(\"connectEnd\");\n    }\n\n    @Override public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy,\n        Protocol protocol, IOException ioe) {\n      printEvent(\"connectFailed\");\n    }\n\n    @Override public void connectionAcquired(Call call, Connection connection) {\n      printEvent(\"connectionAcquired\");\n    }\n\n    @Override public void connectionReleased(Call call, Connection connection) {\n      printEvent(\"connectionReleased\");\n    }\n\n    @Override public void requestHeadersStart(Call call) {\n      printEvent(\"requestHeadersStart\");\n    }\n\n    @Override public void requestHeadersEnd(Call call, Request request) {\n      printEvent(\"requestHeadersEnd\");\n    }\n\n    @Override public void requestBodyStart(Call call) {\n      printEvent(\"requestBodyStart\");\n    }\n\n    @Override public void requestBodyEnd(Call call, long byteCount) {\n      printEvent(\"requestBodyEnd\");\n    }\n\n    @Override public void requestFailed(Call call, IOException ioe) {\n      printEvent(\"requestFailed\");\n    }\n\n    @Override public void responseHeadersStart(Call call) {\n      printEvent(\"responseHeadersStart\");\n    }\n\n    @Override public void responseHeadersEnd(Call call, Response response) {\n      printEvent(\"responseHeadersEnd\");\n    }\n\n    @Override public void responseBodyStart(Call call) {\n      printEvent(\"responseBodyStart\");\n    }\n\n    @Override public void responseBodyEnd(Call call, long byteCount) {\n      printEvent(\"responseBodyEnd\");\n    }\n\n    @Override public void responseFailed(Call call, IOException ioe) {\n      printEvent(\"responseFailed\");\n    }\n\n    @Override public void callEnd(Call call) {\n      printEvent(\"callEnd\");\n    }\n\n    @Override public void callFailed(Call call, IOException ioe) {\n      printEvent(\"callFailed\");\n    }\n\n    @Override public void canceled(Call call) {\n      printEvent(\"canceled\");\n    }\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/Progress.java",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okio.Buffer;\nimport okio.BufferedSource;\nimport okio.ForwardingSource;\nimport okio.Okio;\nimport okio.Source;\n\npublic final class Progress {\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    final ProgressListener progressListener = new ProgressListener() {\n      boolean firstUpdate = true;\n\n      @Override public void update(long bytesRead, long contentLength, boolean done) {\n        if (done) {\n          System.out.println(\"completed\");\n        } else {\n          if (firstUpdate) {\n            firstUpdate = false;\n            if (contentLength == -1) {\n              System.out.println(\"content-length: unknown\");\n            } else {\n              System.out.format(\"content-length: %d\\n\", contentLength);\n            }\n          }\n\n          System.out.println(bytesRead);\n\n          if (contentLength != -1) {\n            System.out.format(\"%d%% done\\n\", (100 * bytesRead) / contentLength);\n          }\n        }\n      }\n    };\n\n    OkHttpClient client = new OkHttpClient.Builder()\n        .addNetworkInterceptor(chain -> {\n          Response originalResponse = chain.proceed(chain.request());\n          return originalResponse.newBuilder()\n              .body(new ProgressResponseBody(originalResponse.body(), progressListener))\n              .build();\n        })\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new Progress().run();\n  }\n\n  private static class ProgressResponseBody extends ResponseBody {\n\n    private final ResponseBody responseBody;\n    private final ProgressListener progressListener;\n    private BufferedSource bufferedSource;\n\n    ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {\n      this.responseBody = responseBody;\n      this.progressListener = progressListener;\n    }\n\n    @Override public MediaType contentType() {\n      return responseBody.contentType();\n    }\n\n    @Override public long contentLength() {\n      return responseBody.contentLength();\n    }\n\n    @Override public BufferedSource source() {\n      if (bufferedSource == null) {\n        bufferedSource = Okio.buffer(source(responseBody.source()));\n      }\n      return bufferedSource;\n    }\n\n    private Source source(Source source) {\n      return new ForwardingSource(source) {\n        long totalBytesRead = 0L;\n\n        @Override public long read(Buffer sink, long byteCount) throws IOException {\n          long bytesRead = super.read(sink, byteCount);\n          // read() returns the number of bytes read, or -1 if this source is exhausted.\n          totalBytesRead += bytesRead != -1 ? bytesRead : 0;\n          progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);\n          return bytesRead;\n        }\n      };\n    }\n  }\n\n  interface ProgressListener {\n    void update(long bytesRead, long contentLength, boolean done);\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/RequestBodyCompression.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.Moshi;\nimport com.squareup.moshi.Types;\nimport java.io.IOException;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport okhttp3.Interceptor;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic final class RequestBodyCompression {\n  /**\n   * The Google API KEY for OkHttp recipes. If you're using Google APIs for anything other than\n   * running these examples, please request your own client ID!\n   *\n   * https://console.developers.google.com/project\n   */\n  public static final String GOOGLE_API_KEY = \"AIzaSyAx2WZYe0My0i-uGurpvraYJxO7XNbwiGs\";\n  public static final MediaType MEDIA_TYPE_JSON = MediaType.get(\"application/json\");\n\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .addInterceptor(new GzipRequestInterceptor())\n      .build();\n  private final Moshi moshi = new Moshi.Builder().build();\n  private final JsonAdapter<Map<String, String>> mapJsonAdapter = moshi.adapter(\n      Types.newParameterizedType(Map.class, String.class, String.class));\n\n  public void run() throws Exception {\n    Map<String, String> requestBody = new LinkedHashMap<>();\n    requestBody.put(\"longUrl\", \"https://publicobject.com/2014/12/04/html-formatting-javadocs/\");\n    RequestBody jsonRequestBody = RequestBody.create(\n        mapJsonAdapter.toJson(requestBody), MEDIA_TYPE_JSON);\n    Request request = new Request.Builder()\n        .url(\"https://www.googleapis.com/urlshortener/v1/url?key=\" + GOOGLE_API_KEY)\n        .post(jsonRequestBody)\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new RequestBodyCompression().run();\n  }\n\n  /** This interceptor compresses the HTTP request body. Many webservers can't handle this! */\n  static class GzipRequestInterceptor implements Interceptor {\n    @Override public Response intercept(Chain chain) throws IOException {\n      Request originalRequest = chain.request();\n      if (originalRequest.body() == null || originalRequest.header(\"Content-Encoding\") != null) {\n        return chain.proceed(originalRequest);\n      }\n\n      Request compressedRequest = originalRequest.newBuilder()\n          .gzip()\n          .build();\n      return chain.proceed(compressedRequest);\n    }\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/RewriteResponseCacheControl.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.File;\nimport java.io.IOException;\nimport okhttp3.Cache;\nimport okhttp3.Interceptor;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class RewriteResponseCacheControl {\n  /** Dangerous interceptor that rewrites the server's cache-control header. */\n  private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = chain -> {\n    Response originalResponse = chain.proceed(chain.request());\n    return originalResponse.newBuilder()\n        .header(\"Cache-Control\", \"max-age=60\")\n        .build();\n  };\n\n  private final OkHttpClient client;\n\n  public RewriteResponseCacheControl(File cacheDirectory) throws Exception {\n    Cache cache = new Cache(cacheDirectory, 1024 * 1024);\n    cache.evictAll();\n\n    client = new OkHttpClient.Builder()\n        .cache(cache)\n        .build();\n  }\n\n  public void run() throws Exception {\n    for (int i = 0; i < 5; i++) {\n      System.out.println(\"    Request: \" + i);\n\n      Request request = new Request.Builder()\n          .url(\"https://api.github.com/search/repositories?q=http\")\n          .build();\n\n      OkHttpClient clientForCall;\n      if (i == 2) {\n        // Force this request's response to be written to the cache. This way, subsequent responses\n        // can be read from the cache.\n        System.out.println(\"Force cache: true\");\n        clientForCall = client.newBuilder()\n            .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)\n            .build();\n      } else {\n        System.out.println(\"Force cache: false\");\n        clientForCall = client;\n      }\n\n      try (Response response = clientForCall.newCall(request).execute()) {\n        if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n        System.out.println(\"    Network: \" + (response.networkResponse() != null));\n        System.out.println();\n      }\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new RewriteResponseCacheControl(new File(\"RewriteResponseCacheControl.tmp\")).run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/SynchronousGet.java",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport java.io.IOException;\nimport okhttp3.Headers;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class SynchronousGet {\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      Headers responseHeaders = response.headers();\n      for (int i = 0; i < responseHeaders.size(); i++) {\n        System.out.println(responseHeaders.name(i) + \": \" + responseHeaders.value(i));\n      }\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new SynchronousGet().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/UploadProgress.java",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes;\n\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\nimport okio.Buffer;\nimport okio.BufferedSink;\nimport okio.ForwardingSink;\nimport okio.Okio;\nimport okio.Sink;\nimport java.io.File;\nimport java.io.IOException;\n\npublic final class UploadProgress {\n  private static final String IMGUR_CLIENT_ID = \"9199fdef135c122\";\n  private static final MediaType MEDIA_TYPE_PNG = MediaType.get(\"image/png\");\n\n  private final OkHttpClient client = new OkHttpClient();\n\n  public void run() throws Exception {\n    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image\n    final ProgressListener progressListener = new ProgressListener() {\n      boolean firstUpdate = true;\n\n      @Override public void update(long bytesWritten, long contentLength, boolean done) {\n        if (done) {\n          System.out.println(\"completed\");\n        } else {\n          if (firstUpdate) {\n            firstUpdate = false;\n            if (contentLength == -1) {\n              System.out.println(\"content-length: unknown\");\n            } else {\n              System.out.format(\"content-length: %d\\n\", contentLength);\n            }\n          }\n\n          System.out.println(bytesWritten);\n\n          if (contentLength != -1) {\n            System.out.format(\"%d%% done\\n\", (100 * bytesWritten) / contentLength);\n          }\n        }\n      }\n    };\n\n    RequestBody requestBody = RequestBody.create(\n      new File(\"docs/images/logo-square.png\"),\n      MEDIA_TYPE_PNG);\n\n    Request request = new Request.Builder()\n      .header(\"Authorization\", \"Client-ID \" + IMGUR_CLIENT_ID)\n      .url(\"https://api.imgur.com/3/image\")\n      .post(new ProgressRequestBody(requestBody, progressListener))\n      .build();\n\n    Response response = client.newCall(request).execute();\n    if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n    System.out.println(response.body().string());\n  }\n\n  public static void main(String... args) throws Exception {\n    new UploadProgress().run();\n  }\n\n  private static class ProgressRequestBody extends RequestBody {\n\n    private final ProgressListener progressListener;\n    private final RequestBody delegate;\n\n    public ProgressRequestBody(RequestBody delegate, ProgressListener progressListener) {\n      this.delegate = delegate;\n      this.progressListener = progressListener;\n    }\n\n    @Override\n    public MediaType contentType() {\n      return delegate.contentType();\n    }\n\n    @Override\n    public long contentLength() throws IOException {\n      return delegate.contentLength();\n    }\n\n    @Override\n    public void writeTo(BufferedSink sink) throws IOException {\n      BufferedSink bufferedSink = Okio.buffer(sink(sink));\n      delegate.writeTo(bufferedSink);\n      bufferedSink.flush();\n    }\n\n    public Sink sink(Sink sink) {\n      return new ForwardingSink(sink) {\n        private long totalBytesWritten = 0L;\n        private boolean completed = false;\n\n        @Override\n        public void write(Buffer source, long byteCount) throws IOException {\n          super.write(source, byteCount);\n          totalBytesWritten += byteCount;\n          progressListener.update(totalBytesWritten, contentLength(), completed);\n        }\n\n        @Override\n        public void close() throws IOException {\n          super.close();\n          if (!completed) {\n            completed = true;\n            progressListener.update(totalBytesWritten, contentLength(), completed);\n          }\n        }\n      };\n    }\n  }\n\n  interface ProgressListener {\n    void update(long bytesWritten, long contentLength, boolean done);\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/WebSocketEcho.java",
    "content": "package okhttp3.recipes;\n\nimport java.util.concurrent.TimeUnit;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.WebSocket;\nimport okhttp3.WebSocketListener;\nimport okio.ByteString;\n\npublic final class WebSocketEcho extends WebSocketListener {\n  private void run() {\n    OkHttpClient client = new OkHttpClient.Builder()\n        .readTimeout(0,  TimeUnit.MILLISECONDS)\n        .build();\n\n    Request request = new Request.Builder()\n        .url(\"ws://echo.websocket.org\")\n        .build();\n    client.newWebSocket(request, this);\n\n    // Trigger shutdown of the dispatcher's executor so this process exits immediately.\n    client.dispatcher().executorService().shutdown();\n  }\n\n  @Override public void onOpen(WebSocket webSocket, Response response) {\n    webSocket.send(\"Hello...\");\n    webSocket.send(\"...World!\");\n    webSocket.send(ByteString.decodeHex(\"deadbeef\"));\n    webSocket.close(1000, \"Goodbye, World!\");\n  }\n\n  @Override public void onMessage(WebSocket webSocket, String text) {\n    System.out.println(\"MESSAGE: \" + text);\n  }\n\n  @Override public void onMessage(WebSocket webSocket, ByteString bytes) {\n    System.out.println(\"MESSAGE: \" + bytes.hex());\n  }\n\n  @Override public void onClosing(WebSocket webSocket, int code, String reason) {\n    webSocket.close(1000, null);\n    System.out.println(\"CLOSE: \" + code + \" \" + reason);\n  }\n\n  @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) {\n    t.printStackTrace();\n  }\n\n  public static void main(String... args) {\n    new WebSocketEcho().run();\n  }\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/AccessHeaders.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass AccessHeaders {\n  private val client = OkHttpClient()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.github.com/repos/square/okhttp/issues\")\n        .header(\"User-Agent\", \"OkHttp Headers.java\")\n        .addHeader(\"Accept\", \"application/json; q=0.5\")\n        .addHeader(\"Accept\", \"application/vnd.github.v3+json\")\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      println(\"Server: ${response.header(\"Server\")}\")\n      println(\"Date: ${response.header(\"Date\")}\")\n      println(\"Vary: ${response.headers(\"Vary\")}\")\n    }\n  }\n}\n\nfun main() {\n  AccessHeaders().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/AsynchronousGet.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\n\nclass AsynchronousGet {\n  private val client = OkHttpClient()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"http://publicobject.com/helloworld.txt\")\n        .build()\n\n    client.newCall(request).enqueue(\n      object : Callback {\n        override fun onFailure(\n          call: Call,\n          e: IOException,\n        ) {\n          e.printStackTrace()\n        }\n\n        override fun onResponse(\n          call: Call,\n          response: Response,\n        ) {\n          response.use {\n            if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n            for ((name, value) in response.headers) {\n              println(\"$name: $value\")\n            }\n\n            println(response.body.string())\n          }\n        }\n      },\n    )\n  }\n}\n\nfun main() {\n  AsynchronousGet().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/Authenticate.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.Authenticator\nimport okhttp3.Credentials\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.Route\n\nclass Authenticate {\n  private val client =\n    OkHttpClient\n      .Builder()\n      .authenticator(\n        object : Authenticator {\n          @Throws(IOException::class)\n          override fun authenticate(\n            route: Route?,\n            response: Response,\n          ): Request? {\n            if (response.request.header(\"Authorization\") != null) {\n              return null // Give up, we've already attempted to authenticate.\n            }\n\n            println(\"Authenticating for response: $response\")\n            println(\"Challenges: ${response.challenges()}\")\n            val credential = Credentials.basic(\"jesse\", \"password1\")\n            return response.request\n              .newBuilder()\n              .header(\"Authorization\", credential)\n              .build()\n          }\n        },\n      ).build()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"http://publicobject.com/secrets/hellosecret.txt\")\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      println(response.body.string())\n    }\n  }\n}\n\nfun main() {\n  Authenticate().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/CacheResponse.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.File\nimport java.io.IOException\nimport okhttp3.Cache\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass CacheResponse(\n  cacheDirectory: File,\n) {\n  private val client: OkHttpClient =\n    OkHttpClient\n      .Builder()\n      .cache(\n        Cache(\n          directory = cacheDirectory,\n          // 1 MiB.\n          maxSize = 10L * 1024L * 1024L,\n        ),\n      ).build()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"http://publicobject.com/helloworld.txt\")\n        .build()\n\n    val response1Body =\n      client.newCall(request).execute().use {\n        if (!it.isSuccessful) throw IOException(\"Unexpected code $it\")\n\n        println(\"Response 1 response:          $it\")\n        println(\"Response 1 cache response:    ${it.cacheResponse}\")\n        println(\"Response 1 network response:  ${it.networkResponse}\")\n        return@use it.body.string()\n      }\n\n    val response2Body =\n      client.newCall(request).execute().use {\n        if (!it.isSuccessful) throw IOException(\"Unexpected code $it\")\n\n        println(\"Response 2 response:          $it\")\n        println(\"Response 2 cache response:    ${it.cacheResponse}\")\n        println(\"Response 2 network response:  ${it.networkResponse}\")\n        return@use it.body.string()\n      }\n\n    println(\"Response 2 equals Response 1? \" + (response1Body == response2Body))\n  }\n}\n\nfun main() {\n  CacheResponse(File(\"CacheResponse.tmp\")).run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/CancelCall.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport java.util.concurrent.Executors\nimport java.util.concurrent.TimeUnit\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass CancelCall {\n  private val executor = Executors.newScheduledThreadPool(1)\n  private val client = OkHttpClient()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"http://httpbin.org/delay/2\") // This URL is served with a 2 second delay.\n        .build()\n\n    val startNanos = System.nanoTime()\n    val call = client.newCall(request)\n\n    // Schedule a job to cancel the call in 1 second.\n    executor.schedule({\n      System.out.printf(\"%.2f Canceling call.%n\", (System.nanoTime() - startNanos) / 1e9f)\n      call.cancel()\n      System.out.printf(\"%.2f Canceled call.%n\", (System.nanoTime() - startNanos) / 1e9f)\n    }, 1, TimeUnit.SECONDS)\n\n    System.out.printf(\"%.2f Executing call.%n\", (System.nanoTime() - startNanos) / 1e9f)\n    try {\n      call.execute().use { response ->\n        System.out.printf(\n          \"%.2f Call was expected to fail, but completed: %s%n\",\n          (System.nanoTime() - startNanos) / 1e9f,\n          response,\n        )\n      }\n    } catch (e: IOException) {\n      System.out.printf(\n        \"%.2f Call failed as expected: %s%n\",\n        (System.nanoTime() - startNanos) / 1e9f,\n        e,\n      )\n    }\n  }\n}\n\nfun main() {\n  CancelCall().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/CertificatePinning.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.CertificatePinner\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass CertificatePinning {\n  private val client =\n    OkHttpClient\n      .Builder()\n      .certificatePinner(\n        CertificatePinner\n          .Builder()\n          .add(\"publicobject.com\", \"sha256/Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=\")\n          .build(),\n      ).build()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://publicobject.com/robots.txt\")\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      for (certificate in response.handshake!!.peerCertificates) {\n        println(CertificatePinner.pin(certificate))\n      }\n    }\n  }\n}\n\nfun main() {\n  CertificatePinning().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/ConfigureTimeouts.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.util.concurrent.TimeUnit\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass ConfigureTimeouts {\n  private val client: OkHttpClient =\n    OkHttpClient\n      .Builder()\n      .connectTimeout(5, TimeUnit.SECONDS)\n      .writeTimeout(5, TimeUnit.SECONDS)\n      .readTimeout(5, TimeUnit.SECONDS)\n      .callTimeout(10, TimeUnit.SECONDS)\n      .build()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"http://httpbin.org/delay/2\") // This URL is served with a 2 second delay.\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      println(\"Response completed: $response\")\n    }\n  }\n}\n\nfun main() {\n  ConfigureTimeouts().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/CustomTrust.kt",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport java.security.cert.X509Certificate\nimport okhttp3.OkHttpClient\nimport okhttp3.Request.Builder\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.decodeCertificatePem\n\nclass CustomTrust {\n  // PEM files for root certificates of Comodo and Entrust. These two CAs are sufficient to view\n  // https://publicobject.com (Comodo) and https://squareup.com (Entrust). But they aren't\n  // sufficient to connect to most HTTPS sites including https://godaddy.com and https://visa.com.\n  // Typically developers will need to get a PEM file from their organization's TLS administrator.\n  val comodoRsaCertificationAuthority =\n    \"\"\"\n    -----BEGIN CERTIFICATE-----\n    MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\n    hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n    A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\n    BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5\n    MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\n    EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\n    Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\n    dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR\n    6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X\n    pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC\n    9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV\n    /erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf\n    Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z\n    +pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w\n    qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah\n    SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC\n    u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf\n    Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq\n    crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\n    FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\n    /wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl\n    wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM\n    4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV\n    2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna\n    FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ\n    CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK\n    boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke\n    jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL\n    S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb\n    QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl\n    0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n    NVOFBkpdn627G190\n    -----END CERTIFICATE-----\n    \"\"\".trimIndent().decodeCertificatePem()\n\n  // CN=Entrust Root Certification Authority, OU=\"(c) 2006 Entrust, Inc.\", OU=www.entrust.net/CPS is incorporated by reference, O=\"Entrust, Inc.\", C=US\n  val entrustRootCertificateAuthority =\n    \"\"\"\n    -----BEGIN CERTIFICATE-----\n    MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC\n    VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0\n    Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW\n    KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl\n    cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw\n    NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw\n    NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy\n    ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV\n    BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ\n    KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo\n    Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4\n    4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9\n    KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI\n    rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi\n    94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB\n    sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi\n    gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo\n    kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE\n    vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA\n    A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t\n    O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua\n    AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP\n    9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/\n    eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m\n    0vdXcDazv/wor3ElhVsT/h5/WrQ8\n    -----END CERTIFICATE-----\n    \"\"\".trimIndent().decodeCertificatePem()\n\n  // CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US\n  val letsEncryptCertificateAuthority =\n    \"\"\"\n    -----BEGIN CERTIFICATE-----\n    MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n    MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n    DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n    SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n    GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n    AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n    q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n    SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n    Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n    a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n    /PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n    AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n    CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n    bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n    c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n    VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n    ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n    MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n    Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n    AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n    uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n    wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n    X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n    PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n    KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n    -----END CERTIFICATE-----\n    \"\"\".trimIndent().decodeCertificatePem()\n\n  private val client: OkHttpClient\n\n  init {\n    // This implementation just embeds the PEM files in Java strings; most applications will\n    // instead read this from a resource file that gets bundled with the application.\n    val certificates =\n      HandshakeCertificates\n        .Builder()\n        .addTrustedCertificate(letsEncryptCertificateAuthority)\n        .addTrustedCertificate(entrustRootCertificateAuthority)\n        .addTrustedCertificate(comodoRsaCertificationAuthority)\n        // Uncomment if standard certificates are also required.\n        // .addPlatformTrustedCertificates()\n        .build()\n    client =\n      OkHttpClient\n        .Builder()\n        .sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager)\n        .build()\n  }\n\n  fun run() {\n    showUrl(\"https://squareup.com/robots.txt\")\n    showUrl(\"https://publicobject.com/helloworld.txt\")\n  }\n\n  private fun showUrl(url: String) {\n    val request = Builder().url(url).build()\n    client\n      .newCall(request)\n      .execute()\n      .use { response ->\n        if (!response.isSuccessful) {\n          val responseHeaders = response.headers\n          for (i in 0 until responseHeaders.size) {\n            println(responseHeaders.name(i) + \": \" + responseHeaders.value(i))\n          }\n          throw IOException(\"Unexpected code $response\")\n        }\n        println(response.body.string())\n\n        for (peerCertificate in response.handshake?.peerCertificates.orEmpty()) {\n          println((peerCertificate as X509Certificate).subjectDN)\n        }\n      }\n  }\n}\n\nfun main() {\n  CustomTrust().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/DevServer.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport java.net.HttpURLConnection.HTTP_MOVED_TEMP\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport okhttp3.tls.HandshakeCertificates\nimport okhttp3.tls.internal.TlsUtil\n\nclass DevServer {\n  val handshakeCertificates = TlsUtil.localhost()\n\n  val server =\n    MockWebServer().apply {\n      useHttps(handshakeCertificates.sslSocketFactory(), false)\n\n      enqueue(\n        MockResponse()\n          .setResponseCode(HTTP_MOVED_TEMP)\n          .setHeader(\"Location\", \"https://www.google.com/robots.txt\"),\n      )\n    }\n\n  val clientCertificates =\n    HandshakeCertificates\n      .Builder()\n      .addPlatformTrustedCertificates()\n      .addInsecureHost(server.hostName)\n      .build()\n\n  val client =\n    OkHttpClient\n      .Builder()\n      .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)\n      .build()\n\n  fun run() {\n    try {\n      val request = Request(server.url(\"/\"))\n\n      client.newCall(request).execute().use { response ->\n        if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n        println(response.request.url)\n      }\n    } finally {\n      server.shutdown()\n    }\n  }\n}\n\nfun main() {\n  DevServer().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/ParseResponseWithMoshi.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport com.squareup.moshi.JsonClass\nimport com.squareup.moshi.Moshi\nimport java.io.IOException\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass ParseResponseWithMoshi {\n  private val client = OkHttpClient()\n  private val moshi = Moshi.Builder().build()\n  private val gistJsonAdapter = moshi.adapter(Gist::class.java)\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://api.github.com/gists/c2a7c39532239ff261be\")\n        .build()\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      val gist = gistJsonAdapter.fromJson(response.body!!.source())\n\n      for ((key, value) in gist!!.files!!) {\n        println(key)\n        println(value.content)\n      }\n    }\n  }\n\n  @JsonClass(generateAdapter = true)\n  data class Gist(\n    var files: Map<String, GistFile>?,\n  )\n\n  @JsonClass(generateAdapter = true)\n  data class GistFile(\n    var content: String?,\n  )\n}\n\nfun main() {\n  ParseResponseWithMoshi().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/PerCallSettings.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport java.util.concurrent.TimeUnit\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass PerCallSettings {\n  private val client = OkHttpClient()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"http://httpbin.org/delay/1\") // This URL is served with a 1 second delay.\n        .build()\n\n    // Copy to customize OkHttp for this request.\n    val client1 =\n      client\n        .newBuilder()\n        .readTimeout(500, TimeUnit.MILLISECONDS)\n        .build()\n    try {\n      client1.newCall(request).execute().use { response ->\n        println(\"Response 1 succeeded: $response\")\n      }\n    } catch (e: IOException) {\n      println(\"Response 1 failed: $e\")\n    }\n\n    // Copy to customize OkHttp for this request.\n    val client2 =\n      client\n        .newBuilder()\n        .readTimeout(3000, TimeUnit.MILLISECONDS)\n        .build()\n    try {\n      client2.newCall(request).execute().use { response ->\n        println(\"Response 2 succeeded: $response\")\n      }\n    } catch (e: IOException) {\n      println(\"Response 2 failed: $e\")\n    }\n  }\n}\n\nfun main() {\n  PerCallSettings().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/PostFile.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.File\nimport java.io.IOException\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.asRequestBody\n\nclass PostFile {\n  private val client = OkHttpClient()\n\n  fun run() {\n    val file = File(\"README.md\")\n\n    val request =\n      Request(\n        url = \"https://api.github.com/markdown/raw\".toHttpUrl(),\n        body = file.asRequestBody(MEDIA_TYPE_MARKDOWN),\n      )\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      println(response.body.string())\n    }\n  }\n\n  companion object {\n    val MEDIA_TYPE_MARKDOWN = \"text/x-markdown; charset=utf-8\".toMediaType()\n  }\n}\n\nfun main() {\n  PostFile().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/PostForm.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.FormBody\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass PostForm {\n  private val client = OkHttpClient()\n\n  fun run() {\n    val formBody =\n      FormBody\n        .Builder()\n        .add(\"search\", \"Jurassic Park\")\n        .build()\n    val request =\n      Request(\n        url = \"https://en.wikipedia.org/w/index.php\".toHttpUrl(),\n        body = formBody,\n      )\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      println(response.body.string())\n    }\n  }\n}\n\nfun main() {\n  PostForm().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/PostMultipart.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.File\nimport java.io.IOException\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.MultipartBody\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.asRequestBody\n\nclass PostMultipart {\n  private val client = OkHttpClient()\n\n  fun run() {\n    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image\n    val requestBody =\n      MultipartBody\n        .Builder()\n        .setType(MultipartBody.FORM)\n        .addFormDataPart(\"title\", \"Square Logo\")\n        .addFormDataPart(\n          \"image\",\n          \"logo-square.png\",\n          File(\"docs/images/logo-square.png\").asRequestBody(MEDIA_TYPE_PNG),\n        ).build()\n\n    val request =\n      Request\n        .Builder()\n        .header(\"Authorization\", \"Client-ID $IMGUR_CLIENT_ID\")\n        .url(\"https://api.imgur.com/3/image\")\n        .post(requestBody)\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      println(response.body.string())\n    }\n  }\n\n  companion object {\n    /**\n     * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running\n     * these examples, please request your own client ID! https://api.imgur.com/oauth2\n     */\n    private const val IMGUR_CLIENT_ID = \"9199fdef135c122\"\n    private val MEDIA_TYPE_PNG = \"image/png\".toMediaType()\n  }\n}\n\nfun main() {\n  PostMultipart().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/PostPath.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.asRequestBody\nimport okio.Path.Companion.toPath\nimport okio.buffer\nimport okio.fakefilesystem.FakeFileSystem\n\nclass PostPath {\n  private val client = OkHttpClient()\n  private val fileSystem = FakeFileSystem()\n  val path = \"test.json\".toPath()\n\n  fun run() {\n    fileSystem.write(path) {\n      writeUtf8(\"{}\")\n    }\n\n    val request =\n      Request\n        .Builder()\n        .url(\"https://httpbin.org/anything\")\n        .put(path.asRequestBody(fileSystem, MEDIA_TYPE_JSON))\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      fileSystem.sink(path).use {\n        response.body.source().readAll(it)\n      }\n\n      println(fileSystem.source(path).buffer().readUtf8())\n    }\n  }\n\n  companion object {\n    val MEDIA_TYPE_JSON = \"application/json\".toMediaType()\n  }\n}\n\nfun main() {\n  PostPath().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/PostStreaming.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody\nimport okio.BufferedSink\n\nclass PostStreaming {\n  private val client = OkHttpClient()\n\n  fun run() {\n    val requestBody =\n      object : RequestBody() {\n        override fun contentType() = MEDIA_TYPE_MARKDOWN\n\n        override fun writeTo(sink: BufferedSink) {\n          sink.writeUtf8(\"Numbers\\n\")\n          sink.writeUtf8(\"-------\\n\")\n          for (i in 2..997) {\n            sink.writeUtf8(String.format(\" * $i = ${factor(i)}\\n\"))\n          }\n        }\n\n        private fun factor(n: Int): String {\n          for (i in 2 until n) {\n            val x = n / i\n            if (x * i == n) return \"${factor(x)} × $i\"\n          }\n          return n.toString()\n        }\n      }\n\n    val request =\n      Request(\n        url = \"https://api.github.com/markdown/raw\".toHttpUrl(),\n        body = requestBody,\n      )\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      println(response.body.string())\n    }\n  }\n\n  companion object {\n    val MEDIA_TYPE_MARKDOWN = \"text/x-markdown; charset=utf-8\".toMediaType()\n  }\n}\n\nfun main() {\n  PostStreaming().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/PostString.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\n\nclass PostString {\n  private val client = OkHttpClient()\n\n  fun run() {\n    val postBody =\n      \"\"\"\n      |Releases\n      |--------\n      |\n      | * _1.0_ May 6, 2013\n      | * _1.1_ June 15, 2013\n      | * _1.2_ August 11, 2013\n      |\n      \"\"\".trimMargin()\n\n    val request =\n      Request(\n        url = \"https://api.github.com/markdown/raw\".toHttpUrl(),\n        body = postBody.toRequestBody(MEDIA_TYPE_MARKDOWN),\n      )\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      println(response.body.string())\n    }\n  }\n\n  companion object {\n    val MEDIA_TYPE_MARKDOWN = \"text/x-markdown; charset=utf-8\".toMediaType()\n  }\n}\n\nfun main() {\n  PostString().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/SynchronousGet.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\n\nclass SynchronousGet {\n  private val client = OkHttpClient()\n\n  fun run() {\n    val request =\n      Request\n        .Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      for ((name, value) in response.headers) {\n        println(\"$name: $value\")\n      }\n\n      println(response.body.string())\n    }\n  }\n}\n\nfun main() {\n  SynchronousGet().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/UploadProgress.kt",
    "content": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.recipes.kt\n\nimport java.io.File\nimport java.io.IOException\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody\nimport okhttp3.RequestBody.Companion.asRequestBody\nimport okio.Buffer\nimport okio.BufferedSink\nimport okio.ForwardingSink\nimport okio.buffer\n\nclass UploadProgress {\n  companion object {\n    private const val IMGUR_CLIENT_ID = \"9199fdef135c122\"\n    private val MEDIA_TYPE_PNG = \"image/png\".toMediaType()\n  }\n\n  private val client = OkHttpClient()\n\n  @Throws(Exception::class)\n  fun run() {\n    val progressListener =\n      object : ProgressListener {\n        private var firstUpdate = true\n\n        override fun update(\n          bytesWritten: Long,\n          contentLength: Long,\n          done: Boolean,\n        ) {\n          if (done) {\n            println(\"completed\")\n          } else {\n            if (firstUpdate) {\n              firstUpdate = false\n              if (contentLength == -1L) {\n                println(\"content-length: unknown\")\n              } else {\n                println(\"content-length: $contentLength\")\n              }\n            }\n            println(bytesWritten)\n            if (contentLength != -1L) {\n              println(\"${100 * bytesWritten / contentLength}% done\")\n            }\n          }\n        }\n      }\n\n    val file = File(\"docs/images/logo-square.png\")\n    val requestBody: RequestBody = file.asRequestBody(MEDIA_TYPE_PNG)\n\n    val request =\n      Request\n        .Builder()\n        .header(\"Authorization\", \"Client-ID $IMGUR_CLIENT_ID\")\n        .url(\"https://api.imgur.com/3/image\")\n        .post(ProgressRequestBody(requestBody, progressListener))\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n      println(response.body.string())\n    }\n  }\n\n  private class ProgressRequestBody(\n    private val delegate: RequestBody,\n    private val progressListener: ProgressListener,\n  ) : RequestBody() {\n    override fun contentType() = delegate.contentType()\n\n    @Throws(IOException::class)\n    override fun contentLength(): Long = delegate.contentLength()\n\n    @Throws(IOException::class)\n    override fun writeTo(sink: BufferedSink) {\n      val forwardingSink =\n        object : ForwardingSink(sink) {\n          private var totalBytesWritten: Long = 0\n          private var completed = false\n\n          override fun write(\n            source: Buffer,\n            byteCount: Long,\n          ) {\n            super.write(source, byteCount)\n            totalBytesWritten += byteCount\n            progressListener.update(totalBytesWritten, contentLength(), completed)\n          }\n\n          override fun close() {\n            super.close()\n            if (!completed) {\n              completed = true\n              progressListener.update(totalBytesWritten, contentLength(), completed)\n            }\n          }\n        }\n\n      val bufferedSink = forwardingSink.buffer()\n      delegate.writeTo(bufferedSink)\n      bufferedSink.flush()\n    }\n  }\n\n  fun interface ProgressListener {\n    fun update(\n      bytesWritten: Long,\n      contentLength: Long,\n      done: Boolean,\n    )\n  }\n}\n\nfun main() {\n  UploadProgress().run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/WiresharkExample.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"Since15\")\n\npackage okhttp3.recipes.kt\n\nimport java.io.File\nimport java.io.IOException\nimport java.lang.ProcessBuilder.Redirect\nimport java.util.logging.Handler\nimport java.util.logging.Level\nimport java.util.logging.LogRecord\nimport java.util.logging.Logger\nimport javax.crypto.SecretKey\nimport javax.net.ssl.SSLSession\nimport javax.net.ssl.SSLSocket\nimport okhttp3.Call\nimport okhttp3.Connection\nimport okhttp3.ConnectionSpec\nimport okhttp3.EventListener\nimport okhttp3.Handshake\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.TlsVersion\nimport okhttp3.TlsVersion.TLS_1_2\nimport okhttp3.TlsVersion.TLS_1_3\nimport okhttp3.internal.SuppressSignatureCheck\nimport okhttp3.recipes.kt.WireSharkListenerFactory.WireSharkKeyLoggerListener.Launch\nimport okhttp3.recipes.kt.WireSharkListenerFactory.WireSharkKeyLoggerListener.Launch.CommandLine\nimport okhttp3.recipes.kt.WireSharkListenerFactory.WireSharkKeyLoggerListener.Launch.Gui\nimport okio.ByteString.Companion.toByteString\n\n/**\n * Logs SSL keys to a log file, allowing Wireshark to decode traffic and be examined with http2\n * filter. The approach is to hook into JSSE log events for the messages between client and server\n * during handshake, and then take the agreed masterSecret from private fields of the session.\n *\n * Copy WireSharkKeyLoggerListener to your test code to use in development.\n *\n * This logs TLSv1.2 on a JVM (OpenJDK 11+) without any additional code.  For TLSv1.3\n * an existing external tool is required.\n *\n * See https://stackoverflow.com/questions/61929216/how-to-log-tlsv1-3-keys-in-jsse-for-wireshark-to-decode-traffic\n *\n * Steps to run in your own code\n *\n * 1. In your main method `WireSharkListenerFactory.register()`\n * 2. Create Listener factory `val eventListenerFactory = WireSharkListenerFactory(\nlogFile = File(\"/tmp/key.log\"), tlsVersions = tlsVersions, launch = launch)`\n * 3. Register with `client.eventListenerFactory(eventListenerFactory)`\n * 4. Launch wireshark if not done externally `val process = eventListenerFactory.launchWireShark()`\n */\n@SuppressSignatureCheck\nclass WireSharkListenerFactory(\n  private val logFile: File,\n  private val tlsVersions: List<TlsVersion>,\n  private val launch: Launch? = null,\n) : EventListener.Factory {\n  override fun create(call: Call): EventListener = WireSharkKeyLoggerListener(logFile, launch == null)\n\n  fun launchWireShark(): Process? {\n    when (launch) {\n      null -> {\n        if (tlsVersions.contains(TLS_1_2)) {\n          println(\"TLSv1.2 traffic will be logged automatically and available via wireshark\")\n        }\n\n        if (tlsVersions.contains(TLS_1_3)) {\n          println(\"TLSv1.3 requires an external command run before first traffic is sent\")\n          println(\"Follow instructions at https://github.com/neykov/extract-tls-secrets for TLSv1.3\")\n          println(\"Pid: ${ProcessHandle.current().pid()}\")\n\n          Thread.sleep(10000)\n        }\n      }\n\n      CommandLine -> {\n        return ProcessBuilder(\n          \"tshark\",\n          \"-l\",\n          \"-V\",\n          \"-o\",\n          \"tls.keylog_file:$logFile\",\n          \"-Y\",\n          \"http2\",\n          \"-O\",\n          \"http2,tls\",\n        ).redirectInput(File(\"/dev/null\"))\n          .redirectOutput(Redirect.INHERIT)\n          .redirectError(Redirect.INHERIT)\n          .start()\n      }\n\n      Gui -> {\n        return ProcessBuilder(\n          \"nohup\",\n          \"wireshark\",\n          \"-o\",\n          \"tls.keylog_file:$logFile\",\n          \"-S\",\n          \"-l\",\n          \"-Y\",\n          \"http2\",\n          \"-k\",\n        ).redirectInput(File(\"/dev/null\"))\n          .redirectOutput(File(\"/dev/null\"))\n          .redirectError(Redirect.INHERIT)\n          .start()\n          .also {\n            // Give it time to start collecting\n            Thread.sleep(2000)\n          }\n      }\n    }\n\n    return null\n  }\n\n  class WireSharkKeyLoggerListener(\n    private val logFile: File,\n    private val verbose: Boolean = false,\n  ) : EventListener() {\n    var random: String? = null\n    lateinit var currentThread: Thread\n\n    private val loggerHandler =\n      object : Handler() {\n        override fun publish(record: LogRecord) {\n          // Try to avoid multi threading issues with concurrent requests\n          if (Thread.currentThread() != currentThread) {\n            return\n          }\n\n          // https://timothybasanov.com/2016/05/26/java-pre-master-secret.html\n          // https://security.stackexchange.com/questions/35639/decrypting-tls-in-wireshark-when-using-dhe-rsa-ciphersuites\n          // https://stackoverflow.com/questions/36240279/how-do-i-extract-the-pre-master-secret-using-an-openssl-based-client\n\n          // TLSv1.2 Events\n          // Produced ClientHello handshake message\n          // Consuming ServerHello handshake message\n          // Consuming server Certificate handshake message\n          // Consuming server CertificateStatus handshake message\n          // Found trusted certificate\n          // Consuming ECDH ServerKeyExchange handshake message\n          // Consuming ServerHelloDone handshake message\n          // Produced ECDHE ClientKeyExchange handshake message\n          // Produced client Finished handshake message\n          // Consuming server Finished handshake message\n          // Produced ClientHello handshake message\n          //\n          // Raw write\n          // Raw read\n          // Plaintext before ENCRYPTION\n          // Plaintext after DECRYPTION\n          val message = record.message\n          val parameters = record.parameters\n\n          if (parameters != null && !message.startsWith(\"Raw\") && !message.startsWith(\"Plaintext\")) {\n            if (verbose) {\n              println(record.message)\n              println(record.parameters[0])\n            }\n\n            // JSSE logs additional messages as parameters that are not referenced in the log message.\n            val parameter = parameters[0] as String\n\n            if (message == \"Produced ClientHello handshake message\") {\n              random = readClientRandom(parameter)\n            }\n          }\n        }\n\n        override fun flush() {}\n\n        override fun close() {}\n      }\n\n    private fun readClientRandom(param: String): String? {\n      val matchResult = randomRegex.find(param)\n\n      return if (matchResult != null) {\n        matchResult.groupValues[1].replace(\" \", \"\")\n      } else {\n        null\n      }\n    }\n\n    override fun secureConnectStart(call: Call) {\n      // Register to capture \"Produced ClientHello handshake message\".\n      currentThread = Thread.currentThread()\n      logger.addHandler(loggerHandler)\n    }\n\n    override fun secureConnectEnd(\n      call: Call,\n      handshake: Handshake?,\n    ) {\n      logger.removeHandler(loggerHandler)\n    }\n\n    override fun callEnd(call: Call) {\n      // Cleanup log handler if failed.\n      logger.removeHandler(loggerHandler)\n    }\n\n    override fun connectionAcquired(\n      call: Call,\n      connection: Connection,\n    ) {\n      if (random != null) {\n        val sslSocket = connection.socket() as SSLSocket\n        val session = sslSocket.session\n\n        val masterSecretHex =\n          session.masterSecret\n            ?.encoded\n            ?.toByteString()\n            ?.hex()\n\n        if (masterSecretHex != null) {\n          val keyLog = \"CLIENT_RANDOM $random $masterSecretHex\"\n\n          if (verbose) {\n            println(keyLog)\n          }\n          logFile.appendText(\"$keyLog\\n\")\n        }\n      }\n\n      random = null\n    }\n\n    enum class Launch {\n      Gui,\n      CommandLine,\n    }\n  }\n\n  companion object {\n    private lateinit var logger: Logger\n\n    private val SSLSession.masterSecret: SecretKey?\n      get() =\n        javaClass\n          .getDeclaredField(\"masterSecret\")\n          .apply {\n            isAccessible = true\n          }.get(this) as? SecretKey\n\n    val randomRegex = \"\\\"random\\\"\\\\s+:\\\\s+\\\"([^\\\"]+)\\\"\".toRegex()\n\n    fun register() {\n      // Enable JUL logging for SSL events, must be activated early or via -D option.\n      System.setProperty(\"javax.net.debug\", \"\")\n      logger =\n        Logger\n          .getLogger(\"javax.net.ssl\")\n          .apply {\n            level = Level.FINEST\n            useParentHandlers = false\n          }\n    }\n  }\n}\n\n@SuppressSignatureCheck\nclass WiresharkExample(\n  tlsVersions: List<TlsVersion>,\n  private val launch: Launch? = null,\n) {\n  private val connectionSpec =\n    ConnectionSpec\n      .Builder(ConnectionSpec.RESTRICTED_TLS)\n      .tlsVersions(*tlsVersions.toTypedArray())\n      .build()\n\n  private val eventListenerFactory =\n    WireSharkListenerFactory(\n      logFile = File(\"/tmp/key.log\"),\n      tlsVersions = tlsVersions,\n      launch = launch,\n    )\n\n  val client =\n    OkHttpClient\n      .Builder()\n      .connectionSpecs(listOf(connectionSpec))\n      .eventListenerFactory(eventListenerFactory)\n      .build()\n\n  fun run() {\n    // Launch wireshark in the background\n    val process = eventListenerFactory.launchWireShark()\n\n    val fbRequest =\n      Request\n        .Builder()\n        .url(\"https://graph.facebook.com/robots.txt?s=fb\")\n        .build()\n    val twitterRequest =\n      Request\n        .Builder()\n        .url(\"https://api.twitter.com/robots.txt?s=tw\")\n        .build()\n    val googleRequest =\n      Request\n        .Builder()\n        .url(\"https://www.google.com/robots.txt?s=g\")\n        .build()\n\n    try {\n      for (i in 1..2) {\n        // Space out traffic to make it easier to demarcate.\n        sendTestRequest(fbRequest)\n        Thread.sleep(1000)\n        sendTestRequest(twitterRequest)\n        Thread.sleep(1000)\n        sendTestRequest(googleRequest)\n        Thread.sleep(2000)\n      }\n    } finally {\n      client.connectionPool.evictAll()\n      client.dispatcher.executorService.shutdownNow()\n\n      if (launch == CommandLine) {\n        process?.destroyForcibly()\n      }\n    }\n  }\n\n  private fun sendTestRequest(request: Request) {\n    try {\n      if (this.launch != CommandLine) {\n        println(request.url)\n      }\n\n      client\n        .newCall(request)\n        .execute()\n        .use {\n          val firstLine =\n            it.body\n              .string()\n              .lines()\n              .first()\n          if (this.launch != CommandLine) {\n            println(\"${it.code} ${it.request.url.host} $firstLine\")\n          }\n          Unit\n        }\n    } catch (e: IOException) {\n      System.err.println(e)\n    }\n  }\n}\n\nfun main() {\n  // Call this before anything else initialises the JSSE stack.\n  WireSharkListenerFactory.register()\n\n  val example = WiresharkExample(tlsVersions = listOf(TLS_1_2), launch = CommandLine)\n  example.run()\n}\n"
  },
  {
    "path": "samples/guide/src/main/java/okhttp3/recipes/kt/YubikeyClientAuth.kt",
    "content": "/*\n * Copyright (C) 2020 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"Since15\")\n\npackage okhttp3.recipes.kt\n\nimport java.io.IOException\nimport java.security.KeyStore\nimport java.security.SecureRandom\nimport java.security.Security\nimport javax.net.ssl.KeyManagerFactory\nimport javax.net.ssl.KeyStoreBuilderParameters\nimport javax.net.ssl.SSLContext\nimport javax.net.ssl.X509ExtendedKeyManager\nimport javax.security.auth.callback.Callback\nimport javax.security.auth.callback.CallbackHandler\nimport javax.security.auth.callback.PasswordCallback\nimport javax.security.auth.callback.UnsupportedCallbackException\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.internal.SuppressSignatureCheck\nimport okhttp3.internal.platform.Platform\n\n/**\n * Example of using a hardware key to perform client auth.\n * Prefer recent JDK builds, and results are temperamental to slight environment changes.\n * Different instructions and configuration may be required for other hardware devices.\n *\n * Using a yubikey device as a SSL key store.\n * https://lauri.võsandi.com/2017/03/yubikey-for-ssh-auth.html\n *\n * Using PKCS11 support in the JDK.\n * https://tersesystems.com/blog/2018/09/08/keymanagers-and-keystores/\n *\n * Install OpenSC separately. On a mac `brew cast install opensc`.\n */\n@SuppressSignatureCheck\nclass YubikeyClientAuth {\n  fun run() {\n    // The typical PKCS11 slot, may vary with different hardware.\n    val slot = 0\n\n    val config = \"--name=OpenSC\\nlibrary=/Library/OpenSC/lib/opensc-pkcs11.so\\nslot=$slot\\n\"\n\n    // May fail with ProviderException with root cause like\n    // sun.security.pkcs11.wrapper.PKCS11Exception: CKR_SLOT_ID_INVALID\n    val pkcs11 = Security.getProvider(\"SunPKCS11\").configure(config)\n    Security.addProvider(pkcs11)\n\n    val callbackHandler = ConsoleCallbackHandler\n\n    val builderList: List<KeyStore.Builder> =\n      listOf(\n        KeyStore.Builder.newInstance(\"PKCS11\", null, KeyStore.CallbackHandlerProtection(callbackHandler)),\n        // Example if you want to combine multiple keystore types\n        // KeyStore.Builder.newInstance(\"PKCS12\", null, File(\"keystore.p12\"), PasswordProtection(\"rosebud\".toCharArray()))\n      )\n\n    val keyManagerFactory = KeyManagerFactory.getInstance(\"NewSunX509\")\n    keyManagerFactory.init(KeyStoreBuilderParameters(builderList))\n    val keyManager = keyManagerFactory.keyManagers[0] as X509ExtendedKeyManager\n\n    val trustManager = Platform.get().platformTrustManager()\n\n    val sslContext = SSLContext.getInstance(\"TLS\")\n    sslContext.init(arrayOf(keyManager), arrayOf(trustManager), SecureRandom())\n\n    val client =\n      OkHttpClient\n        .Builder()\n        .sslSocketFactory(sslContext.socketFactory, trustManager)\n        .build()\n\n    // An example test URL that returns client certificate details.\n    val request =\n      Request\n        .Builder()\n        .url(\"https://prod.idrix.eu/secure/\")\n        .build()\n\n    client.newCall(request).execute().use { response ->\n      if (!response.isSuccessful) throw IOException(\"Unexpected code $response\")\n\n      println(response.body.string())\n    }\n  }\n}\n\nobject ConsoleCallbackHandler : CallbackHandler {\n  override fun handle(callbacks: Array<Callback>) {\n    for (callback in callbacks) {\n      if (callback is PasswordCallback) {\n        val console = System.console()\n\n        if (console != null) {\n          callback.password = console.readPassword(callback.prompt)\n        } else {\n          System.err.println(callback.prompt)\n          callback.password =\n            System.`in`\n              .bufferedReader()\n              .readLine()\n              .toCharArray()\n        }\n      } else {\n        throw UnsupportedCallbackException(callback)\n      }\n    }\n  }\n}\n\nfun main() {\n  YubikeyClientAuth().run()\n}\n"
  },
  {
    "path": "samples/guide/src/test/kotlin/okhttp3/AllMainsTest.kt",
    "content": "/*\n * Copyright (C) 2019 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3\n\nimport java.io.File\nimport java.lang.reflect.InvocationTargetException\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ArgumentsSource\n\nprivate val prefix = if (File(\"samples\").exists()) \"\" else \"../../\"\n\nprivate fun mainFiles(): List<File> {\n  val directories =\n    listOf(\n      \"$prefix/samples/guide/src/main/java/okhttp3/guide\",\n      \"$prefix/samples/guide/src/main/java/okhttp3/recipes\",\n      \"$prefix/samples/guide/src/main/java/okhttp3/recipes/kt\",\n    ).map { File(it) }\n\n  return directories.flatMap {\n    it\n      .listFiles()\n      .orEmpty()\n      .filter { f -> f.isFile }\n      .toList()\n  }\n}\n\ninternal class MainTestProvider : SimpleProvider() {\n  override fun arguments(): List<Any> {\n    val mainFiles = mainFiles()\n    return mainFiles\n      .map {\n        val suffix = it.path.replace(\"${prefix}samples/guide/src/main/java/\", \"\")\n        suffix\n          .replace(\"(.*)\\\\.java\".toRegex()) { mr ->\n            mr.groupValues[1].replace('/', '.')\n          }.replace(\"(.*)\\\\.kt\".toRegex()) { mr ->\n            mr.groupValues[1].replace('/', '.') + \"Kt\"\n          }\n      }.sorted()\n  }\n}\n\n@Disabled(\"Don't run by default\")\n@Tag(\"Slow\")\nclass AllMainsTest {\n  @ParameterizedTest\n  @ArgumentsSource(MainTestProvider::class)\n  fun runMain(className: String) {\n    val mainMethod =\n      Class\n        .forName(className)\n        .methods\n        .find { it.name == \"main\" }\n    try {\n      if (mainMethod != null) {\n        if (mainMethod.parameters.isEmpty()) {\n          mainMethod.invoke(null)\n        } else {\n          mainMethod.invoke(null, arrayOf<String>())\n        }\n      } else {\n        System.err.println(\"No main for $className\")\n      }\n    } catch (ite: InvocationTargetException) {\n      if (!expectedFailure(className, ite.cause!!)) {\n        throw ite.cause!!\n      }\n    }\n  }\n\n  @Suppress(\"UNUSED_PARAMETER\")\n  private fun expectedFailure(\n    className: String,\n    cause: Throwable,\n  ): Boolean =\n    when (className) {\n      \"okhttp3.recipes.CheckHandshake\" -> true\n\n      // by design\n      \"okhttp3.recipes.RequestBodyCompression\" -> true\n\n      // expired token\n      else -> false\n    }\n}\n"
  },
  {
    "path": "samples/simple-client/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\ndependencies {\n  implementation(projects.okhttp)\n  implementation(libs.square.moshi)\n}\n"
  },
  {
    "path": "samples/simple-client/src/main/java/okhttp3/sample/OkHttpContributors.java",
    "content": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sample;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.Moshi;\nimport com.squareup.moshi.Types;\nimport java.util.Collections;\nimport java.util.List;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\n\npublic class OkHttpContributors {\n  private static final String ENDPOINT = \"https://api.github.com/repos/square/okhttp/contributors\";\n  private static final Moshi MOSHI = new Moshi.Builder().build();\n  private static final JsonAdapter<List<Contributor>> CONTRIBUTORS_JSON_ADAPTER = MOSHI.adapter(\n      Types.newParameterizedType(List.class, Contributor.class));\n\n  static class Contributor {\n    String login;\n    int contributions;\n  }\n\n  public static void main(String... args) throws Exception {\n    OkHttpClient client = new OkHttpClient();\n\n    // Create request for remote resource.\n    Request request = new Request.Builder()\n        .url(ENDPOINT)\n        .build();\n\n    // Execute the request and retrieve the response.\n    try (Response response = client.newCall(request).execute()) {\n      // Deserialize HTTP response to concrete type.\n      ResponseBody body = response.body();\n      List<Contributor> contributors = CONTRIBUTORS_JSON_ADAPTER.fromJson(body.source());\n\n      // Sort list by the most contributions.\n      Collections.sort(contributors, (c1, c2) -> c2.contributions - c1.contributions);\n\n      // Output list of contributors.\n      for (Contributor contributor : contributors) {\n        System.out.println(contributor.login + \": \" + contributor.contributions);\n      }\n    }\n  }\n\n  private OkHttpContributors() {\n    // No instances.\n  }\n}\n"
  },
  {
    "path": "samples/slack/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\ndependencies {\n  implementation(projects.okhttp)\n  implementation(projects.mockwebserver)\n  implementation(libs.square.moshi)\n}\n"
  },
  {
    "path": "samples/slack/src/main/java/okhttp3/slack/OAuthSession.java",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.slack;\n\n/** Authorization for an application to make Slack API calls on behalf of a user. */\n@SuppressWarnings(\"checkstyle:membername\")\npublic final class OAuthSession {\n  public final boolean ok;\n  public final String access_token;\n  public final String scope;\n  public final String user_id;\n  public final String team_name;\n  public final String team_id;\n\n  public OAuthSession(\n      boolean ok, String accessToken, String scope, String userId, String teamName, String teamId) {\n    this.ok = ok;\n    this.access_token = accessToken;\n    this.scope = scope;\n    this.user_id = userId;\n    this.team_name = teamName;\n    this.team_id = teamId;\n  }\n\n  @Override public String toString() {\n    return String.format(\"(ok=%s, access_token=%s, scope=%s, user_id=%s, team_name=%s, team_id=%s)\",\n        ok, access_token, scope, user_id, team_name, team_id);\n  }\n}\n"
  },
  {
    "path": "samples/slack/src/main/java/okhttp3/slack/OAuthSessionFactory.java",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.slack;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.security.SecureRandom;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport okhttp3.HttpUrl;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport okio.ByteString;\n\n/**\n * Runs a MockWebServer on localhost and uses it as the backend to receive an OAuth session.\n *\n * <p>Clients should call {@link #start}, {@link #newAuthorizeUrl} and {@link #close} in that order.\n * Clients may request multiple sessions.\n */\npublic final class OAuthSessionFactory extends Dispatcher implements Closeable {\n  private final SecureRandom secureRandom = new SecureRandom();\n\n  private final SlackApi slackApi;\n  private MockWebServer mockWebServer;\n\n  /** Guarded by this. */\n  private final Map<ByteString, Listener> listeners = new LinkedHashMap<>();\n\n  public OAuthSessionFactory(SlackApi slackApi) {\n    this.slackApi = slackApi;\n  }\n\n  public void start() throws Exception {\n    if (mockWebServer != null) throw new IllegalStateException();\n\n    mockWebServer = new MockWebServer();\n    mockWebServer.setDispatcher(this);\n    mockWebServer.start(slackApi.port);\n  }\n\n  public HttpUrl newAuthorizeUrl(String scopes, String team, Listener listener) {\n    if (mockWebServer == null) throw new IllegalStateException();\n\n    ByteString state = randomToken();\n    synchronized (this) {\n      listeners.put(state, listener);\n    }\n\n    return slackApi.authorizeUrl(scopes, redirectUrl(), state, team);\n  }\n\n  private ByteString randomToken() {\n    byte[] bytes = new byte[16];\n    secureRandom.nextBytes(bytes);\n    return ByteString.of(bytes);\n  }\n\n  private HttpUrl redirectUrl() {\n    return mockWebServer.url(\"/oauth/\");\n  }\n\n  /** When the browser hits the redirect URL, use the provided code to ask Slack for a session. */\n  @Override public MockResponse dispatch(RecordedRequest request) {\n    HttpUrl requestUrl = mockWebServer.url(request.getPath());\n    String code = requestUrl.queryParameter(\"code\");\n    String stateString = requestUrl.queryParameter(\"state\");\n    ByteString state = stateString != null ? ByteString.decodeBase64(stateString) : null;\n\n    Listener listener;\n    synchronized (this) {\n      listener = listeners.get(state);\n    }\n\n    if (code == null || listener == null) {\n      return new MockResponse()\n          .setResponseCode(404)\n          .setBody(\"unexpected request\");\n    }\n\n    try {\n      OAuthSession session = slackApi.exchangeCode(code, redirectUrl());\n      listener.sessionGranted(session);\n    } catch (IOException e) {\n      return new MockResponse()\n          .setResponseCode(400)\n          .setBody(\"code exchange failed: \" + e.getMessage());\n    }\n\n    synchronized (this) {\n      listeners.remove(state);\n    }\n\n    // Success!\n    return new MockResponse()\n        .setResponseCode(302)\n        .addHeader(\"Location\", \"https://twitter.com/CuteEmergency/status/789457462864863232\");\n  }\n\n  public interface Listener {\n    void sessionGranted(OAuthSession session);\n  }\n\n  @Override public void close() {\n    if (mockWebServer == null) throw new IllegalStateException();\n    try {\n      mockWebServer.close();\n    } catch (IOException ignored) {\n    }\n  }\n}\n"
  },
  {
    "path": "samples/slack/src/main/java/okhttp3/slack/RtmSession.java",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.slack;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport okhttp3.WebSocket;\nimport okhttp3.Response;\nimport okhttp3.WebSocketListener;\n\n/** A realtime messaging session. */\npublic final class RtmSession extends WebSocketListener implements Closeable {\n  private final SlackApi slackApi;\n\n  /** Guarded by this. */\n  private WebSocket webSocket;\n\n  public RtmSession(SlackApi slackApi) {\n    this.slackApi = slackApi;\n  }\n\n  public void open(String accessToken) throws IOException {\n    if (webSocket != null) throw new IllegalStateException();\n\n    RtmStartResponse rtmStartResponse = slackApi.rtmStart(accessToken);\n    webSocket = slackApi.rtm(rtmStartResponse.url, this);\n  }\n\n  // TODO(jwilson): can I read the response body? Do I have to?\n  //                the body from slack is a 0-byte-buffer\n  @Override public synchronized void onOpen(WebSocket webSocket, Response response) {\n    System.out.println(\"onOpen: \" + response);\n  }\n\n  // TOOD(jwilson): decode incoming messages and dispatch them somewhere.\n  @Override public void onMessage(WebSocket webSocket, String text) {\n    System.out.println(\"onMessage: \" + text);\n  }\n\n  @Override public void onClosing(WebSocket webSocket, int code, String reason) {\n    webSocket.close(1000, null);\n    System.out.println(\"onClose (\" + code + \"): \" + reason);\n  }\n\n  @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) {\n    // TODO(jwilson): can I read the response body? Do I have to?\n    System.out.println(\"onFailure \" + response);\n  }\n\n  @Override public void close() throws IOException {\n    if (webSocket == null) return;\n\n    WebSocket webSocket;\n    synchronized (this) {\n      webSocket = this.webSocket;\n    }\n\n    if (webSocket != null) {\n      webSocket.close(1000, \"bye\");\n    }\n  }\n}\n"
  },
  {
    "path": "samples/slack/src/main/java/okhttp3/slack/RtmStartResponse.java",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.slack;\n\nimport java.util.List;\nimport okhttp3.HttpUrl;\n\n/** See https://api.slack.com/methods/rtm.start. */\npublic final class RtmStartResponse {\n  HttpUrl url;\n  Object self;\n  Object team;\n  List<Object> users;\n  List<Object> channels;\n  List<Object> groups;\n  List<Object> mpims;\n  List<Object> ims;\n  List<Object> bots;\n}\n"
  },
  {
    "path": "samples/slack/src/main/java/okhttp3/slack/SlackApi.java",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.slack;\n\nimport com.squareup.moshi.FromJson;\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.Moshi;\nimport com.squareup.moshi.ToJson;\nimport java.io.IOException;\nimport okhttp3.Call;\nimport okhttp3.HttpUrl;\nimport okhttp3.WebSocket;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.WebSocketListener;\nimport okio.ByteString;\n\n/**\n * API access to the <a href=\"https://api.slack.com/apps\">Slack API</a> as an application. One\n * instance of this class may operate without a user, or on behalf of many users. Use the Slack API\n * dashboard to create a client ID and secret for this application.\n *\n * <p>You must configure your Slack API OAuth and Permissions page with a localhost URL like {@code\n * http://localhost:53203/oauth/}, passing the same port to this class’ constructor.\n */\npublic final class SlackApi {\n  private final HttpUrl baseUrl = HttpUrl.get(\"https://slack.com/api/\");\n  private final OkHttpClient httpClient;\n  private final Moshi moshi;\n\n  public final String clientId;\n  public final String clientSecret;\n  public final int port;\n\n  public SlackApi(String clientId, String clientSecret, int port) {\n    this.httpClient = new OkHttpClient.Builder()\n        .build();\n    this.moshi = new Moshi.Builder()\n        .add(new SlackJsonAdapters())\n        .build();\n    this.clientId = clientId;\n    this.clientSecret = clientSecret;\n    this.port = port;\n  }\n\n  /** See https://api.slack.com/docs/oauth. */\n  public HttpUrl authorizeUrl(String scopes, HttpUrl redirectUrl, ByteString state, String team) {\n    HttpUrl.Builder builder = baseUrl.newBuilder(\"/oauth/authorize\")\n        .addQueryParameter(\"client_id\", clientId)\n        .addQueryParameter(\"scope\", scopes)\n        .addQueryParameter(\"redirect_uri\", redirectUrl.toString())\n        .addQueryParameter(\"state\", state.base64());\n\n    if (team != null) {\n      builder.addQueryParameter(\"team\", team);\n    }\n\n    return builder.build();\n  }\n\n  /** See https://api.slack.com/methods/oauth.access. */\n  public OAuthSession exchangeCode(String code, HttpUrl redirectUrl) throws IOException {\n    HttpUrl url = baseUrl.newBuilder(\"oauth.access\")\n        .addQueryParameter(\"client_id\", clientId)\n        .addQueryParameter(\"client_secret\", clientSecret)\n        .addQueryParameter(\"code\", code)\n        .addQueryParameter(\"redirect_uri\", redirectUrl.toString())\n        .build();\n    Request request = new Request.Builder()\n        .url(url)\n        .build();\n    Call call = httpClient.newCall(request);\n    try (Response response = call.execute()) {\n      JsonAdapter<OAuthSession> jsonAdapter = moshi.adapter(OAuthSession.class);\n      return jsonAdapter.fromJson(response.body().source());\n    }\n  }\n\n  /** See https://api.slack.com/methods/rtm.start. */\n  public RtmStartResponse rtmStart(String accessToken) throws IOException {\n    HttpUrl url = baseUrl.newBuilder(\"rtm.start\")\n        .addQueryParameter(\"token\", accessToken)\n        .build();\n    Request request = new Request.Builder()\n        .url(url)\n        .build();\n    Call call = httpClient.newCall(request);\n    try (Response response = call.execute()) {\n      JsonAdapter<RtmStartResponse> jsonAdapter = moshi.adapter(RtmStartResponse.class);\n      return jsonAdapter.fromJson(response.body().source());\n    }\n  }\n\n  /** See https://api.slack.com/rtm. */\n  public WebSocket rtm(HttpUrl url, WebSocketListener listener) {\n    return httpClient.newWebSocket(new Request.Builder()\n        .url(url)\n        .build(), listener);\n  }\n\n  static final class SlackJsonAdapters {\n    @ToJson String urlToJson(HttpUrl httpUrl) {\n      return httpUrl.toString();\n    }\n\n    @FromJson HttpUrl urlFromJson(String urlString) {\n      if (urlString.startsWith(\"wss:\")) urlString = \"https:\" + urlString.substring(4);\n      if (urlString.startsWith(\"ws:\")) urlString = \"http:\" + urlString.substring(3);\n      return HttpUrl.get(urlString);\n    }\n  }\n}\n"
  },
  {
    "path": "samples/slack/src/main/java/okhttp3/slack/SlackClient.java",
    "content": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.slack;\n\nimport java.io.IOException;\nimport java.io.InterruptedIOException;\nimport okhttp3.HttpUrl;\nimport okio.Timeout;\n\n/** A connection to Slack as a single user. */\npublic final class SlackClient {\n  private final SlackApi slackApi;\n  private OAuthSessionFactory sessionFactory;\n\n  /** Guarded by this. */\n  private OAuthSession session;\n\n  public SlackClient(SlackApi slackApi) {\n    this.slackApi = slackApi;\n  }\n\n  /** Shows a browser URL to authorize this app to act as this user. */\n  public void requestOauthSession(String scopes, String team) throws Exception {\n    if (sessionFactory == null) {\n      sessionFactory = new OAuthSessionFactory(slackApi);\n      sessionFactory.start();\n    }\n\n    HttpUrl authorizeUrl = sessionFactory.newAuthorizeUrl(scopes, team, session -> {\n      initOauthSession(session);\n      System.out.printf(\"session granted: %s\\n\", session);\n    });\n\n    System.out.printf(\"open this URL in a browser: %s\\n\", authorizeUrl);\n  }\n\n  /** Set the OAuth session for this client. */\n  public synchronized void initOauthSession(OAuthSession session) {\n    this.session = session;\n    this.notifyAll();\n  }\n\n  /** Waits for an OAuth session for this client to be set. */\n  public synchronized void awaitAccessToken(Timeout timeout) throws InterruptedIOException {\n    while (session == null) {\n      timeout.waitUntilNotified(this);\n    }\n  }\n\n  /** Starts a real time messaging session. */\n  public void startRtm() throws IOException {\n    String accessToken;\n    synchronized (this) {\n      accessToken = session.access_token;\n    }\n\n    RtmSession rtmSession = new RtmSession(slackApi);\n    rtmSession.open(accessToken);\n  }\n\n  public static void main(String... args) throws Exception {\n    String clientId = \"0000000000.00000000000\";\n    String clientSecret = \"00000000000000000000000000000000\";\n    int port = 53203;\n    SlackApi slackApi = new SlackApi(clientId, clientSecret, port);\n\n    SlackClient client = new SlackClient(slackApi);\n    String scopes = \"channels:history channels:read channels:write chat:write:bot chat:write:user \"\n        + \"dnd:read dnd:write emoji:read files:read files:write:user groups:history groups:read \"\n        + \"groups:write im:history im:read im:write mpim:history mpim:read mpim:write pins:read \"\n        + \"pins:write reactions:read reactions:write search:read stars:read stars:write team:read \"\n        + \"usergroups:read usergroups:write users:read users:write identify\";\n\n    if (true) {\n      client.requestOauthSession(scopes, null);\n    } else {\n      OAuthSession session = new OAuthSession(true,\n          \"xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\",\n          scopes, \"UXXXXXXXX\", \"My Slack Group\", \"TXXXXXXXX\");\n      client.initOauthSession(session);\n    }\n\n    client.awaitAccessToken(Timeout.NONE);\n    client.startRtm();\n  }\n}\n"
  },
  {
    "path": "samples/static-server/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n  id(\"com.gradleup.shadow\")\n}\n\ntasks.compileJava {\n  options.isWarnings = false\n}\n\ntasks.jar {\n  manifest {\n    attributes(\"Main-Class\" to \"okhttp3.sample.SampleServer\")\n  }\n}\n\ndependencies {\n  implementation(projects.okhttp)\n  implementation(projects.mockwebserver)\n}\n\ntasks.shadowJar {\n  mergeServiceFiles()\n}\n"
  },
  {
    "path": "samples/static-server/src/main/java/okhttp3/sample/SampleServer.java",
    "content": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.sample;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.security.GeneralSecurityException;\nimport java.security.KeyStore;\nimport java.security.SecureRandom;\nimport javax.net.ssl.KeyManagerFactory;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.TrustManagerFactory;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport okio.Buffer;\nimport okio.Okio;\n\npublic class SampleServer extends Dispatcher {\n  private final SSLContext sslContext;\n  private final String root;\n  private final int port;\n\n  public SampleServer(SSLContext sslContext, String root, int port) {\n    this.sslContext = sslContext;\n    this.root = root;\n    this.port = port;\n  }\n\n  public void run() throws IOException {\n    MockWebServer server = new MockWebServer();\n    server.useHttps(sslContext.getSocketFactory(), false);\n    server.setDispatcher(this);\n    server.start(port);\n  }\n\n  @Override public MockResponse dispatch(RecordedRequest request) {\n    String path = request.getPath();\n    try {\n      if (!path.startsWith(\"/\") || path.contains(\"..\")) throw new FileNotFoundException();\n\n      File file = new File(root + path);\n      return file.isDirectory()\n          ? directoryToResponse(path, file)\n          : fileToResponse(path, file);\n    } catch (FileNotFoundException e) {\n      return new MockResponse()\n          .setStatus(\"HTTP/1.1 404\")\n          .addHeader(\"content-type: text/plain; charset=utf-8\")\n          .setBody(\"NOT FOUND: \" + path);\n    } catch (IOException e) {\n      return new MockResponse()\n          .setStatus(\"HTTP/1.1 500\")\n          .addHeader(\"content-type: text/plain; charset=utf-8\")\n          .setBody(\"SERVER ERROR: \" + e);\n    }\n  }\n\n  private MockResponse directoryToResponse(String basePath, File directory) {\n    if (!basePath.endsWith(\"/\")) basePath += \"/\";\n\n    StringBuilder response = new StringBuilder();\n    response.append(String.format(\"<html><head><title>%s</title></head><body>\", basePath));\n    response.append(String.format(\"<h1>%s</h1>\", basePath));\n    for (String file : directory.list()) {\n      response.append(String.format(\"<div class='file'><a href='%s'>%s</a></div>\",\n          basePath + file, file));\n    }\n    response.append(\"</body></html>\");\n\n    return new MockResponse()\n        .setStatus(\"HTTP/1.1 200\")\n        .addHeader(\"content-type: text/html; charset=utf-8\")\n        .setBody(response.toString());\n  }\n\n  private MockResponse fileToResponse(String path, File file) throws IOException {\n    return new MockResponse()\n        .setStatus(\"HTTP/1.1 200\")\n        .setBody(fileToBytes(file))\n        .addHeader(\"content-type: \" + contentType(path));\n  }\n\n  private Buffer fileToBytes(File file) throws IOException {\n    Buffer result = new Buffer();\n    result.writeAll(Okio.source(file));\n    return result;\n  }\n\n  private String contentType(String path) {\n    if (path.endsWith(\".png\")) return \"image/png\";\n    if (path.endsWith(\".jpg\")) return \"image/jpeg\";\n    if (path.endsWith(\".jpeg\")) return \"image/jpeg\";\n    if (path.endsWith(\".gif\")) return \"image/gif\";\n    if (path.endsWith(\".html\")) return \"text/html; charset=utf-8\";\n    if (path.endsWith(\".txt\")) return \"text/plain; charset=utf-8\";\n    return \"application/octet-stream\";\n  }\n\n  public static void main(String[] args) throws Exception {\n    if (args.length != 4) {\n      System.out.println(\"Usage: SampleServer <keystore> <password> <root file> <port>\");\n      return;\n    }\n\n    String keystoreFile = args[0];\n    String password = args[1];\n    String root = args[2];\n    int port = Integer.parseInt(args[3]);\n\n    SSLContext sslContext = sslContext(keystoreFile, password);\n    SampleServer server = new SampleServer(sslContext, root, port);\n    server.run();\n  }\n\n  private static SSLContext sslContext(String keystoreFile, String password)\n      throws GeneralSecurityException, IOException {\n    KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());\n    try (InputStream in = new FileInputStream(keystoreFile)) {\n      keystore.load(in, password.toCharArray());\n    }\n    KeyManagerFactory keyManagerFactory =\n        KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());\n    keyManagerFactory.init(keystore, password.toCharArray());\n\n    TrustManagerFactory trustManagerFactory =\n        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n    trustManagerFactory.init(keystore);\n\n    SSLContext sslContext = SSLContext.getInstance(\"TLS\");\n    sslContext.init(\n        keyManagerFactory.getKeyManagers(),\n        trustManagerFactory.getTrustManagers(),\n        new SecureRandom());\n\n    return sslContext;\n  }\n}\n"
  },
  {
    "path": "samples/tlssurvey/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n  application\n  id(\"com.google.devtools.ksp\")\n}\n\napplication {\n  mainClass.set(\"okhttp3.survey.RunSurveyKt\")\n}\n\ndependencies {\n  implementation(projects.okhttp)\n  implementation(projects.okhttpCoroutines)\n  implementation(libs.conscrypt.openjdk)\n\n  implementation(libs.square.retrofit)\n  implementation(libs.square.retrofit.converter.moshi)\n  implementation(libs.square.moshi)\n  implementation(libs.square.moshi.kotlin)\n\n  ksp(libs.square.moshi.compiler)\n}\n\ntasks.compileJava {\n  options.isWarnings = false\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/CipherSuiteSurvey.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey\n\nimport okhttp3.survey.types.Client\nimport okhttp3.survey.types.SuiteId\n\n/**\n * Organizes information on SSL cipher suite inclusion and precedence for this spreadsheet.\n * https://docs.google.com/spreadsheets/d/1C3FdZSlCBq_-qrVwG1KDIzNIB3Hyg_rKAcgmSzOsHyQ/edit#gid=0\n */\nclass CipherSuiteSurvey(\n  val clients: List<Client>,\n  val ianaSuites: IanaSuites,\n  val orderBy: List<SuiteId>,\n) {\n  fun printGoogleSheet() {\n    print(\"name\")\n    for (client in clients) {\n      print(\"\\t\")\n      print(client.nameAndVersion)\n    }\n    println()\n    val sortedSuites =\n      ianaSuites.suites.sortedBy { ianaSuite ->\n        val index = orderBy.indexOfFirst { it.matches(ianaSuite) }\n        if (index == -1) Integer.MAX_VALUE else index\n      }\n    for (suiteId in sortedSuites) {\n      print(suiteId.name)\n      for (client in clients) {\n        print(\"\\t\")\n        val index = client.enabled.indexOfFirst { it.matches(suiteId) }\n        if (index != -1) {\n          print(index + 1)\n        }\n      }\n      println()\n    }\n  }\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/Clients.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey\n\nimport javax.net.ssl.SSLSocket\nimport javax.net.ssl.SSLSocketFactory\nimport okhttp3.ConnectionSpec\nimport okhttp3.OkHttp\nimport okhttp3.survey.types.Client\nimport okhttp3.survey.types.SuiteId\nimport okio.FileSystem\nimport okio.Path.Companion.toPath\nimport org.conscrypt.Conscrypt\n\nfun currentOkHttp(ianaSuites: IanaSuites): Client =\n  Client(\n    userAgent = \"OkHttp\",\n    version = OkHttp.VERSION,\n    enabled =\n      ConnectionSpec.MODERN_TLS.cipherSuites!!.map {\n        ianaSuites.fromJavaName(it.javaName)\n      },\n    supported =\n      ConnectionSpec.COMPATIBLE_TLS.cipherSuites!!.map {\n        ianaSuites.fromJavaName(it.javaName)\n      },\n  )\n\nfun historicOkHttp(version: String): Client {\n  val enabled =\n    FileSystem.RESOURCES.read(\"okhttp_$version.txt\".toPath()) {\n      this.readUtf8().lines().filter { it.isNotBlank() }.map {\n        SuiteId(id = null, name = it.trim())\n      }\n    }\n  return Client(\n    userAgent = \"OkHttp\",\n    version = version,\n    enabled = enabled,\n  )\n}\n\nfun currentVm(ianaSuites: IanaSuites): Client =\n  systemDefault(\n    name = System.getProperty(\"java.vm.name\"),\n    version = System.getProperty(\"java.version\"),\n    ianaSuites = ianaSuites,\n  )\n\nfun conscrypt(ianaSuites: IanaSuites): Client {\n  val version = Conscrypt.version()\n  return systemDefault(\n    name = \"Conscrypt\",\n    version = \"${version.major()}.${version.minor()}\",\n    ianaSuites = ianaSuites,\n  )\n}\n\nfun systemDefault(\n  name: String,\n  version: String,\n  ianaSuites: IanaSuites,\n): Client {\n  val socketFactory = SSLSocketFactory.getDefault() as SSLSocketFactory\n  val sslSocket = socketFactory.createSocket() as SSLSocket\n\n  return Client(\n    userAgent = name,\n    version = version,\n    enabled = sslSocket.enabledCipherSuites.map { ianaSuites.fromJavaName(it) },\n    supported = sslSocket.supportedCipherSuites.map { ianaSuites.fromJavaName(it) },\n  )\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/Iana.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey\n\nimport okhttp3.HttpUrl.Companion.toHttpUrl\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.coroutines.executeAsync\nimport okhttp3.survey.types.SuiteId\nimport okio.ByteString.Companion.decodeHex\nimport okio.IOException\n\n/** Example: \"0x00,0x08\",TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,Y,N,[RFC4346] */\nval IANA_CSV_PATTERN = \"\\\"0x(\\\\w\\\\w),0x(\\\\w\\\\w)\\\",(\\\\w+).*\".toRegex()\n\nfun parseIanaCsvRow(s: String): SuiteId? {\n  if (s.contains(\"Reserved\") || s.contains(\"Unassigned\")) return null\n  val matcher = IANA_CSV_PATTERN.matchEntire(s) ?: return null\n  val id = (matcher.groupValues[1] + matcher.groupValues[2]).decodeHex()\n  return SuiteId(id, matcher.groupValues[3])\n}\n\nclass IanaSuites(\n  val name: String,\n  val suites: List<SuiteId>,\n) {\n  fun fromJavaName(javaName: String): SuiteId =\n    suites.firstOrNull {\n      it.name == javaName || it.name == \"TLS_${javaName.drop(4)}\"\n    } ?: throw IllegalArgumentException(\"No such suite: $javaName\")\n}\n\nsuspend fun fetchIanaSuites(okHttpClient: OkHttpClient): IanaSuites {\n  val url = \"https://www.iana.org/assignments/tls-parameters/tls-parameters-4.csv\"\n\n  val call = okHttpClient.newCall(Request(url.toHttpUrl()))\n\n  val suites =\n    call.executeAsync().use {\n      if (!it.isSuccessful) {\n        throw IOException(\"Failed ${it.code} ${it.message}\")\n      }\n      it.body\n        .string()\n        .lines()\n        .mapNotNull { parseIanaCsvRow(it) }\n    }\n\n  return IanaSuites(\"current\", suites)\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/RunSurvey.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey\n\nimport java.security.Security\nimport okhttp3.Cache\nimport okhttp3.OkHttpClient\nimport okhttp3.survey.ssllabs.SslLabsClient\nimport okhttp3.survey.types.Client\nimport okhttp3.survey.types.SuiteId\nimport okio.FileSystem\nimport okio.Path.Companion.toPath\nimport org.conscrypt.Conscrypt\n\n@Suppress(\"ktlint:standard:property-naming\")\nsuspend fun main() {\n  val includeConscrypt = false\n\n  val client =\n    OkHttpClient\n      .Builder()\n      .cache(Cache(FileSystem.SYSTEM, \"build/okhttp_cache\".toPath(), 100_000_000))\n      .build()\n\n  val sslLabsClients = SslLabsClient(client).clients()\n  val ianaSuitesNew = fetchIanaSuites(client)\n\n  val android5 = sslLabsClients.first { it.userAgent == \"Android\" && it.version == \"5.0.0\" }\n  val android9 = sslLabsClients.first { it.userAgent == \"Android\" && it.version == \"9.0\" }\n  val chrome33 = sslLabsClients.first { it.userAgent == \"Chrome\" && it.version == \"33\" }\n  val chrome57 = sslLabsClients.first { it.userAgent == \"Chrome\" && it.version == \"57\" }\n  val chrome80 = sslLabsClients.first { it.userAgent == \"Chrome\" && it.version == \"80\" }\n  val firefox34 = sslLabsClients.first { it.userAgent == \"Firefox\" && it.version == \"34\" }\n  val firefox53 = sslLabsClients.first { it.userAgent == \"Firefox\" && it.version == \"53\" }\n  val firefox73 = sslLabsClients.first { it.userAgent == \"Firefox\" && it.version == \"73\" }\n  val java7 = sslLabsClients.first { it.userAgent == \"Java\" && it.version == \"7u25\" }\n  val java12 = sslLabsClients.first { it.userAgent == \"Java\" && it.version == \"12.0.1\" }\n  val safari12iOS = sslLabsClients.first { it.userAgent == \"Safari\" && it.platform == \"iOS 12.3.1\" }\n  val safari12Osx =\n    sslLabsClients.first { it.userAgent == \"Safari\" && it.platform == \"MacOS 10.14.6 Beta\" }\n\n  val okhttp = currentOkHttp(ianaSuitesNew)\n\n  val okHttp_4_10 = historicOkHttp(\"4.10\")\n  val okHttp_3_14 = historicOkHttp(\"3.14\")\n  val okHttp_3_13 = historicOkHttp(\"3.13\")\n  val okHttp_3_11 = historicOkHttp(\"3.11\")\n  val okHttp_3_9 = historicOkHttp(\"3.9\")\n\n  val currentVm = currentVm(ianaSuitesNew)\n\n  val conscrypt =\n    if (includeConscrypt) {\n      Security.addProvider(Conscrypt.newProvider())\n      conscrypt(ianaSuitesNew)\n    } else {\n      Client(\"Conscrypt\", \"Disabled\", null, listOf())\n    }\n\n  val clients =\n    listOf(\n      okhttp,\n      chrome80,\n      firefox73,\n      android9,\n      safari12iOS,\n      conscrypt,\n      currentVm,\n      okHttp_3_9,\n      okHttp_3_11,\n      okHttp_3_13,\n      okHttp_3_14,\n      okHttp_4_10,\n      android5,\n      java7,\n      java12,\n      firefox34,\n      firefox53,\n      chrome33,\n      chrome57,\n      safari12Osx,\n    )\n\n  val orderBy = okhttp.enabled + chrome80.enabled + safari12Osx.enabled + rest(clients)\n  val survey = CipherSuiteSurvey(clients = clients, ianaSuites = ianaSuitesNew, orderBy = orderBy)\n\n  survey.printGoogleSheet()\n}\n\nfun rest(clients: List<Client>): List<SuiteId> {\n  // combine all ciphers to get these near the top\n  return clients.flatMap { it.enabled }\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/ssllabs/SslLabsApi.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey.ssllabs\n\nimport retrofit2.http.GET\n\ninterface SslLabsApi {\n  @GET(\"getClients\")\n  suspend fun clients(): List<UserAgentCapabilities>\n\n  companion object {\n    const val BASE_URL = \"https://api.ssllabs.com/api/v3/\"\n  }\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/ssllabs/SslLabsClient.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey.ssllabs\n\nimport com.squareup.moshi.Moshi\nimport okhttp3.Call\nimport okhttp3.OkHttpClient\nimport okhttp3.survey.types.Client\nimport okhttp3.survey.types.SuiteId\nimport retrofit2.Retrofit\nimport retrofit2.converter.moshi.MoshiConverterFactory\n\nclass SslLabsClient(\n  callFactory: Call.Factory,\n) {\n  private val moshi = Moshi.Builder().build()\n\n  private val moshiConverterFactory = MoshiConverterFactory.create(moshi)\n\n  private val retrofit =\n    Retrofit\n      .Builder()\n      .baseUrl(SslLabsApi.BASE_URL)\n      .addConverterFactory(moshiConverterFactory)\n      .callFactory(callFactory)\n      .build()\n\n  private val sslLabsApi = retrofit.create(SslLabsApi::class.java)\n\n  suspend fun clients(): List<Client> =\n    sslLabsApi.clients().map { userAgent ->\n      Client(\n        userAgent = userAgent.name,\n        version = userAgent.version,\n        platform = userAgent.platform,\n        enabled = userAgent.suiteNames.map { SuiteId(null, it) },\n      )\n    }\n}\n\nsuspend fun main() {\n  val sslLabsClient =\n    SslLabsClient(\n      callFactory = OkHttpClient(),\n    )\n\n  for (client in sslLabsClient.clients()) {\n    println(client)\n  }\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/ssllabs/UserAgentCapabilities.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey.ssllabs\n\nimport com.squareup.moshi.JsonClass\n\n@JsonClass(generateAdapter = true)\nclass UserAgentCapabilities(\n  val abortsOnUnrecognizedName: Boolean,\n  val alpnProtocols: List<String>,\n  val ellipticCurves: List<Int>,\n  val handshakeFormat: String,\n  val hexHandshakeBytes: String,\n  val highestProtocol: Int,\n  val id: Int,\n  val isGrade0: Boolean,\n  val lowestProtocol: Int,\n  val maxDhBits: Int,\n  val maxRsaBits: Int,\n  val minDhBits: Int,\n  val minEcdsaBits: Int,\n  val minRsaBits: Int,\n  val name: String,\n  val npnProtocols: List<String>,\n  val platform: String?,\n  val requiresSha2: Boolean,\n  val signatureAlgorithms: List<Int>,\n  val suiteIds: List<Int>,\n  val suiteNames: List<String>,\n  val supportsCompression: Boolean,\n  val supportsNpn: Boolean,\n  val supportsRi: Boolean,\n  val supportsSni: Boolean,\n  val supportsStapling: Boolean,\n  val supportsTickets: Boolean,\n  val userAgent: String?,\n  val version: String,\n)\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/types/Client.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey.types\n\ndata class Client(\n  val userAgent: String,\n  val version: String,\n  val platform: String? = null,\n  val enabled: List<SuiteId> = listOf(),\n  val supported: List<SuiteId> = listOf(),\n) {\n  val nameAndVersion: String\n    get() = \"$userAgent/$version\"\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/kotlin/okhttp3/survey/types/SuiteId.kt",
    "content": "/*\n * Copyright (C) 2022 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.survey.types\n\nimport okio.ByteString\n\ndata class SuiteId(\n  val id: ByteString?,\n  val name: String,\n) {\n  fun matches(suiteId: SuiteId): Boolean = id == suiteId.id || name.substring(4) == suiteId.name.substring(4)\n}\n"
  },
  {
    "path": "samples/tlssurvey/src/main/resources/okhttp_3.11.txt",
    "content": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\nTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_AES_128_GCM_SHA256\nTLS_RSA_WITH_AES_256_GCM_SHA384\nTLS_RSA_WITH_AES_128_CBC_SHA\nTLS_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_3DES_EDE_CBC_SHA\n"
  },
  {
    "path": "samples/tlssurvey/src/main/resources/okhttp_3.13.txt",
    "content": "TLS_AES_128_GCM_SHA256\nTLS_AES_256_GCM_SHA384\nTLS_CHACHA20_POLY1305_SHA256\nTLS_AES_128_CCM_SHA256\nTLS_AES_256_CCM_8_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\nTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_AES_128_GCM_SHA256\nTLS_RSA_WITH_AES_256_GCM_SHA384\nTLS_RSA_WITH_AES_128_CBC_SHA\nTLS_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_3DES_EDE_CBC_SHA\n"
  },
  {
    "path": "samples/tlssurvey/src/main/resources/okhttp_3.14.txt",
    "content": "TLS_AES_128_GCM_SHA256\nTLS_AES_256_GCM_SHA384\nTLS_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\nTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_AES_128_GCM_SHA256\nTLS_RSA_WITH_AES_256_GCM_SHA384\nTLS_RSA_WITH_AES_128_CBC_SHA\nTLS_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_3DES_EDE_CBC_SHA\n"
  },
  {
    "path": "samples/tlssurvey/src/main/resources/okhttp_3.9.txt",
    "content": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA\nTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\nTLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA\nTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_AES_128_GCM_SHA256\nTLS_RSA_WITH_AES_256_GCM_SHA384\nTLS_RSA_WITH_AES_128_CBC_SHA\nTLS_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_3DES_EDE_CBC_SHA\n"
  },
  {
    "path": "samples/tlssurvey/src/main/resources/okhttp_4.10.txt",
    "content": "TLS_AES_128_GCM_SHA256\nTLS_AES_256_GCM_SHA384\nTLS_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\nTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_AES_128_GCM_SHA256\nTLS_RSA_WITH_AES_256_GCM_SHA384\nTLS_RSA_WITH_AES_128_CBC_SHA\nTLS_RSA_WITH_AES_256_CBC_SHA\nTLS_RSA_WITH_3DES_EDE_CBC_SHA\n"
  },
  {
    "path": "samples/unixdomainsockets/build.gradle.kts",
    "content": "plugins {\n  kotlin(\"jvm\")\n  id(\"okhttp.jvm-conventions\")\n  id(\"okhttp.quality-conventions\")\n  id(\"okhttp.testing-conventions\")\n}\n\ndependencies {\n  implementation(projects.okhttp)\n  implementation(projects.mockwebserver)\n  implementation(libs.jnr.unixsocket)\n}\n"
  },
  {
    "path": "samples/unixdomainsockets/src/main/java/okhttp3/unixdomainsockets/ClientAndServer.java",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.unixdomainsockets;\n\nimport java.io.File;\nimport java.util.Collections;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Protocol;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\n\n/**\n * Create UNIX domain sockets for MockWebServer and OkHttp and connect 'em together. Note that we\n * cannot do TLS over domain sockets.\n */\npublic class ClientAndServer {\n  public void run() throws Exception {\n    File socketFile = new File(\"/tmp/ClientAndServer.sock\");\n    socketFile.delete(); // Clean up from previous runs.\n\n    MockWebServer server = new MockWebServer();\n    server.setServerSocketFactory(new UnixDomainServerSocketFactory(socketFile));\n    server.setProtocols(Collections.singletonList(Protocol.H2_PRIOR_KNOWLEDGE));\n    server.enqueue(new MockResponse().setBody(\"hello\"));\n    server.start();\n\n    OkHttpClient client = new OkHttpClient.Builder()\n        .socketFactory(new UnixDomainSocketFactory(socketFile))\n        .protocols(Collections.singletonList(Protocol.H2_PRIOR_KNOWLEDGE))\n        .build();\n\n    Request request = new Request.Builder()\n        .url(\"http://publicobject.com/helloworld.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      System.out.println(response.body().string());\n    }\n\n    server.shutdown();\n    socketFile.delete();\n  }\n\n  public static void main(String... args) throws Exception {\n    new ClientAndServer().run();\n  }\n}\n"
  },
  {
    "path": "samples/unixdomainsockets/src/main/java/okhttp3/unixdomainsockets/TunnelingUnixSocket.java",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.unixdomainsockets;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.SocketAddress;\nimport jnr.unixsocket.UnixSocket;\nimport jnr.unixsocket.UnixSocketAddress;\nimport jnr.unixsocket.UnixSocketChannel;\n\n/**\n * Subtype UNIX socket for a higher-fidelity impersonation of TCP sockets. This is named \"tunneling\"\n * because it assumes the ultimate destination has a hostname and port.\n */\nfinal class TunnelingUnixSocket extends UnixSocket {\n  private final File path;\n  private InetSocketAddress inetSocketAddress;\n\n  TunnelingUnixSocket(File path, UnixSocketChannel channel) {\n    super(channel);\n    this.path = path;\n  }\n\n  TunnelingUnixSocket(File path, UnixSocketChannel channel, InetSocketAddress address) {\n    this(path, channel);\n    this.inetSocketAddress = address;\n  }\n\n  @Override public void connect(SocketAddress endpoint) throws IOException {\n    this.inetSocketAddress = (InetSocketAddress) endpoint;\n    super.connect(new UnixSocketAddress(path), 0);\n  }\n\n  @Override public void connect(SocketAddress endpoint, int timeout) throws IOException {\n    this.inetSocketAddress = (InetSocketAddress) endpoint;\n    super.connect(new UnixSocketAddress(path), timeout);\n  }\n\n  @Override public InetAddress getInetAddress() {\n    return inetSocketAddress.getAddress();\n  }\n}\n"
  },
  {
    "path": "samples/unixdomainsockets/src/main/java/okhttp3/unixdomainsockets/UnixDomainServerSocketFactory.java",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.unixdomainsockets;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.ServerSocket;\nimport java.net.Socket;\nimport java.net.SocketAddress;\nimport java.net.SocketException;\nimport java.nio.channels.ClosedChannelException;\nimport javax.net.ServerSocketFactory;\nimport jnr.unixsocket.UnixServerSocketChannel;\nimport jnr.unixsocket.UnixSocketAddress;\nimport jnr.unixsocket.UnixSocketChannel;\n\n/** Impersonate TCP-style ServerSocketFactory over UNIX domain sockets. */\npublic final class UnixDomainServerSocketFactory extends ServerSocketFactory {\n  private final File path;\n\n  public UnixDomainServerSocketFactory(File path) {\n    this.path = path;\n  }\n\n  @Override public ServerSocket createServerSocket() throws IOException {\n    return new UnixDomainServerSocket();\n  }\n\n  @Override public ServerSocket createServerSocket(int port) throws IOException {\n    return createServerSocket();\n  }\n\n  @Override public ServerSocket createServerSocket(int port, int backlog) throws IOException {\n    return createServerSocket();\n  }\n\n  @Override public ServerSocket createServerSocket(\n      int port, int backlog, InetAddress inetAddress) throws IOException {\n    return createServerSocket();\n  }\n\n  final class UnixDomainServerSocket extends ServerSocket {\n    private UnixServerSocketChannel serverSocketChannel;\n    private InetSocketAddress endpoint;\n\n    UnixDomainServerSocket() throws IOException {\n    }\n\n    @Override public void bind(SocketAddress endpoint, int backlog) throws IOException {\n      this.endpoint = (InetSocketAddress) endpoint;\n\n      UnixSocketAddress address = new UnixSocketAddress(path);\n      serverSocketChannel = UnixServerSocketChannel.open();\n      serverSocketChannel.configureBlocking(true);\n      serverSocketChannel.socket().bind(address);\n    }\n\n    @Override public int getLocalPort() {\n      return 1; // A white lie. There is no local port.\n    }\n\n    @Override public SocketAddress getLocalSocketAddress() {\n      return endpoint;\n    }\n\n    @Override public Socket accept() throws IOException {\n      try {\n        UnixSocketChannel channel = serverSocketChannel.accept();\n        return new TunnelingUnixSocket(path, channel, endpoint);\n      } catch (ClosedChannelException e) {\n        SocketException exception = new SocketException();\n        exception.initCause(e);\n        throw exception;\n      }\n    }\n\n    @Override public void close() throws IOException {\n      serverSocketChannel.close();\n    }\n  }\n}\n"
  },
  {
    "path": "samples/unixdomainsockets/src/main/java/okhttp3/unixdomainsockets/UnixDomainSocketFactory.java",
    "content": "/*\n * Copyright (C) 2018 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage okhttp3.unixdomainsockets;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport javax.net.SocketFactory;\nimport jnr.unixsocket.UnixSocketChannel;\n\n/** Impersonate TCP-style SocketFactory over UNIX domain sockets. */\npublic final class UnixDomainSocketFactory extends SocketFactory {\n  private final File path;\n\n  public UnixDomainSocketFactory(File path) {\n    this.path = path;\n  }\n\n  @Override public Socket createSocket() throws IOException {\n    UnixSocketChannel channel = UnixSocketChannel.open();\n    return new TunnelingUnixSocket(path, channel);\n  }\n\n  @Override public Socket createSocket(String host, int port) throws IOException {\n    Socket result = createSocket();\n\n    try {\n      result.connect(new InetSocketAddress(host, port));\n    } catch (IOException e) {\n      result.close();\n      throw e;\n    }\n    return result;\n  }\n\n  @Override public Socket createSocket(\n      String host, int port, InetAddress localHost, int localPort) throws IOException {\n    return createSocket(host, port);\n  }\n\n  @Override public Socket createSocket(InetAddress host, int port) throws IOException {\n    Socket result = createSocket();\n\n    try {\n      result.connect(new InetSocketAddress(host, port));\n    } catch (IOException e) {\n      result.close();\n      throw e;\n    }\n    return result;\n  }\n\n  @Override public Socket createSocket(\n      InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException {\n    return createSocket(host, port);\n  }\n}\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npluginManagement {\n  includeBuild(\"build-logic\")\n  repositories {\n    mavenCentral()\n    gradlePluginPortal()\n    google()\n  }\n}\n\nrootProject.name = \"okhttp-parent\"\n\ndependencyResolutionManagement {\n  repositories {\n    mavenCentral()\n    google()\n  }\n}\n\nplugins {\n  id(\"org.gradle.toolchains.foojay-resolver-convention\") version(\"1.0.0\")\n}\n\ninclude(\":mockwebserver\")\nproject(\":mockwebserver\").name = \"mockwebserver3\"\ninclude(\":mockwebserver-deprecated\")\nproject(\":mockwebserver-deprecated\").name = \"mockwebserver\"\ninclude(\":mockwebserver-junit4\")\nproject(\":mockwebserver-junit4\").name = \"mockwebserver3-junit4\"\ninclude(\":mockwebserver-junit5\")\nproject(\":mockwebserver-junit5\").name = \"mockwebserver3-junit5\"\n\nval androidBuild: String by settings\nval graalBuild: String by settings\nval loomBuild: String by settings\n\nif (androidBuild.toBoolean()) {\n  include(\":regression-test\")\n}\n\nif (graalBuild.toBoolean()) {\n  include(\":native-image-tests\")\n}\n\ninclude(\":okcurl\")\ninclude(\":okhttp\")\ninclude(\":okhttp-bom\")\ninclude(\":okhttp-brotli\")\ninclude(\":okhttp-coroutines\")\ninclude(\":okhttp-dnsoverhttps\")\ninclude(\":okhttp-hpacktests\")\ninclude(\":okhttp-idna-mapping-table\")\ninclude(\":okhttp-java-net-cookiejar\")\ninclude(\":okhttp-logging-interceptor\")\ninclude(\":okhttp-osgi-tests\")\ninclude(\":okhttp-sse\")\ninclude(\":okhttp-testing-support\")\ninclude(\":okhttp-tls\")\ninclude(\":okhttp-urlconnection\")\ninclude(\":okhttp-zstd\")\ninclude(\":samples:compare\")\ninclude(\":samples:crawler\")\ninclude(\":samples:guide\")\ninclude(\":samples:simple-client\")\ninclude(\":samples:slack\")\ninclude(\":samples:static-server\")\ninclude(\":samples:tlssurvey\")\ninclude(\":samples:unixdomainsockets\")\ninclude(\":container-tests\")\nval okhttpModuleTests: String by settings\nif (okhttpModuleTests.toBoolean()) {\n  include(\":module-tests\")\n}\n\nproject(\":okhttp-logging-interceptor\").name = \"logging-interceptor\"\n\nval androidHome = System.getenv(\"ANDROID_HOME\")\nval localProperties = java.util.Properties().apply {\n  val file = rootProject.projectDir.resolve(\"local.properties\")\n  if (file.exists()) {\n    load(file.inputStream())\n  }\n}\nval sdkDir = localProperties.getProperty(\"sdk.dir\")\nif (androidHome != null || sdkDir != null) {\n  include(\":android-test\")\n  include(\":android-test-app\")\n}\n\nenableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\nenableFeaturePreview(\"STABLE_CONFIGURATION_CACHE\")\n"
  },
  {
    "path": "test_docs.sh",
    "content": "#!/bin/bash\n\n# The website is built using MkDocs with the Material theme.\n# https://squidfunk.github.io/mkdocs-material/\n# It requires Python to run.\n# Install the packages with the following command:\n# pip install mkdocs mkdocs-material mkdocs-redirects\n\nset -ex\n\n# Test generating the javadoc jars\n./gradlew publishToMavenLocal -DRELEASE_SIGNING_ENABLED=false -PokhttpDokka=true\n\n# Generate the API docs\n./gradlew dokkaGeneratePublicationHtml -PokhttpDokka=true\n\nmv ./build/dokka/html docs/5.x\n\n# Copy in special files that GitHub wants in the project root.\ncat README.md | grep -v 'project website' > docs/index.md\ncp CHANGELOG.md docs/changelogs/changelog.md\ncp CONTRIBUTING.md docs/contribute/contributing.md\n\n# Build the site locally\nmkdocs build\n"
  }
]